LCOV - code coverage report
Current view: top level - frmts/vrt - vrtsources.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1690 2010 84.1 %
Date: 2026-03-29 15:03:50 Functions: 94 117 80.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTSimpleSource, VRTFuncSource and
       5             :  *           VRTAveragedSource.
       6             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "gdal_vrt.h"
      16             : #include "vrtdataset.h"
      17             : 
      18             : #include <cassert>
      19             : #include <climits>
      20             : #include <cmath>
      21             : #include <cstddef>
      22             : #include <cstdio>
      23             : #include <cstdlib>
      24             : #include <cstring>
      25             : #include <algorithm>
      26             : #include <limits>
      27             : #include <string>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_hash_set.h"
      32             : #include "cpl_minixml.h"
      33             : #include "cpl_progress.h"
      34             : #include "cpl_string.h"
      35             : #include "cpl_vsi.h"
      36             : #include "gdal.h"
      37             : #include "gdal_priv.h"
      38             : #include "gdal_proxy.h"
      39             : #include "gdal_priv_templates.hpp"
      40             : #include "gdalsubdatasetinfo.h"
      41             : 
      42             : /*! @cond Doxygen_Suppress */
      43             : 
      44             : // #define DEBUG_VERBOSE 1
      45             : 
      46             : /************************************************************************/
      47             : /* ==================================================================== */
      48             : /*                             VRTSource                                */
      49             : /* ==================================================================== */
      50             : /************************************************************************/
      51             : 
      52      248017 : VRTSource::~VRTSource()
      53             : {
      54      248017 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                            GetFileList()                             */
      58             : /************************************************************************/
      59             : 
      60           0 : void VRTSource::GetFileList(char *** /* ppapszFileList */, int * /* pnSize */,
      61             :                             int * /* pnMaxSize */, CPLHashSet * /* hSetFiles */)
      62             : {
      63           0 : }
      64             : 
      65             : /************************************************************************/
      66             : /* ==================================================================== */
      67             : /*                          VRTSimpleSource                             */
      68             : /* ==================================================================== */
      69             : /************************************************************************/
      70             : 
      71             : /************************************************************************/
      72             : /*                          VRTSimpleSource()                           */
      73             : /************************************************************************/
      74             : 
      75             : VRTSimpleSource::VRTSimpleSource() = default;
      76             : 
      77             : /************************************************************************/
      78             : /*                          VRTSimpleSource()                           */
      79             : /************************************************************************/
      80             : 
      81         106 : VRTSimpleSource::VRTSimpleSource(const VRTSimpleSource *poSrcSource,
      82         106 :                                  double dfXDstRatio, double dfYDstRatio)
      83             :     : VRTSource(*poSrcSource),
      84         106 :       m_poMapSharedSources(poSrcSource->m_poMapSharedSources),
      85         106 :       m_poRasterBand(poSrcSource->m_poRasterBand),
      86         106 :       m_poMaskBandMainBand(poSrcSource->m_poMaskBandMainBand),
      87         106 :       m_aosOpenOptionsOri(poSrcSource->m_aosOpenOptionsOri),
      88         106 :       m_aosOpenOptions(poSrcSource->m_aosOpenOptions),
      89         106 :       m_bSrcDSNameFromVRT(poSrcSource->m_bSrcDSNameFromVRT),
      90         106 :       m_nBand(poSrcSource->m_nBand),
      91         106 :       m_bGetMaskBand(poSrcSource->m_bGetMaskBand),
      92         106 :       m_dfSrcXOff(poSrcSource->m_dfSrcXOff),
      93         106 :       m_dfSrcYOff(poSrcSource->m_dfSrcYOff),
      94         106 :       m_dfSrcXSize(poSrcSource->m_dfSrcXSize),
      95         106 :       m_dfSrcYSize(poSrcSource->m_dfSrcYSize),
      96         106 :       m_nMaxValue(poSrcSource->m_nMaxValue), m_bRelativeToVRTOri(-1),
      97         106 :       m_nExplicitSharedStatus(poSrcSource->m_nExplicitSharedStatus),
      98         106 :       m_osSrcDSName(poSrcSource->m_osSrcDSName),
      99         106 :       m_bDropRefOnSrcBand(poSrcSource->m_bDropRefOnSrcBand)
     100             : {
     101         106 :     if (!poSrcSource->IsSrcWinSet() && !poSrcSource->IsDstWinSet() &&
     102           0 :         (dfXDstRatio != 1.0 || dfYDstRatio != 1.0))
     103             :     {
     104           2 :         auto l_band = GetRasterBand();
     105           2 :         if (l_band)
     106             :         {
     107           2 :             m_dfSrcXOff = 0;
     108           2 :             m_dfSrcYOff = 0;
     109           2 :             m_dfSrcXSize = l_band->GetXSize();
     110           2 :             m_dfSrcYSize = l_band->GetYSize();
     111           2 :             m_dfDstXOff = 0;
     112           2 :             m_dfDstYOff = 0;
     113           2 :             m_dfDstXSize = l_band->GetXSize() * dfXDstRatio;
     114           2 :             m_dfDstYSize = l_band->GetYSize() * dfYDstRatio;
     115             :         }
     116             :     }
     117         104 :     else if (poSrcSource->IsDstWinSet())
     118             :     {
     119         104 :         m_dfDstXOff = poSrcSource->m_dfDstXOff * dfXDstRatio;
     120         104 :         m_dfDstYOff = poSrcSource->m_dfDstYOff * dfYDstRatio;
     121         104 :         m_dfDstXSize = poSrcSource->m_dfDstXSize * dfXDstRatio;
     122         104 :         m_dfDstYSize = poSrcSource->m_dfDstYSize * dfYDstRatio;
     123             :     }
     124             : 
     125         106 :     if (m_bDropRefOnSrcBand)
     126             :     {
     127         106 :         GDALDataset *poDS = GetSourceDataset();
     128         106 :         if (poDS)
     129         102 :             poDS->Reference();
     130             :     }
     131         106 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                          ~VRTSimpleSource()                          */
     135             : /************************************************************************/
     136             : 
     137      495064 : VRTSimpleSource::~VRTSimpleSource()
     138             : 
     139             : {
     140      247963 :     if (m_bDropRefOnSrcBand)
     141             :     {
     142      247538 :         GDALDataset *poDS = GetSourceDataset();
     143      247538 :         if (poDS)
     144      244943 :             poDS->ReleaseRef();
     145             :     }
     146      495064 : }
     147             : 
     148             : /************************************************************************/
     149             : /*                          GetSourceDataset()                          */
     150             : /************************************************************************/
     151             : 
     152      247644 : GDALDataset *VRTSimpleSource::GetSourceDataset() const
     153             : {
     154      247644 :     GDALDataset *poDS = nullptr;
     155      247644 :     if (m_poMaskBandMainBand)
     156          72 :         poDS = m_poMaskBandMainBand->GetDataset();
     157      247572 :     else if (m_poRasterBand)
     158      244973 :         poDS = m_poRasterBand->GetDataset();
     159      247644 :     return poDS;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                           GetTypeStatic()                            */
     164             : /************************************************************************/
     165             : 
     166      262922 : const char *VRTSimpleSource::GetTypeStatic()
     167             : {
     168             :     static const char *TYPE = "SimpleSource";
     169      262922 :     return TYPE;
     170             : }
     171             : 
     172             : /************************************************************************/
     173             : /*                              GetType()                               */
     174             : /************************************************************************/
     175             : 
     176       28587 : const char *VRTSimpleSource::GetType() const
     177             : {
     178       28587 :     return GetTypeStatic();
     179             : }
     180             : 
     181             : /************************************************************************/
     182             : /*                             FlushCache()                             */
     183             : /************************************************************************/
     184             : 
     185      313750 : CPLErr VRTSimpleSource::FlushCache(bool bAtClosing)
     186             : 
     187             : {
     188      313750 :     if (m_poMaskBandMainBand != nullptr)
     189             :     {
     190          18 :         return m_poMaskBandMainBand->FlushCache(bAtClosing);
     191             :     }
     192      313732 :     else if (m_poRasterBand != nullptr)
     193             :     {
     194      311097 :         return m_poRasterBand->FlushCache(bAtClosing);
     195             :     }
     196        2635 :     return CE_None;
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                  UnsetPreservedRelativeFilenames()                   */
     201             : /************************************************************************/
     202             : 
     203          96 : void VRTSimpleSource::UnsetPreservedRelativeFilenames()
     204             : {
     205         282 :     if (m_bRelativeToVRTOri &&
     206         186 :         !STARTS_WITH(m_osSourceFileNameOri.c_str(), "http://") &&
     207          90 :         !STARTS_WITH(m_osSourceFileNameOri.c_str(), "https://"))
     208             :     {
     209          90 :         m_bRelativeToVRTOri = -1;
     210          90 :         m_osSourceFileNameOri = "";
     211             :     }
     212          96 : }
     213             : 
     214             : /************************************************************************/
     215             : /*                             SetSrcBand()                             */
     216             : /************************************************************************/
     217             : 
     218         463 : void VRTSimpleSource::SetSrcBand(const char *pszFilename, int nBand)
     219             : 
     220             : {
     221         463 :     m_nBand = nBand;
     222         463 :     m_osSrcDSName = pszFilename;
     223         463 : }
     224             : 
     225             : /************************************************************************/
     226             : /*                             SetSrcBand()                             */
     227             : /************************************************************************/
     228             : 
     229      142834 : void VRTSimpleSource::SetSrcBand(GDALRasterBand *poNewSrcBand)
     230             : 
     231             : {
     232      142834 :     m_poRasterBand = poNewSrcBand;
     233      142834 :     m_nBand = m_poRasterBand->GetBand();
     234      142834 :     auto poDS = poNewSrcBand->GetDataset();
     235      142834 :     if (poDS != nullptr)
     236             :     {
     237      142834 :         m_osSrcDSName = poDS->GetDescription();
     238      142834 :         m_aosOpenOptions = CSLDuplicate(poDS->GetOpenOptions());
     239      142834 :         m_aosOpenOptionsOri = m_aosOpenOptions;
     240             :     }
     241      142834 : }
     242             : 
     243             : /************************************************************************/
     244             : /*                        SetSourceDatasetName()                        */
     245             : /************************************************************************/
     246             : 
     247           7 : void VRTSimpleSource::SetSourceDatasetName(const char *pszFilename,
     248             :                                            bool bRelativeToVRT)
     249             : {
     250           7 :     CPLAssert(m_nBand >= 0);
     251           7 :     m_osSrcDSName = pszFilename;
     252           7 :     m_osSourceFileNameOri = pszFilename;
     253           7 :     m_bRelativeToVRTOri = bRelativeToVRT;
     254           7 : }
     255             : 
     256             : /************************************************************************/
     257             : /*                           SetSrcMaskBand()                           */
     258             : /************************************************************************/
     259             : 
     260             : // poSrcBand is not the mask band, but the band from which the mask band is
     261             : // taken.
     262          48 : void VRTSimpleSource::SetSrcMaskBand(GDALRasterBand *poNewSrcBand)
     263             : 
     264             : {
     265          48 :     m_poRasterBand = poNewSrcBand->GetMaskBand();
     266          48 :     m_poMaskBandMainBand = poNewSrcBand;
     267          48 :     m_nBand = poNewSrcBand->GetBand();
     268          48 :     auto poDS = poNewSrcBand->GetDataset();
     269          48 :     if (poDS != nullptr)
     270             :     {
     271          48 :         m_osSrcDSName = poDS->GetDescription();
     272          48 :         m_aosOpenOptions = CSLDuplicate(poDS->GetOpenOptions());
     273          48 :         m_aosOpenOptionsOri = m_aosOpenOptions;
     274             :     }
     275          48 :     m_bGetMaskBand = true;
     276          48 : }
     277             : 
     278             : /************************************************************************/
     279             : /*                         RoundIfCloseToInt()                          */
     280             : /************************************************************************/
     281             : 
     282     2655950 : static double RoundIfCloseToInt(double dfValue)
     283             : {
     284     2655950 :     double dfClosestInt = floor(dfValue + 0.5);
     285     2655950 :     return (fabs(dfValue - dfClosestInt) < 1e-3) ? dfClosestInt : dfValue;
     286             : }
     287             : 
     288             : /************************************************************************/
     289             : /*                            SetSrcWindow()                            */
     290             : /************************************************************************/
     291             : 
     292      146424 : void VRTSimpleSource::SetSrcWindow(double dfNewXOff, double dfNewYOff,
     293             :                                    double dfNewXSize, double dfNewYSize)
     294             : 
     295             : {
     296      146424 :     m_dfSrcXOff = RoundIfCloseToInt(dfNewXOff);
     297      146424 :     m_dfSrcYOff = RoundIfCloseToInt(dfNewYOff);
     298      146424 :     m_dfSrcXSize = RoundIfCloseToInt(dfNewXSize);
     299      146424 :     m_dfSrcYSize = RoundIfCloseToInt(dfNewYSize);
     300      146424 : }
     301             : 
     302             : /************************************************************************/
     303             : /*                            SetDstWindow()                            */
     304             : /************************************************************************/
     305             : 
     306      146423 : void VRTSimpleSource::SetDstWindow(double dfNewXOff, double dfNewYOff,
     307             :                                    double dfNewXSize, double dfNewYSize)
     308             : 
     309             : {
     310      146423 :     m_dfDstXOff = RoundIfCloseToInt(dfNewXOff);
     311      146423 :     m_dfDstYOff = RoundIfCloseToInt(dfNewYOff);
     312      146423 :     m_dfDstXSize = RoundIfCloseToInt(dfNewXSize);
     313      146423 :     m_dfDstYSize = RoundIfCloseToInt(dfNewYSize);
     314      146423 : }
     315             : 
     316             : /************************************************************************/
     317             : /*                            GetDstWindow()                            */
     318             : /************************************************************************/
     319             : 
     320         549 : void VRTSimpleSource::GetDstWindow(double &dfDstXOff, double &dfDstYOff,
     321             :                                    double &dfDstXSize, double &dfDstYSize) const
     322             : {
     323         549 :     dfDstXOff = m_dfDstXOff;
     324         549 :     dfDstYOff = m_dfDstYOff;
     325         549 :     dfDstXSize = m_dfDstXSize;
     326         549 :     dfDstYSize = m_dfDstYSize;
     327         549 : }
     328             : 
     329             : /************************************************************************/
     330             : /*                        DstWindowIntersects()                         */
     331             : /************************************************************************/
     332             : 
     333        1104 : bool VRTSimpleSource::DstWindowIntersects(double dfXOff, double dfYOff,
     334             :                                           double dfXSize, double dfYSize) const
     335             : {
     336        2199 :     return IsDstWinSet() && m_dfDstXOff + m_dfDstXSize > dfXOff &&
     337        1095 :            m_dfDstYOff + m_dfDstYSize > dfYOff &&
     338        2199 :            m_dfDstXOff < dfXOff + dfXSize && m_dfDstYOff < dfYOff + dfYSize;
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                            IsSlowSource()                            */
     343             : /************************************************************************/
     344             : 
     345        2581 : static bool IsSlowSource(const char *pszSrcName)
     346             : {
     347        5162 :     return strstr(pszSrcName, "/vsicurl/http") != nullptr ||
     348        5162 :            strstr(pszSrcName, "/vsicurl/ftp") != nullptr ||
     349        2581 :            (strstr(pszSrcName, "/vsicurl?") != nullptr &&
     350        2581 :             strstr(pszSrcName, "&url=http") != nullptr);
     351             : }
     352             : 
     353             : /************************************************************************/
     354             : /*                       AddSourceFilenameNode()                        */
     355             : /************************************************************************/
     356             : 
     357             : /* static */
     358        2597 : std::pair<std::string, bool> VRTSimpleSource::ComputeSourceNameAndRelativeFlag(
     359             :     const char *pszVRTPath, const std::string &osSourceNameIn)
     360             : {
     361        2597 :     std::string osSourceFilename = osSourceNameIn;
     362        2597 :     int bRelativeToVRT = false;
     363             : 
     364             :     // If this isn't actually a file, don't even try to know if it is a
     365             :     // relative path. It can't be !, and unfortunately CPLIsFilenameRelative()
     366             :     // can only work with strings that are filenames To be clear
     367             :     // NITF_TOC_ENTRY:CADRG_JOG-A_250K_1_0:some_path isn't a relative file
     368             :     // path.
     369             :     VSIStatBufL sStat;
     370        2597 :     if (VSIStatExL(osSourceFilename.c_str(), &sStat, VSI_STAT_EXISTS_FLAG) != 0)
     371             :     {
     372             :         // Try subdatasetinfo API first
     373             :         // Note: this will become the only branch when subdatasetinfo will become
     374             :         //       available for NITF_IM, RASTERLITE and TILEDB
     375          73 :         const auto oSubDSInfo{GDALGetSubdatasetInfo(osSourceFilename.c_str())};
     376          73 :         if (oSubDSInfo && !oSubDSInfo->GetPathComponent().empty())
     377             :         {
     378           8 :             auto path{oSubDSInfo->GetPathComponent()};
     379             :             std::string relPath{CPLExtractRelativePath(pszVRTPath, path.c_str(),
     380           8 :                                                        &bRelativeToVRT)};
     381           4 :             osSourceFilename = oSubDSInfo->ModifyPathComponent(relPath);
     382           4 :             GDALDestroySubdatasetInfo(oSubDSInfo);
     383             :         }
     384             :         else
     385             :         {
     386         330 :             for (const char *pszSyntax :
     387         399 :                  GDALDataset::apszSpecialSubDatasetSyntax)
     388             :             {
     389         333 :                 CPLString osPrefix(pszSyntax);
     390         333 :                 osPrefix.resize(strchr(pszSyntax, ':') - pszSyntax + 1);
     391         333 :                 if (pszSyntax[osPrefix.size()] == '"')
     392          66 :                     osPrefix += '"';
     393         333 :                 if (EQUALN(osSourceFilename.c_str(), osPrefix, osPrefix.size()))
     394             :                 {
     395           3 :                     if (STARTS_WITH_CI(pszSyntax + osPrefix.size(), "{ANY}"))
     396             :                     {
     397             :                         const char *pszLastPart =
     398           3 :                             strrchr(osSourceFilename.c_str(), ':') + 1;
     399             :                         // CSV:z:/foo.xyz
     400           0 :                         if ((pszLastPart[0] == '/' || pszLastPart[0] == '\\') &&
     401           6 :                             pszLastPart - osSourceFilename.c_str() >= 3 &&
     402           3 :                             pszLastPart[-3] == ':')
     403           0 :                             pszLastPart -= 2;
     404           3 :                         CPLString osPrefixFilename(osSourceFilename);
     405           3 :                         osPrefixFilename.resize(pszLastPart -
     406           3 :                                                 osSourceFilename.c_str());
     407             :                         osSourceFilename = CPLExtractRelativePath(
     408           3 :                             pszVRTPath, pszLastPart, &bRelativeToVRT);
     409           3 :                         osSourceFilename = osPrefixFilename + osSourceFilename;
     410             :                     }
     411           0 :                     else if (STARTS_WITH_CI(pszSyntax + osPrefix.size(),
     412             :                                             "{FILENAME}"))
     413             :                     {
     414           0 :                         CPLString osFilename(osSourceFilename.c_str() +
     415           0 :                                              osPrefix.size());
     416           0 :                         size_t nPos = 0;
     417           0 :                         if (osFilename.size() >= 3 && osFilename[1] == ':' &&
     418           0 :                             (osFilename[2] == '\\' || osFilename[2] == '/'))
     419           0 :                             nPos = 2;
     420           0 :                         nPos = osFilename.find(
     421           0 :                             pszSyntax[osPrefix.size() + strlen("{FILENAME}")],
     422             :                             nPos);
     423           0 :                         if (nPos != std::string::npos)
     424             :                         {
     425           0 :                             const CPLString osSuffix = osFilename.substr(nPos);
     426           0 :                             osFilename.resize(nPos);
     427             :                             osSourceFilename = CPLExtractRelativePath(
     428           0 :                                 pszVRTPath, osFilename, &bRelativeToVRT);
     429             :                             osSourceFilename =
     430           0 :                                 osPrefix + osSourceFilename + osSuffix;
     431             :                         }
     432             :                     }
     433           3 :                     break;
     434             :                 }
     435             :             }
     436             :         }
     437             :     }
     438             :     else
     439             :     {
     440        5048 :         std::string osVRTFilename = pszVRTPath;
     441        5048 :         std::string osSourceDataset = osSourceNameIn;
     442             :         ;
     443        2524 :         char *pszCurDir = CPLGetCurrentDir();
     444        2524 :         if (CPLIsFilenameRelative(osSourceDataset.c_str()) &&
     445        2524 :             !CPLIsFilenameRelative(osVRTFilename.c_str()) &&
     446             :             pszCurDir != nullptr)
     447             :         {
     448         194 :             osSourceDataset = CPLFormFilenameSafe(
     449          97 :                 pszCurDir, osSourceDataset.c_str(), nullptr);
     450             :         }
     451        2427 :         else if (!CPLIsFilenameRelative(osSourceDataset.c_str()) &&
     452        2427 :                  CPLIsFilenameRelative(osVRTFilename.c_str()) &&
     453             :                  pszCurDir != nullptr)
     454             :         {
     455             :             osVRTFilename =
     456          79 :                 CPLFormFilenameSafe(pszCurDir, osVRTFilename.c_str(), nullptr);
     457             :         }
     458        2524 :         CPLFree(pszCurDir);
     459             :         osSourceFilename = CPLExtractRelativePath(
     460        2524 :             osVRTFilename.c_str(), osSourceDataset.c_str(), &bRelativeToVRT);
     461             :     }
     462             : 
     463        7791 :     return {osSourceFilename, static_cast<bool>(bRelativeToVRT)};
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                       AddSourceFilenameNode()                        */
     468             : /************************************************************************/
     469             : 
     470        2625 : void VRTSimpleSource::AddSourceFilenameNode(const char *pszVRTPath,
     471             :                                             CPLXMLNode *psSrc)
     472             : {
     473             : 
     474        2625 :     bool bRelativeToVRT = false;
     475        5250 :     std::string osSourceFilename;
     476             : 
     477        2625 :     if (m_bRelativeToVRTOri >= 0)
     478             :     {
     479          44 :         osSourceFilename = m_osSourceFileNameOri;
     480          44 :         bRelativeToVRT = m_bRelativeToVRTOri;
     481             :     }
     482        2581 :     else if (IsSlowSource(m_osSrcDSName))
     483             :     {
     484             :         // Testing the existence of remote resources can be excruciating
     485             :         // slow, so let's just suppose they exist.
     486           0 :         osSourceFilename = m_osSrcDSName;
     487           0 :         bRelativeToVRT = false;
     488             :     }
     489             :     else
     490             :     {
     491        2581 :         std::tie(osSourceFilename, bRelativeToVRT) =
     492        5162 :             ComputeSourceNameAndRelativeFlag(pszVRTPath, m_osSrcDSName);
     493             :     }
     494             : 
     495        2625 :     CPLSetXMLValue(psSrc, "SourceFilename", osSourceFilename.c_str());
     496             : 
     497        2625 :     CPLCreateXMLNode(CPLCreateXMLNode(CPLGetXMLNode(psSrc, "SourceFilename"),
     498             :                                       CXT_Attribute, "relativeToVRT"),
     499             :                      CXT_Text, bRelativeToVRT ? "1" : "0");
     500             : 
     501             :     // Determine if we must write the shared attribute. The config option
     502             :     // will override the m_nExplicitSharedStatus value
     503        2625 :     const char *pszShared = CPLGetConfigOption("VRT_SHARED_SOURCE", nullptr);
     504        2625 :     if ((pszShared == nullptr && m_nExplicitSharedStatus == 0) ||
     505           0 :         (pszShared != nullptr && !CPLTestBool(pszShared)))
     506             :     {
     507           0 :         CPLCreateXMLNode(
     508             :             CPLCreateXMLNode(CPLGetXMLNode(psSrc, "SourceFilename"),
     509             :                              CXT_Attribute, "shared"),
     510             :             CXT_Text, "0");
     511             :     }
     512        2625 : }
     513             : 
     514             : /************************************************************************/
     515             : /*                           SerializeToXML()                           */
     516             : /************************************************************************/
     517             : 
     518        2669 : CPLXMLNode *VRTSimpleSource::SerializeToXML(const char *pszVRTPath)
     519             : 
     520             : {
     521             :     CPLXMLNode *const psSrc =
     522        2669 :         CPLCreateXMLNode(nullptr, CXT_Element, GetTypeStatic());
     523             : 
     524        2669 :     if (!m_osResampling.empty())
     525             :     {
     526          24 :         CPLCreateXMLNode(CPLCreateXMLNode(psSrc, CXT_Attribute, "resampling"),
     527             :                          CXT_Text, m_osResampling.c_str());
     528             :     }
     529             : 
     530        2669 :     if (!m_osName.empty())
     531             :     {
     532          74 :         CPLAddXMLAttributeAndValue(psSrc, "name", m_osName.c_str());
     533             :     }
     534             : 
     535        2669 :     if (m_bSrcDSNameFromVRT)
     536             :     {
     537           1 :         CPLAddXMLChild(psSrc, CPLParseXMLString(m_osSrcDSName.c_str()));
     538             :     }
     539             :     else
     540             :     {
     541        2668 :         bool bDone = false;
     542        2668 :         if (m_osSrcDSName.empty() && m_poRasterBand)
     543             :         {
     544         103 :             auto poSrcDS = m_poRasterBand->GetDataset();
     545         103 :             if (poSrcDS)
     546             :             {
     547         103 :                 VRTDataset *poSrcVRTDS = nullptr;
     548             :                 // For GDALComputedDataset
     549         103 :                 void *pHandle = poSrcDS->GetInternalHandle("VRT_DATASET");
     550         103 :                 if (pHandle && poSrcDS->GetInternalHandle(nullptr) == nullptr)
     551             :                 {
     552          40 :                     poSrcVRTDS = static_cast<VRTDataset *>(pHandle);
     553             :                 }
     554             :                 else
     555             :                 {
     556          63 :                     poSrcVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
     557             :                 }
     558         103 :                 if (poSrcVRTDS)
     559             :                 {
     560          43 :                     poSrcVRTDS->UnsetPreservedRelativeFilenames();
     561          43 :                     CPLAddXMLChild(psSrc,
     562          43 :                                    poSrcVRTDS->SerializeToXML(pszVRTPath));
     563          43 :                     bDone = true;
     564             :                 }
     565             :             }
     566             :         }
     567        2668 :         if (!bDone)
     568             :         {
     569        2625 :             AddSourceFilenameNode(pszVRTPath, psSrc);
     570             :         }
     571             :     }
     572             : 
     573        2669 :     GDALSerializeOpenOptionsToXML(psSrc, m_aosOpenOptionsOri.List());
     574             : 
     575        2669 :     if (m_bGetMaskBand)
     576          19 :         CPLSetXMLValue(psSrc, "SourceBand", CPLSPrintf("mask,%d", m_nBand));
     577             :     else
     578        2650 :         CPLSetXMLValue(psSrc, "SourceBand", CPLSPrintf("%d", m_nBand));
     579             : 
     580             :     // TODO: in a later version, no longer emit SourceProperties, which
     581             :     // is no longer used by GDAL 3.4
     582        2669 :     if (m_poRasterBand)
     583             :     {
     584             :         /* Write a few additional useful properties of the dataset */
     585             :         /* so that we can use a proxy dataset when re-opening. See XMLInit() */
     586             :         /* below */
     587        2556 :         CPLSetXMLValue(psSrc, "SourceProperties.#RasterXSize",
     588        2556 :                        CPLSPrintf("%d", m_poRasterBand->GetXSize()));
     589        2556 :         CPLSetXMLValue(psSrc, "SourceProperties.#RasterYSize",
     590        2556 :                        CPLSPrintf("%d", m_poRasterBand->GetYSize()));
     591        2556 :         CPLSetXMLValue(
     592             :             psSrc, "SourceProperties.#DataType",
     593        2556 :             GDALGetDataTypeName(m_poRasterBand->GetRasterDataType()));
     594             : 
     595        2556 :         int nBlockXSize = 0;
     596        2556 :         int nBlockYSize = 0;
     597        2556 :         m_poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     598             : 
     599        2556 :         CPLSetXMLValue(psSrc, "SourceProperties.#BlockXSize",
     600             :                        CPLSPrintf("%d", nBlockXSize));
     601        2556 :         CPLSetXMLValue(psSrc, "SourceProperties.#BlockYSize",
     602             :                        CPLSPrintf("%d", nBlockYSize));
     603             :     }
     604             : 
     605        2669 :     if (IsSrcWinSet())
     606             :     {
     607        2655 :         CPLSetXMLValue(psSrc, "SrcRect.#xOff",
     608             :                        CPLSPrintf("%.15g", m_dfSrcXOff));
     609        2655 :         CPLSetXMLValue(psSrc, "SrcRect.#yOff",
     610             :                        CPLSPrintf("%.15g", m_dfSrcYOff));
     611        2655 :         CPLSetXMLValue(psSrc, "SrcRect.#xSize",
     612             :                        CPLSPrintf("%.15g", m_dfSrcXSize));
     613        2655 :         CPLSetXMLValue(psSrc, "SrcRect.#ySize",
     614             :                        CPLSPrintf("%.15g", m_dfSrcYSize));
     615             :     }
     616             : 
     617        2669 :     if (IsDstWinSet())
     618             :     {
     619        2655 :         CPLSetXMLValue(psSrc, "DstRect.#xOff",
     620             :                        CPLSPrintf("%.15g", m_dfDstXOff));
     621        2655 :         CPLSetXMLValue(psSrc, "DstRect.#yOff",
     622             :                        CPLSPrintf("%.15g", m_dfDstYOff));
     623        2655 :         CPLSetXMLValue(psSrc, "DstRect.#xSize",
     624             :                        CPLSPrintf("%.15g", m_dfDstXSize));
     625        2655 :         CPLSetXMLValue(psSrc, "DstRect.#ySize",
     626             :                        CPLSPrintf("%.15g", m_dfDstYSize));
     627             :     }
     628             : 
     629        2669 :     return psSrc;
     630             : }
     631             : 
     632             : /************************************************************************/
     633             : /*                              XMLInit()                               */
     634             : /************************************************************************/
     635             : 
     636      104142 : CPLErr VRTSimpleSource::XMLInit(const CPLXMLNode *psSrc, const char *pszVRTPath,
     637             :                                 VRTMapSharedResources &oMapSharedSources)
     638             : 
     639             : {
     640      104142 :     m_poMapSharedSources = &oMapSharedSources;
     641             : 
     642      104142 :     m_osResampling = CPLGetXMLValue(psSrc, "resampling", "");
     643      104142 :     m_osName = CPLGetXMLValue(psSrc, "name", "");
     644             : 
     645             :     /* -------------------------------------------------------------------- */
     646             :     /*      Prepare filename.                                               */
     647             :     /* -------------------------------------------------------------------- */
     648             :     const CPLXMLNode *psSourceFileNameNode =
     649      104142 :         CPLGetXMLNode(psSrc, "SourceFilename");
     650      104142 :     const CPLXMLNode *psSourceVRTDataset = CPLGetXMLNode(psSrc, "VRTDataset");
     651             :     const char *pszFilename =
     652      104142 :         psSourceFileNameNode ? CPLGetXMLValue(psSourceFileNameNode, nullptr, "")
     653      104142 :                              : "";
     654             : 
     655      104142 :     if (pszFilename[0] == '\0' && !psSourceVRTDataset)
     656             :     {
     657           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     658             :                  "Missing <SourceFilename> or <VRTDataset> element in <%s>.",
     659           1 :                  psSrc->pszValue);
     660           1 :         return CE_Failure;
     661             :     }
     662             : 
     663             :     // Backup original filename and relativeToVRT so as to be able to
     664             :     // serialize them identically again (#5985)
     665      104141 :     m_osSourceFileNameOri = pszFilename;
     666      104141 :     if (pszFilename[0])
     667             :     {
     668      104129 :         m_bRelativeToVRTOri =
     669      104129 :             atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0"));
     670             :         const char *pszShared =
     671      104129 :             CPLGetXMLValue(psSourceFileNameNode, "shared", nullptr);
     672      104129 :         if (pszShared == nullptr)
     673             :         {
     674      104127 :             pszShared = CPLGetConfigOption("VRT_SHARED_SOURCE", nullptr);
     675             :         }
     676      104129 :         if (pszShared != nullptr)
     677             :         {
     678           8 :             m_nExplicitSharedStatus = CPLTestBool(pszShared);
     679             :         }
     680             : 
     681      208258 :         m_osSrcDSName = GDALDataset::BuildFilename(
     682      208258 :             pszFilename, pszVRTPath, CPL_TO_BOOL(m_bRelativeToVRTOri));
     683             :     }
     684          12 :     else if (psSourceVRTDataset)
     685             :     {
     686             :         CPLXMLNode sNode;
     687          12 :         sNode.eType = psSourceVRTDataset->eType;
     688          12 :         sNode.pszValue = psSourceVRTDataset->pszValue;
     689          12 :         sNode.psNext = nullptr;
     690          12 :         sNode.psChild = psSourceVRTDataset->psChild;
     691          12 :         char *pszXML = CPLSerializeXMLTree(&sNode);
     692          12 :         if (pszXML)
     693             :         {
     694          12 :             m_bSrcDSNameFromVRT = true;
     695          12 :             m_osSrcDSName = pszXML;
     696          12 :             CPLFree(pszXML);
     697             :         }
     698             :     }
     699             : 
     700      104141 :     const char *pszSourceBand = CPLGetXMLValue(psSrc, "SourceBand", "1");
     701      104141 :     m_bGetMaskBand = false;
     702      104141 :     if (STARTS_WITH_CI(pszSourceBand, "mask"))
     703             :     {
     704          23 :         m_bGetMaskBand = true;
     705          23 :         if (pszSourceBand[4] == ',')
     706          23 :             m_nBand = atoi(pszSourceBand + 5);
     707             :         else
     708           0 :             m_nBand = 1;
     709             :     }
     710             :     else
     711             :     {
     712      104118 :         m_nBand = atoi(pszSourceBand);
     713             :     }
     714      104141 :     if (!GDALCheckBandCount(m_nBand, 0))
     715             :     {
     716           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     717             :                  "Invalid <SourceBand> element in VRTRasterBand.");
     718           0 :         return CE_Failure;
     719             :     }
     720             : 
     721      104141 :     m_aosOpenOptions = GDALDeserializeOpenOptionsFromXML(psSrc);
     722      104141 :     m_aosOpenOptionsOri = m_aosOpenOptions;
     723      104141 :     if (strstr(m_osSrcDSName.c_str(), "<VRTDataset") != nullptr)
     724          14 :         m_aosOpenOptions.SetNameValue("ROOT_PATH", pszVRTPath);
     725             : 
     726      104141 :     return ParseSrcRectAndDstRect(psSrc);
     727             : }
     728             : 
     729             : /************************************************************************/
     730             : /*                       ParseSrcRectAndDstRect()                       */
     731             : /************************************************************************/
     732             : 
     733      104164 : CPLErr VRTSimpleSource::ParseSrcRectAndDstRect(const CPLXMLNode *psSrc)
     734             : {
     735       24876 :     const auto GetAttrValue = [](const CPLXMLNode *psNode,
     736             :                                  const char *pszAttrName, double dfDefaultVal)
     737             :     {
     738       24876 :         if (const char *pszVal = CPLGetXMLValue(psNode, pszAttrName, nullptr))
     739       24876 :             return CPLAtof(pszVal);
     740             :         else
     741           0 :             return dfDefaultVal;
     742             :     };
     743             : 
     744             :     /* -------------------------------------------------------------------- */
     745             :     /*      Set characteristics.                                            */
     746             :     /* -------------------------------------------------------------------- */
     747      104164 :     const CPLXMLNode *const psSrcRect = CPLGetXMLNode(psSrc, "SrcRect");
     748      104164 :     if (psSrcRect)
     749             :     {
     750        3110 :         double xOff = GetAttrValue(psSrcRect, "xOff", UNINIT_WINDOW);
     751        3110 :         double yOff = GetAttrValue(psSrcRect, "yOff", UNINIT_WINDOW);
     752        3110 :         double xSize = GetAttrValue(psSrcRect, "xSize", UNINIT_WINDOW);
     753        3110 :         double ySize = GetAttrValue(psSrcRect, "ySize", UNINIT_WINDOW);
     754             :         // Test written that way to catch NaN values
     755        3110 :         if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
     756        3110 :             !(yOff >= INT_MIN && yOff <= INT_MAX) ||
     757        3110 :             !(xSize > 0 || xSize == UNINIT_WINDOW) || xSize > INT_MAX ||
     758        3109 :             !(ySize > 0 || ySize == UNINIT_WINDOW) || ySize > INT_MAX)
     759             :         {
     760           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in SrcRect");
     761           1 :             return CE_Failure;
     762             :         }
     763        3109 :         SetSrcWindow(xOff, yOff, xSize, ySize);
     764             :     }
     765             :     else
     766             :     {
     767      101054 :         m_dfSrcXOff = UNINIT_WINDOW;
     768      101054 :         m_dfSrcYOff = UNINIT_WINDOW;
     769      101054 :         m_dfSrcXSize = UNINIT_WINDOW;
     770      101054 :         m_dfSrcYSize = UNINIT_WINDOW;
     771             :     }
     772             : 
     773      104163 :     const CPLXMLNode *const psDstRect = CPLGetXMLNode(psSrc, "DstRect");
     774      104163 :     if (psDstRect)
     775             :     {
     776        3109 :         double xOff = GetAttrValue(psDstRect, "xOff", UNINIT_WINDOW);
     777             :         ;
     778        3109 :         double yOff = GetAttrValue(psDstRect, "yOff", UNINIT_WINDOW);
     779        3109 :         double xSize = GetAttrValue(psDstRect, "xSize", UNINIT_WINDOW);
     780             :         ;
     781        3109 :         double ySize = GetAttrValue(psDstRect, "ySize", UNINIT_WINDOW);
     782             :         // Test written that way to catch NaN values
     783        3109 :         if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
     784        3109 :             !(yOff >= INT_MIN && yOff <= INT_MAX) ||
     785        3109 :             !(xSize > 0 || xSize == UNINIT_WINDOW) || xSize > INT_MAX ||
     786        3109 :             !(ySize > 0 || ySize == UNINIT_WINDOW) || ySize > INT_MAX)
     787             :         {
     788           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in DstRect");
     789           1 :             return CE_Failure;
     790             :         }
     791        3108 :         SetDstWindow(xOff, yOff, xSize, ySize);
     792             :     }
     793             :     else
     794             :     {
     795      101054 :         m_dfDstXOff = UNINIT_WINDOW;
     796      101054 :         m_dfDstYOff = UNINIT_WINDOW;
     797      101054 :         m_dfDstXSize = UNINIT_WINDOW;
     798      101054 :         m_dfDstYSize = UNINIT_WINDOW;
     799             :     }
     800             : 
     801      104162 :     return CE_None;
     802             : }
     803             : 
     804             : /************************************************************************/
     805             : /*                            GetFileList()                             */
     806             : /************************************************************************/
     807             : 
     808         114 : void VRTSimpleSource::GetFileList(char ***ppapszFileList, int *pnSize,
     809             :                                   int *pnMaxSize, CPLHashSet *hSetFiles)
     810             : {
     811         114 :     if (!m_osSrcDSName.empty() && !m_bSrcDSNameFromVRT)
     812             :     {
     813         114 :         const char *pszFilename = m_osSrcDSName.c_str();
     814             : 
     815             :         /* --------------------------------------------------------------------
     816             :          */
     817             :         /*      Is it already in the list ? */
     818             :         /* --------------------------------------------------------------------
     819             :          */
     820         114 :         if (CPLHashSetLookup(hSetFiles, pszFilename) != nullptr)
     821          15 :             return;
     822             : 
     823             :         /* --------------------------------------------------------------------
     824             :          */
     825             :         /*      Grow array if necessary */
     826             :         /* --------------------------------------------------------------------
     827             :          */
     828          99 :         if (*pnSize + 1 >= *pnMaxSize)
     829             :         {
     830          68 :             *pnMaxSize = std::max(*pnSize + 2, 2 + 2 * (*pnMaxSize));
     831          68 :             *ppapszFileList = static_cast<char **>(
     832          68 :                 CPLRealloc(*ppapszFileList, sizeof(char *) * (*pnMaxSize)));
     833             :         }
     834             : 
     835             :         /* --------------------------------------------------------------------
     836             :          */
     837             :         /*      Add the string to the list */
     838             :         /* --------------------------------------------------------------------
     839             :          */
     840          99 :         (*ppapszFileList)[*pnSize] = CPLStrdup(pszFilename);
     841          99 :         (*ppapszFileList)[(*pnSize + 1)] = nullptr;
     842          99 :         CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]);
     843             : 
     844          99 :         (*pnSize)++;
     845             :     }
     846             : }
     847             : 
     848             : /************************************************************************/
     849             : /*                             OpenSource()                             */
     850             : /************************************************************************/
     851             : 
     852      102292 : void VRTSimpleSource::OpenSource() const
     853             : {
     854      102292 :     CPLAssert(m_poRasterBand == nullptr);
     855             : 
     856             :     /* ----------------------------------------------------------------- */
     857             :     /*      Create a proxy dataset                                       */
     858             :     /* ----------------------------------------------------------------- */
     859      102292 :     GDALProxyPoolDataset *proxyDS = nullptr;
     860      102292 :     std::string osKeyMapSharedSources;
     861      102292 :     if (m_poMapSharedSources)
     862             :     {
     863      102244 :         osKeyMapSharedSources = m_osSrcDSName;
     864      102258 :         for (int i = 0; i < m_aosOpenOptions.size(); ++i)
     865             :         {
     866          14 :             osKeyMapSharedSources += "||";
     867          14 :             osKeyMapSharedSources += m_aosOpenOptions[i];
     868             :         }
     869             : 
     870      102244 :         proxyDS = cpl::down_cast<GDALProxyPoolDataset *>(
     871      102244 :             m_poMapSharedSources->Get(osKeyMapSharedSources));
     872             :     }
     873             : 
     874      102292 :     if (proxyDS == nullptr)
     875             :     {
     876        3314 :         int bShared = true;
     877        3314 :         if (m_nExplicitSharedStatus != -1)
     878           8 :             bShared = m_nExplicitSharedStatus;
     879             : 
     880        3314 :         const CPLString osUniqueHandle(CPLSPrintf("%p", m_poMapSharedSources));
     881        3314 :         proxyDS = GDALProxyPoolDataset::Create(
     882             :             m_osSrcDSName, m_aosOpenOptions.List(), GA_ReadOnly, bShared,
     883             :             osUniqueHandle.c_str());
     884        3314 :         if (proxyDS == nullptr)
     885         266 :             return;
     886             :     }
     887             :     else
     888             :     {
     889       98978 :         proxyDS->Reference();
     890             :     }
     891             : 
     892      102026 :     if (m_bGetMaskBand)
     893             :     {
     894             :         GDALProxyPoolRasterBand *poMaskBand =
     895          15 :             cpl::down_cast<GDALProxyPoolRasterBand *>(
     896          15 :                 proxyDS->GetRasterBand(m_nBand));
     897          15 :         poMaskBand->AddSrcMaskBandDescriptionFromUnderlying();
     898             :     }
     899             : 
     900             :     /* -------------------------------------------------------------------- */
     901             :     /*      Get the raster band.                                            */
     902             :     /* -------------------------------------------------------------------- */
     903             : 
     904      102026 :     m_poRasterBand = proxyDS->GetRasterBand(m_nBand);
     905      102026 :     if (m_poRasterBand == nullptr || !ValidateOpenedBand(m_poRasterBand))
     906             :     {
     907           2 :         proxyDS->ReleaseRef();
     908           2 :         return;
     909             :     }
     910             : 
     911      102024 :     if (m_bGetMaskBand)
     912             :     {
     913          15 :         m_poRasterBand = m_poRasterBand->GetMaskBand();
     914          15 :         if (m_poRasterBand == nullptr)
     915             :         {
     916           0 :             proxyDS->ReleaseRef();
     917           0 :             return;
     918             :         }
     919          15 :         m_poMaskBandMainBand = m_poRasterBand;
     920             :     }
     921             : 
     922      102024 :     if (m_poMapSharedSources)
     923             :     {
     924      101976 :         m_poMapSharedSources->Insert(osKeyMapSharedSources, proxyDS);
     925             :     }
     926             : }
     927             : 
     928             : /************************************************************************/
     929             : /*                           GetRasterBand()                            */
     930             : /************************************************************************/
     931             : 
     932      963887 : GDALRasterBand *VRTSimpleSource::GetRasterBand() const
     933             : {
     934      963887 :     if (m_poRasterBand == nullptr)
     935      102135 :         OpenSource();
     936      963887 :     return m_poRasterBand;
     937             : }
     938             : 
     939             : /************************************************************************/
     940             : /*                        GetMaskBandMainBand()                         */
     941             : /************************************************************************/
     942             : 
     943        5815 : GDALRasterBand *VRTSimpleSource::GetMaskBandMainBand()
     944             : {
     945        5815 :     if (m_poRasterBand == nullptr)
     946         157 :         OpenSource();
     947        5815 :     return m_poMaskBandMainBand;
     948             : }
     949             : 
     950             : /************************************************************************/
     951             : /*                       IsSameExceptBandNumber()                       */
     952             : /************************************************************************/
     953             : 
     954        3078 : bool VRTSimpleSource::IsSameExceptBandNumber(
     955             :     const VRTSimpleSource *poOtherSource) const
     956             : {
     957        6156 :     return m_dfSrcXOff == poOtherSource->m_dfSrcXOff &&
     958        3078 :            m_dfSrcYOff == poOtherSource->m_dfSrcYOff &&
     959        3078 :            m_dfSrcXSize == poOtherSource->m_dfSrcXSize &&
     960        3078 :            m_dfSrcYSize == poOtherSource->m_dfSrcYSize &&
     961        3078 :            m_dfDstXOff == poOtherSource->m_dfDstXOff &&
     962        3078 :            m_dfDstYOff == poOtherSource->m_dfDstYOff &&
     963        3078 :            m_dfDstXSize == poOtherSource->m_dfDstXSize &&
     964        9234 :            m_dfDstYSize == poOtherSource->m_dfDstYSize &&
     965        6156 :            m_osSrcDSName == poOtherSource->m_osSrcDSName;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                              SrcToDst()                              */
     970             : /*                                                                      */
     971             : /*      Note: this is a no-op if the both src and dst windows are unset */
     972             : /************************************************************************/
     973             : 
     974       15846 : void VRTSimpleSource::SrcToDst(double dfX, double dfY, double &dfXOut,
     975             :                                double &dfYOut) const
     976             : 
     977             : {
     978       15846 :     dfXOut = ((dfX - m_dfSrcXOff) / m_dfSrcXSize) * m_dfDstXSize + m_dfDstXOff;
     979       15846 :     dfYOut = ((dfY - m_dfSrcYOff) / m_dfSrcYSize) * m_dfDstYSize + m_dfDstYOff;
     980       15846 : }
     981             : 
     982             : /************************************************************************/
     983             : /*                              DstToSrc()                              */
     984             : /*                                                                      */
     985             : /*      Note: this is a no-op if the both src and dst windows are unset */
     986             : /************************************************************************/
     987             : 
     988     4022310 : void VRTSimpleSource::DstToSrc(double dfX, double dfY, double &dfXOut,
     989             :                                double &dfYOut) const
     990             : 
     991             : {
     992     4022310 :     dfXOut = ((dfX - m_dfDstXOff) / m_dfDstXSize) * m_dfSrcXSize + m_dfSrcXOff;
     993     4022310 :     dfYOut = ((dfY - m_dfDstYOff) / m_dfDstYSize) * m_dfSrcYSize + m_dfSrcYOff;
     994     4022310 : }
     995             : 
     996             : /************************************************************************/
     997             : /*                          GetSrcDstWindow()                           */
     998             : /************************************************************************/
     999             : 
    1000           0 : int VRTSimpleSource::GetSrcDstWindow(
    1001             :     double dfXOff, double dfYOff, double dfXSize, double dfYSize, int nBufXSize,
    1002             :     int nBufYSize, double *pdfReqXOff, double *pdfReqYOff, double *pdfReqXSize,
    1003             :     double *pdfReqYSize, int *pnReqXOff, int *pnReqYOff, int *pnReqXSize,
    1004             :     int *pnReqYSize, int *pnOutXOff, int *pnOutYOff, int *pnOutXSize,
    1005             :     int *pnOutYSize, bool &bErrorOut)
    1006             : 
    1007             : {
    1008           0 :     return GetSrcDstWindow(
    1009             :         dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    1010             :         GRIORA_Bilinear,  // to stick with legacy behavior
    1011             :         pdfReqXOff, pdfReqYOff, pdfReqXSize, pdfReqYSize, pnReqXOff, pnReqYOff,
    1012             :         pnReqXSize, pnReqYSize, pnOutXOff, pnOutYOff, pnOutXSize, pnOutYSize,
    1013           0 :         bErrorOut);
    1014             : }
    1015             : 
    1016      396748 : int VRTSimpleSource::GetSrcDstWindow(
    1017             :     double dfXOff, double dfYOff, double dfXSize, double dfYSize, int nBufXSize,
    1018             :     int nBufYSize, GDALRIOResampleAlg eResampleAlg, double *pdfReqXOff,
    1019             :     double *pdfReqYOff, double *pdfReqXSize, double *pdfReqYSize,
    1020             :     int *pnReqXOff, int *pnReqYOff, int *pnReqXSize, int *pnReqYSize,
    1021             :     int *pnOutXOff, int *pnOutYOff, int *pnOutXSize, int *pnOutYSize,
    1022             :     bool &bErrorOut)
    1023             : 
    1024             : {
    1025      396748 :     bErrorOut = false;
    1026             : 
    1027      396748 :     if (m_dfSrcXSize == 0.0 || m_dfSrcYSize == 0.0 || m_dfDstXSize == 0.0 ||
    1028      396748 :         m_dfDstYSize == 0.0)
    1029             :     {
    1030           0 :         return FALSE;
    1031             :     }
    1032             : 
    1033      396748 :     const bool bDstWinSet = IsDstWinSet();
    1034             : 
    1035             : #ifdef DEBUG
    1036      396748 :     const bool bSrcWinSet = IsSrcWinSet();
    1037             : 
    1038      396748 :     if (bSrcWinSet != bDstWinSet)
    1039             :     {
    1040           0 :         return FALSE;
    1041             :     }
    1042             : #endif
    1043             : 
    1044             :     /* -------------------------------------------------------------------- */
    1045             :     /*      If the input window completely misses the portion of the        */
    1046             :     /*      virtual dataset provided by this source we have nothing to do.  */
    1047             :     /* -------------------------------------------------------------------- */
    1048      396748 :     if (bDstWinSet)
    1049             :     {
    1050      293615 :         if (dfXOff >= m_dfDstXOff + m_dfDstXSize ||
    1051      282587 :             dfYOff >= m_dfDstYOff + m_dfDstYSize ||
    1052      279747 :             dfXOff + dfXSize <= m_dfDstXOff || dfYOff + dfYSize <= m_dfDstYOff)
    1053       25496 :             return FALSE;
    1054             :     }
    1055             : 
    1056             :     /* -------------------------------------------------------------------- */
    1057             :     /*      This request window corresponds to the whole output buffer.     */
    1058             :     /* -------------------------------------------------------------------- */
    1059      371252 :     *pnOutXOff = 0;
    1060      371252 :     *pnOutYOff = 0;
    1061      371252 :     *pnOutXSize = nBufXSize;
    1062      371252 :     *pnOutYSize = nBufYSize;
    1063             : 
    1064             :     /* -------------------------------------------------------------------- */
    1065             :     /*      If the input window extents outside the portion of the on       */
    1066             :     /*      the virtual file that this source can set, then clip down       */
    1067             :     /*      the requested window.                                           */
    1068             :     /* -------------------------------------------------------------------- */
    1069      371252 :     bool bModifiedX = false;
    1070      371252 :     bool bModifiedY = false;
    1071      371252 :     double dfRXOff = dfXOff;
    1072      371252 :     double dfRYOff = dfYOff;
    1073      371252 :     double dfRXSize = dfXSize;
    1074      371252 :     double dfRYSize = dfYSize;
    1075             : 
    1076      371252 :     if (bDstWinSet)
    1077             :     {
    1078      268119 :         if (dfRXOff < m_dfDstXOff)
    1079             :         {
    1080        3340 :             dfRXSize = dfRXSize + dfRXOff - m_dfDstXOff;
    1081        3340 :             dfRXOff = m_dfDstXOff;
    1082        3340 :             bModifiedX = true;
    1083             :         }
    1084             : 
    1085      268119 :         if (dfRYOff < m_dfDstYOff)
    1086             :         {
    1087         179 :             dfRYSize = dfRYSize + dfRYOff - m_dfDstYOff;
    1088         179 :             dfRYOff = m_dfDstYOff;
    1089         179 :             bModifiedY = true;
    1090             :         }
    1091             : 
    1092      268119 :         if (dfRXOff + dfRXSize > m_dfDstXOff + m_dfDstXSize)
    1093             :         {
    1094        4885 :             dfRXSize = m_dfDstXOff + m_dfDstXSize - dfRXOff;
    1095        4885 :             bModifiedX = true;
    1096             :         }
    1097             : 
    1098      268119 :         if (dfRYOff + dfRYSize > m_dfDstYOff + m_dfDstYSize)
    1099             :         {
    1100         358 :             dfRYSize = m_dfDstYOff + m_dfDstYSize - dfRYOff;
    1101         358 :             bModifiedY = true;
    1102             :         }
    1103             :     }
    1104             : 
    1105             :     /* -------------------------------------------------------------------- */
    1106             :     /*      Translate requested region in virtual file into the source      */
    1107             :     /*      band coordinates.                                               */
    1108             :     /* -------------------------------------------------------------------- */
    1109      371252 :     const double dfScaleX = m_dfSrcXSize / m_dfDstXSize;
    1110      371252 :     const double dfScaleY = m_dfSrcYSize / m_dfDstYSize;
    1111             : 
    1112      371252 :     *pdfReqXOff = (dfRXOff - m_dfDstXOff) * dfScaleX + m_dfSrcXOff;
    1113      371252 :     *pdfReqYOff = (dfRYOff - m_dfDstYOff) * dfScaleY + m_dfSrcYOff;
    1114      371252 :     *pdfReqXSize = dfRXSize * dfScaleX;
    1115      371252 :     *pdfReqYSize = dfRYSize * dfScaleY;
    1116             : 
    1117      742504 :     if (!std::isfinite(*pdfReqXOff) || !std::isfinite(*pdfReqYOff) ||
    1118      371252 :         !std::isfinite(*pdfReqXSize) || !std::isfinite(*pdfReqYSize) ||
    1119     1113760 :         *pdfReqXOff > INT_MAX || *pdfReqYOff > INT_MAX || *pdfReqXSize < 0 ||
    1120      371252 :         *pdfReqYSize < 0)
    1121             :     {
    1122           0 :         return FALSE;
    1123             :     }
    1124             : 
    1125             :     /* -------------------------------------------------------------------- */
    1126             :     /*      Clamp within the bounds of the available source data.           */
    1127             :     /* -------------------------------------------------------------------- */
    1128      371252 :     if (*pdfReqXOff < 0)
    1129             :     {
    1130           9 :         *pdfReqXSize += *pdfReqXOff;
    1131           9 :         *pdfReqXOff = 0;
    1132           9 :         bModifiedX = true;
    1133             :     }
    1134      371252 :     if (*pdfReqYOff < 0)
    1135             :     {
    1136           9 :         *pdfReqYSize += *pdfReqYOff;
    1137           9 :         *pdfReqYOff = 0;
    1138           9 :         bModifiedY = true;
    1139             :     }
    1140             : 
    1141      371252 :     constexpr double EPSILON = 1e-10;
    1142      371252 :     if (eResampleAlg == GRIORA_NearestNeighbour &&
    1143      365216 :         (std::fabs(m_dfSrcXOff - std::round(m_dfSrcXOff)) > EPSILON ||
    1144      365152 :          std::fabs(m_dfSrcYOff - std::round(m_dfSrcYOff)) > EPSILON ||
    1145      365150 :          std::fabs(m_dfDstXOff - std::round(m_dfDstXOff)) > EPSILON ||
    1146      365145 :          std::fabs(m_dfDstYOff - std::round(m_dfDstYOff)) > EPSILON))
    1147             :     {
    1148             :         // Align the behavior with https://github.com/OSGeo/gdal/blob/0e5bb914b80d049198d9a85e04b22c9b0590cc36/gcore/rasterio.cpp#L799
    1149             :         // in the way we round coordinates
    1150             :         // Add small epsilon to avoid some numeric precision issues.
    1151             :         // Covered by test_vrt_read_multithreaded_non_integer_coordinates_nearest test
    1152          79 :         *pnReqXOff = static_cast<int>(*pdfReqXOff + 0.5 + EPSILON);
    1153          79 :         *pnReqYOff = static_cast<int>(*pdfReqYOff + 0.5 + EPSILON);
    1154             :     }
    1155             :     else
    1156             :     {
    1157      371173 :         *pnReqXOff = static_cast<int>(*pdfReqXOff);
    1158      371173 :         *pnReqYOff = static_cast<int>(*pdfReqYOff);
    1159             :     }
    1160             : 
    1161      371252 :     constexpr double EPS = 1e-3;
    1162      371252 :     constexpr double ONE_MINUS_EPS = 1.0 - EPS;
    1163      371252 :     if (*pdfReqXOff - *pnReqXOff > ONE_MINUS_EPS)
    1164             :     {
    1165           0 :         (*pnReqXOff)++;
    1166           0 :         *pdfReqXOff = *pnReqXOff;
    1167             :     }
    1168      371252 :     if (*pdfReqYOff - *pnReqYOff > ONE_MINUS_EPS)
    1169             :     {
    1170          16 :         (*pnReqYOff)++;
    1171          16 :         *pdfReqYOff = *pnReqYOff;
    1172             :     }
    1173             : 
    1174      371252 :     if (*pdfReqXSize > INT_MAX)
    1175           0 :         *pnReqXSize = INT_MAX;
    1176             :     else
    1177      371252 :         *pnReqXSize = static_cast<int>(floor(*pdfReqXSize + 0.5));
    1178             : 
    1179      371252 :     if (*pdfReqYSize > INT_MAX)
    1180           0 :         *pnReqYSize = INT_MAX;
    1181             :     else
    1182      371252 :         *pnReqYSize = static_cast<int>(floor(*pdfReqYSize + 0.5));
    1183             : 
    1184             :     /* -------------------------------------------------------------------- */
    1185             :     /*      Clamp within the bounds of the available source data.           */
    1186             :     /* -------------------------------------------------------------------- */
    1187             : 
    1188      371252 :     if (*pnReqXSize == 0)
    1189        1213 :         *pnReqXSize = 1;
    1190      371252 :     if (*pnReqYSize == 0)
    1191       24751 :         *pnReqYSize = 1;
    1192             : 
    1193      371252 :     auto l_band = GetRasterBand();
    1194      371252 :     if (!l_band)
    1195             :     {
    1196          94 :         bErrorOut = true;
    1197          94 :         return FALSE;
    1198             :     }
    1199      742316 :     if (*pnReqXSize > INT_MAX - *pnReqXOff ||
    1200      371158 :         *pnReqXOff + *pnReqXSize > l_band->GetXSize())
    1201             :     {
    1202         270 :         *pnReqXSize = l_band->GetXSize() - *pnReqXOff;
    1203         270 :         bModifiedX = true;
    1204             :     }
    1205      371158 :     if (*pdfReqXOff + *pdfReqXSize > l_band->GetXSize())
    1206             :     {
    1207         268 :         *pdfReqXSize = l_band->GetXSize() - *pdfReqXOff;
    1208         268 :         bModifiedX = true;
    1209             :     }
    1210             : 
    1211      742316 :     if (*pnReqYSize > INT_MAX - *pnReqYOff ||
    1212      371158 :         *pnReqYOff + *pnReqYSize > l_band->GetYSize())
    1213             :     {
    1214          48 :         *pnReqYSize = l_band->GetYSize() - *pnReqYOff;
    1215          48 :         bModifiedY = true;
    1216             :     }
    1217      371158 :     if (*pdfReqYOff + *pdfReqYSize > l_band->GetYSize())
    1218             :     {
    1219          66 :         *pdfReqYSize = l_band->GetYSize() - *pdfReqYOff;
    1220          66 :         bModifiedY = true;
    1221             :     }
    1222             : 
    1223             :     /* -------------------------------------------------------------------- */
    1224             :     /*      Don't do anything if the requesting region is completely off    */
    1225             :     /*      the source image.                                               */
    1226             :     /* -------------------------------------------------------------------- */
    1227      742309 :     if (*pnReqXOff >= l_band->GetXSize() || *pnReqYOff >= l_band->GetYSize() ||
    1228      742309 :         *pnReqXSize <= 0 || *pnReqYSize <= 0)
    1229             :     {
    1230          15 :         return FALSE;
    1231             :     }
    1232             : 
    1233             :     /* -------------------------------------------------------------------- */
    1234             :     /*      If we haven't had to modify the source rectangle, then the      */
    1235             :     /*      destination rectangle must be the whole region.                 */
    1236             :     /* -------------------------------------------------------------------- */
    1237      371143 :     if (bModifiedX || bModifiedY)
    1238             :     {
    1239             :         /* --------------------------------------------------------------------
    1240             :          */
    1241             :         /*      Now transform this possibly reduced request back into the */
    1242             :         /*      destination buffer coordinates in case the output region is */
    1243             :         /*      less than the whole buffer. */
    1244             :         /* --------------------------------------------------------------------
    1245             :          */
    1246        7923 :         double dfDstULX = 0.0;
    1247        7923 :         double dfDstULY = 0.0;
    1248        7923 :         double dfDstLRX = 0.0;
    1249        7923 :         double dfDstLRY = 0.0;
    1250             : 
    1251        7923 :         SrcToDst(*pdfReqXOff, *pdfReqYOff, dfDstULX, dfDstULY);
    1252        7923 :         SrcToDst(*pdfReqXOff + *pdfReqXSize, *pdfReqYOff + *pdfReqYSize,
    1253             :                  dfDstLRX, dfDstLRY);
    1254             : #if DEBUG_VERBOSE
    1255             :         CPLDebug("VRT", "dfDstULX=%g dfDstULY=%g dfDstLRX=%g dfDstLRY=%g",
    1256             :                  dfDstULX, dfDstULY, dfDstLRX, dfDstLRY);
    1257             : #endif
    1258             : 
    1259        7923 :         if (bModifiedX)
    1260             :         {
    1261        7861 :             const double dfScaleWinToBufX = nBufXSize / dfXSize;
    1262             : 
    1263        7861 :             const double dfOutXOff = (dfDstULX - dfXOff) * dfScaleWinToBufX;
    1264        7861 :             if (dfOutXOff <= 0)
    1265        4523 :                 *pnOutXOff = 0;
    1266        3338 :             else if (dfOutXOff > INT_MAX)
    1267           0 :                 *pnOutXOff = INT_MAX;
    1268             :             else
    1269        3338 :                 *pnOutXOff = static_cast<int>(dfOutXOff + EPS);
    1270             : 
    1271             :             // Apply correction on floating-point source window
    1272             :             {
    1273        7861 :                 double dfDstDeltaX =
    1274        7861 :                     (dfOutXOff - *pnOutXOff) / dfScaleWinToBufX;
    1275        7861 :                 double dfSrcDeltaX = dfDstDeltaX / m_dfDstXSize * m_dfSrcXSize;
    1276        7861 :                 *pdfReqXOff -= dfSrcDeltaX;
    1277       15722 :                 *pdfReqXSize = std::min(*pdfReqXSize + dfSrcDeltaX,
    1278        7861 :                                         static_cast<double>(INT_MAX));
    1279             :             }
    1280             : 
    1281        7861 :             double dfOutRightXOff = (dfDstLRX - dfXOff) * dfScaleWinToBufX;
    1282        7861 :             if (dfOutRightXOff < dfOutXOff)
    1283           3 :                 return FALSE;
    1284        7861 :             if (dfOutRightXOff > INT_MAX)
    1285           0 :                 dfOutRightXOff = INT_MAX;
    1286        7861 :             const int nOutRightXOff =
    1287        7861 :                 static_cast<int>(ceil(dfOutRightXOff - EPS));
    1288        7861 :             *pnOutXSize = nOutRightXOff - *pnOutXOff;
    1289             : 
    1290        7861 :             if (*pnOutXSize > INT_MAX - *pnOutXOff ||
    1291        7861 :                 *pnOutXOff + *pnOutXSize > nBufXSize)
    1292           0 :                 *pnOutXSize = nBufXSize - *pnOutXOff;
    1293             : 
    1294             :             // Apply correction on floating-point source window
    1295             :             {
    1296        7861 :                 double dfDstDeltaX =
    1297        7861 :                     (nOutRightXOff - dfOutRightXOff) / dfScaleWinToBufX;
    1298        7861 :                 double dfSrcDeltaX = dfDstDeltaX / m_dfDstXSize * m_dfSrcXSize;
    1299       15722 :                 *pdfReqXSize = std::min(*pdfReqXSize + dfSrcDeltaX,
    1300        7861 :                                         static_cast<double>(INT_MAX));
    1301             :             }
    1302             :         }
    1303             : 
    1304        7923 :         if (bModifiedY)
    1305             :         {
    1306         483 :             const double dfScaleWinToBufY = nBufYSize / dfYSize;
    1307             : 
    1308         483 :             const double dfOutYOff = (dfDstULY - dfYOff) * dfScaleWinToBufY;
    1309         483 :             if (dfOutYOff <= 0)
    1310         287 :                 *pnOutYOff = 0;
    1311         196 :             else if (dfOutYOff > INT_MAX)
    1312           0 :                 *pnOutYOff = INT_MAX;
    1313             :             else
    1314         196 :                 *pnOutYOff = static_cast<int>(dfOutYOff + EPS);
    1315             : 
    1316             :             // Apply correction on floating-point source window
    1317             :             {
    1318         483 :                 double dfDstDeltaY =
    1319         483 :                     (dfOutYOff - *pnOutYOff) / dfScaleWinToBufY;
    1320         483 :                 double dfSrcDeltaY = dfDstDeltaY / m_dfDstYSize * m_dfSrcYSize;
    1321         483 :                 *pdfReqYOff -= dfSrcDeltaY;
    1322         966 :                 *pdfReqYSize = std::min(*pdfReqYSize + dfSrcDeltaY,
    1323         483 :                                         static_cast<double>(INT_MAX));
    1324             :             }
    1325             : 
    1326         483 :             double dfOutTopYOff = (dfDstLRY - dfYOff) * dfScaleWinToBufY;
    1327         483 :             if (dfOutTopYOff < dfOutYOff)
    1328           0 :                 return FALSE;
    1329         483 :             if (dfOutTopYOff > INT_MAX)
    1330           0 :                 dfOutTopYOff = INT_MAX;
    1331         483 :             const int nOutTopYOff = static_cast<int>(ceil(dfOutTopYOff - EPS));
    1332         483 :             *pnOutYSize = nOutTopYOff - *pnOutYOff;
    1333             : 
    1334         483 :             if (*pnOutYSize > INT_MAX - *pnOutYOff ||
    1335         483 :                 *pnOutYOff + *pnOutYSize > nBufYSize)
    1336           0 :                 *pnOutYSize = nBufYSize - *pnOutYOff;
    1337             : 
    1338             :             // Apply correction on floating-point source window
    1339             :             {
    1340         483 :                 double dfDstDeltaY =
    1341         483 :                     (nOutTopYOff - dfOutTopYOff) / dfScaleWinToBufY;
    1342         483 :                 double dfSrcDeltaY = dfDstDeltaY / m_dfDstYSize * m_dfSrcYSize;
    1343         966 :                 *pdfReqYSize = std::min(*pdfReqYSize + dfSrcDeltaY,
    1344         483 :                                         static_cast<double>(INT_MAX));
    1345             :             }
    1346             :         }
    1347             : 
    1348        7923 :         if (*pnOutXSize < 1 || *pnOutYSize < 1)
    1349           3 :             return FALSE;
    1350             :     }
    1351             : 
    1352      371140 :     *pdfReqXOff = RoundIfCloseToInt(*pdfReqXOff);
    1353      371140 :     *pdfReqYOff = RoundIfCloseToInt(*pdfReqYOff);
    1354      371140 :     *pdfReqXSize = RoundIfCloseToInt(*pdfReqXSize);
    1355      371140 :     *pdfReqYSize = RoundIfCloseToInt(*pdfReqYSize);
    1356             : 
    1357      371140 :     return TRUE;
    1358             : }
    1359             : 
    1360             : /************************************************************************/
    1361             : /*                        NeedMaxValAdjustment()                        */
    1362             : /************************************************************************/
    1363             : 
    1364      353045 : int VRTSimpleSource::NeedMaxValAdjustment() const
    1365             : {
    1366      353045 :     if (!m_nMaxValue)
    1367      353023 :         return FALSE;
    1368             : 
    1369          22 :     auto l_band = GetRasterBand();
    1370          22 :     if (!l_band)
    1371           0 :         return FALSE;
    1372          22 :     const char *pszNBITS = l_band->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
    1373          22 :     const int nBits = (pszNBITS) ? atoi(pszNBITS) : 0;
    1374          22 :     if (nBits >= 1 && nBits <= 31)
    1375             :     {
    1376           0 :         const int nBandMaxValue = static_cast<int>((1U << nBits) - 1);
    1377           0 :         return nBandMaxValue > m_nMaxValue;
    1378             :     }
    1379          22 :     return TRUE;
    1380             : }
    1381             : 
    1382             : /************************************************************************/
    1383             : /*                             CopyWordIn()                             */
    1384             : /************************************************************************/
    1385             : 
    1386             : template <class DstType>
    1387        4400 : static void CopyWordIn(const void *pSrcVal, GDALDataType eSrcType,
    1388             :                        DstType *pDstVal, GDALDataType eDstType)
    1389             : {
    1390        4400 :     switch (eSrcType)
    1391             :     {
    1392         800 :         case GDT_UInt8:
    1393         800 :             GDALCopyWord(*static_cast<const uint8_t *>(pSrcVal), *pDstVal);
    1394         800 :             break;
    1395           0 :         case GDT_Int8:
    1396           0 :             GDALCopyWord(*static_cast<const int8_t *>(pSrcVal), *pDstVal);
    1397           0 :             break;
    1398           0 :         case GDT_UInt16:
    1399           0 :             GDALCopyWord(*static_cast<const uint16_t *>(pSrcVal), *pDstVal);
    1400           0 :             break;
    1401           0 :         case GDT_Int16:
    1402           0 :             GDALCopyWord(*static_cast<const int16_t *>(pSrcVal), *pDstVal);
    1403           0 :             break;
    1404           0 :         case GDT_UInt32:
    1405           0 :             GDALCopyWord(*static_cast<const uint32_t *>(pSrcVal), *pDstVal);
    1406           0 :             break;
    1407        3600 :         case GDT_Int32:
    1408        3600 :             GDALCopyWord(*static_cast<const int32_t *>(pSrcVal), *pDstVal);
    1409        3600 :             break;
    1410           0 :         case GDT_UInt64:
    1411           0 :             GDALCopyWord(*static_cast<const uint64_t *>(pSrcVal), *pDstVal);
    1412           0 :             break;
    1413           0 :         case GDT_Int64:
    1414           0 :             GDALCopyWord(*static_cast<const int64_t *>(pSrcVal), *pDstVal);
    1415           0 :             break;
    1416           0 :         case GDT_Float16:
    1417           0 :             GDALCopyWord(*static_cast<const GFloat16 *>(pSrcVal), *pDstVal);
    1418           0 :             break;
    1419           0 :         case GDT_Float32:
    1420           0 :             GDALCopyWord(*static_cast<const float *>(pSrcVal), *pDstVal);
    1421           0 :             break;
    1422           0 :         case GDT_Float64:
    1423           0 :             GDALCopyWord(*static_cast<const double *>(pSrcVal), *pDstVal);
    1424           0 :             break;
    1425           0 :         case GDT_CInt16:
    1426             :         case GDT_CInt32:
    1427             :         case GDT_CFloat16:
    1428             :         case GDT_CFloat32:
    1429             :         case GDT_CFloat64:
    1430           0 :             GDALCopyWords(pSrcVal, eSrcType, 0, pDstVal, eDstType, 0, 1);
    1431           0 :             break;
    1432           0 :         case GDT_Unknown:
    1433             :         case GDT_TypeCount:
    1434           0 :             CPLAssert(false);
    1435             :     }
    1436        4400 : }
    1437             : 
    1438             : /************************************************************************/
    1439             : /*                            CopyWordOut()                             */
    1440             : /************************************************************************/
    1441             : 
    1442             : template <class SrcType>
    1443    10070957 : static void CopyWordOut(const SrcType *pSrcVal, GDALDataType eSrcType,
    1444             :                         void *pDstVal, GDALDataType eDstType)
    1445             : {
    1446    10070957 :     switch (eDstType)
    1447             :     {
    1448     2645910 :         case GDT_UInt8:
    1449     2645910 :             GDALCopyWord(*pSrcVal, *static_cast<uint8_t *>(pDstVal));
    1450     2645910 :             break;
    1451          62 :         case GDT_Int8:
    1452          62 :             GDALCopyWord(*pSrcVal, *static_cast<int8_t *>(pDstVal));
    1453          62 :             break;
    1454     2073100 :         case GDT_UInt16:
    1455     2073100 :             GDALCopyWord(*pSrcVal, *static_cast<uint16_t *>(pDstVal));
    1456     2073100 :             break;
    1457     5087870 :         case GDT_Int16:
    1458     5087870 :             GDALCopyWord(*pSrcVal, *static_cast<int16_t *>(pDstVal));
    1459     5087870 :             break;
    1460          22 :         case GDT_UInt32:
    1461          22 :             GDALCopyWord(*pSrcVal, *static_cast<uint32_t *>(pDstVal));
    1462          22 :             break;
    1463        3622 :         case GDT_Int32:
    1464        3622 :             GDALCopyWord(*pSrcVal, *static_cast<int32_t *>(pDstVal));
    1465        3622 :             break;
    1466          22 :         case GDT_UInt64:
    1467          22 :             GDALCopyWord(*pSrcVal, *static_cast<uint64_t *>(pDstVal));
    1468          22 :             break;
    1469          42 :         case GDT_Int64:
    1470          42 :             GDALCopyWord(*pSrcVal, *static_cast<int64_t *>(pDstVal));
    1471          42 :             break;
    1472           0 :         case GDT_Float16:
    1473           0 :             GDALCopyWord(*pSrcVal, *static_cast<GFloat16 *>(pDstVal));
    1474           0 :             break;
    1475      252738 :         case GDT_Float32:
    1476      252738 :             GDALCopyWord(*pSrcVal, *static_cast<float *>(pDstVal));
    1477      252738 :             break;
    1478        6527 :         case GDT_Float64:
    1479        6527 :             GDALCopyWord(*pSrcVal, *static_cast<double *>(pDstVal));
    1480        6527 :             break;
    1481        1019 :         case GDT_CInt16:
    1482             :         case GDT_CInt32:
    1483             :         case GDT_CFloat16:
    1484             :         case GDT_CFloat32:
    1485             :         case GDT_CFloat64:
    1486        1019 :             GDALCopyWords(pSrcVal, eSrcType, 0, pDstVal, eDstType, 0, 1);
    1487        1019 :             break;
    1488           0 :         case GDT_Unknown:
    1489             :         case GDT_TypeCount:
    1490           0 :             CPLAssert(false);
    1491             :     }
    1492    10070957 : }
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                        GDALClampValueToType()                        */
    1496             : /************************************************************************/
    1497             : 
    1498             : template <class T>
    1499      198828 : inline void GDALClampValueToType(T *pValue, GDALDataType eClampingType)
    1500             : {
    1501      198828 :     switch (eClampingType)
    1502             :     {
    1503       48074 :         case GDT_UInt8:
    1504       48074 :             *pValue = GDALClampValueToType<T, uint8_t>(*pValue);
    1505       48074 :             break;
    1506         420 :         case GDT_Int8:
    1507         420 :             *pValue = GDALClampValueToType<T, int8_t>(*pValue);
    1508         420 :             break;
    1509       27480 :         case GDT_UInt16:
    1510       27480 :             *pValue = GDALClampValueToType<T, uint16_t>(*pValue);
    1511       27480 :             break;
    1512       36094 :         case GDT_Int16:
    1513             :         case GDT_CInt16:
    1514       36094 :             *pValue = GDALClampValueToType<T, int16_t>(*pValue);
    1515       36094 :             break;
    1516       24388 :         case GDT_UInt32:
    1517       24388 :             *pValue = GDALClampValueToType<T, uint32_t>(*pValue);
    1518       24388 :             break;
    1519       20636 :         case GDT_Int32:
    1520             :         case GDT_CInt32:
    1521       20636 :             *pValue = GDALClampValueToType<T, int32_t>(*pValue);
    1522       20636 :             break;
    1523           0 :         case GDT_UInt64:
    1524           0 :             *pValue = GDALClampValueToType<T, uint64_t>(*pValue);
    1525           0 :             break;
    1526           0 :         case GDT_Int64:
    1527           0 :             *pValue = GDALClampValueToType<T, int64_t>(*pValue);
    1528           0 :             break;
    1529           0 :         case GDT_Float16:
    1530             :         case GDT_CFloat16:
    1531           0 :             *pValue = GDALClampValueToType<T, GFloat16>(*pValue);
    1532           0 :             break;
    1533       26996 :         case GDT_Float32:
    1534             :         case GDT_CFloat32:
    1535       26996 :             *pValue = GDALClampValueToType<T, float>(*pValue);
    1536       26996 :             break;
    1537       14740 :         case GDT_Float64:
    1538             :         case GDT_CFloat64:
    1539       14740 :             *pValue = GDALClampValueToType<T, double>(*pValue);
    1540       14740 :             break;
    1541           0 :         case GDT_Unknown:
    1542             :         case GDT_TypeCount:
    1543           0 :             CPLAssert(false);
    1544             :             break;
    1545             :     }
    1546      198828 : }
    1547             : 
    1548             : /************************************************************************/
    1549             : /*                        GDALClampValueToType()                        */
    1550             : /************************************************************************/
    1551             : 
    1552      194588 : inline void GDALClampValueToType(void *pValue, GDALDataType eValueType,
    1553             :                                  GDALDataType eClampingType)
    1554             : {
    1555      194588 :     switch (eValueType)
    1556             :     {
    1557       37656 :         case GDT_UInt8:
    1558       37656 :             GDALClampValueToType(static_cast<uint8_t *>(pValue), eClampingType);
    1559       37656 :             break;
    1560         400 :         case GDT_Int8:
    1561         400 :             GDALClampValueToType(static_cast<int8_t *>(pValue), eClampingType);
    1562         400 :             break;
    1563       27480 :         case GDT_UInt16:
    1564       27480 :             GDALClampValueToType(static_cast<uint16_t *>(pValue),
    1565             :                                  eClampingType);
    1566       27480 :             break;
    1567       36094 :         case GDT_Int16:
    1568       36094 :             GDALClampValueToType(static_cast<int16_t *>(pValue), eClampingType);
    1569       36094 :             break;
    1570       24388 :         case GDT_UInt32:
    1571       24388 :             GDALClampValueToType(static_cast<uint32_t *>(pValue),
    1572             :                                  eClampingType);
    1573       24388 :             break;
    1574       31042 :         case GDT_Int32:
    1575       31042 :             GDALClampValueToType(static_cast<int32_t *>(pValue), eClampingType);
    1576       31042 :             break;
    1577           0 :         case GDT_UInt64:
    1578           0 :             GDALClampValueToType(static_cast<uint64_t *>(pValue),
    1579             :                                  eClampingType);
    1580           0 :             break;
    1581           0 :         case GDT_Int64:
    1582           0 :             GDALClampValueToType(static_cast<int64_t *>(pValue), eClampingType);
    1583           0 :             break;
    1584           0 :         case GDT_Float16:
    1585           0 :             GDALClampValueToType(static_cast<GFloat16 *>(pValue),
    1586             :                                  eClampingType);
    1587           0 :             break;
    1588       18788 :         case GDT_Float32:
    1589       18788 :             GDALClampValueToType(static_cast<float *>(pValue), eClampingType);
    1590       18788 :             break;
    1591       14740 :         case GDT_Float64:
    1592       14740 :             GDALClampValueToType(static_cast<double *>(pValue), eClampingType);
    1593       14740 :             break;
    1594           0 :         case GDT_CInt16:
    1595           0 :             GDALClampValueToType(static_cast<int16_t *>(pValue) + 0,
    1596             :                                  eClampingType);
    1597           0 :             GDALClampValueToType(static_cast<int16_t *>(pValue) + 1,
    1598             :                                  eClampingType);
    1599           0 :             break;
    1600           0 :         case GDT_CInt32:
    1601           0 :             GDALClampValueToType(static_cast<int32_t *>(pValue) + 0,
    1602             :                                  eClampingType);
    1603           0 :             GDALClampValueToType(static_cast<int32_t *>(pValue) + 1,
    1604             :                                  eClampingType);
    1605           0 :             break;
    1606           0 :         case GDT_CFloat16:
    1607           0 :             GDALClampValueToType(static_cast<GFloat16 *>(pValue) + 0,
    1608             :                                  eClampingType);
    1609           0 :             GDALClampValueToType(static_cast<GFloat16 *>(pValue) + 1,
    1610             :                                  eClampingType);
    1611           0 :             break;
    1612        4000 :         case GDT_CFloat32:
    1613        4000 :             GDALClampValueToType(static_cast<float *>(pValue) + 0,
    1614             :                                  eClampingType);
    1615        4000 :             GDALClampValueToType(static_cast<float *>(pValue) + 1,
    1616             :                                  eClampingType);
    1617        4000 :             break;
    1618           0 :         case GDT_CFloat64:
    1619           0 :             GDALClampValueToType(static_cast<double *>(pValue) + 0,
    1620             :                                  eClampingType);
    1621           0 :             GDALClampValueToType(static_cast<double *>(pValue) + 1,
    1622             :                                  eClampingType);
    1623           0 :             break;
    1624           0 :         case GDT_Unknown:
    1625             :         case GDT_TypeCount:
    1626           0 :             CPLAssert(false);
    1627             :             break;
    1628             :     }
    1629      194588 : }
    1630             : 
    1631             : /************************************************************************/
    1632             : /*                              RasterIO()                              */
    1633             : /************************************************************************/
    1634             : 
    1635      354784 : CPLErr VRTSimpleSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
    1636             :                                  int nYOff, int nXSize, int nYSize, void *pData,
    1637             :                                  int nBufXSize, int nBufYSize,
    1638             :                                  GDALDataType eBufType, GSpacing nPixelSpace,
    1639             :                                  GSpacing nLineSpace,
    1640             :                                  GDALRasterIOExtraArg *psExtraArgIn,
    1641             :                                  WorkingState & /*oWorkingState*/)
    1642             : 
    1643             : {
    1644             :     GDALRasterIOExtraArg sExtraArg;
    1645      354784 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1646      354784 :     GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
    1647             : 
    1648      354784 :     double dfXOff = nXOff;
    1649      354784 :     double dfYOff = nYOff;
    1650      354784 :     double dfXSize = nXSize;
    1651      354784 :     double dfYSize = nYSize;
    1652      354784 :     if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
    1653             :     {
    1654         356 :         dfXOff = psExtraArgIn->dfXOff;
    1655         356 :         dfYOff = psExtraArgIn->dfYOff;
    1656         356 :         dfXSize = psExtraArgIn->dfXSize;
    1657         356 :         dfYSize = psExtraArgIn->dfYSize;
    1658             :     }
    1659             : 
    1660             :     // The window we will actually request from the source raster band.
    1661      354784 :     double dfReqXOff = 0.0;
    1662      354784 :     double dfReqYOff = 0.0;
    1663      354784 :     double dfReqXSize = 0.0;
    1664      354784 :     double dfReqYSize = 0.0;
    1665      354784 :     int nReqXOff = 0;
    1666      354784 :     int nReqYOff = 0;
    1667      354784 :     int nReqXSize = 0;
    1668      354784 :     int nReqYSize = 0;
    1669             : 
    1670             :     // The window we will actual set _within_ the pData buffer.
    1671      354784 :     int nOutXOff = 0;
    1672      354784 :     int nOutYOff = 0;
    1673      354784 :     int nOutXSize = 0;
    1674      354784 :     int nOutYSize = 0;
    1675             : 
    1676      354784 :     if (!m_osResampling.empty())
    1677             :     {
    1678      203260 :         psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    1679             :     }
    1680      151524 :     else if (psExtraArgIn != nullptr)
    1681             :     {
    1682      151524 :         psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
    1683             :     }
    1684             : 
    1685      354784 :     bool bError = false;
    1686      354784 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    1687             :                          psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    1688             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    1689             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    1690             :                          &nOutXSize, &nOutYSize, bError))
    1691             :     {
    1692        9704 :         return bError ? CE_Failure : CE_None;
    1693             :     }
    1694             : #if DEBUG_VERBOSE
    1695             :     CPLDebug("VRT",
    1696             :              "nXOff=%d, nYOff=%d, nXSize=%d, nYSize=%d, nBufXSize=%d, "
    1697             :              "nBufYSize=%d,\n"
    1698             :              "dfReqXOff=%g, dfReqYOff=%g, dfReqXSize=%g, dfReqYSize=%g,\n"
    1699             :              "nReqXOff=%d, nReqYOff=%d, nReqXSize=%d, nReqYSize=%d,\n"
    1700             :              "nOutXOff=%d, nOutYOff=%d, nOutXSize=%d, nOutYSize=%d",
    1701             :              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, dfReqXOff,
    1702             :              dfReqYOff, dfReqXSize, dfReqYSize, nReqXOff, nReqYOff, nReqXSize,
    1703             :              nReqYSize, nOutXOff, nOutYOff, nOutXSize, nOutYSize);
    1704             : #endif
    1705             : 
    1706             :     /* -------------------------------------------------------------------- */
    1707             :     /*      Actually perform the IO request.                                */
    1708             :     /* -------------------------------------------------------------------- */
    1709      345080 :     psExtraArg->bFloatingPointWindowValidity = TRUE;
    1710      345080 :     psExtraArg->dfXOff = dfReqXOff;
    1711      345080 :     psExtraArg->dfYOff = dfReqYOff;
    1712      345080 :     psExtraArg->dfXSize = dfReqXSize;
    1713      345080 :     psExtraArg->dfYSize = dfReqYSize;
    1714      345080 :     if (psExtraArgIn)
    1715             :     {
    1716      345080 :         psExtraArg->pfnProgress = psExtraArgIn->pfnProgress;
    1717      345080 :         psExtraArg->pProgressData = psExtraArgIn->pProgressData;
    1718      345080 :         if (psExtraArgIn->nVersion >= 2)
    1719             :         {
    1720      345080 :             psExtraArg->bUseOnlyThisScale = psExtraArgIn->bUseOnlyThisScale;
    1721             :         }
    1722      345080 :         psExtraArg->bOperateInBufType =
    1723      345080 :             GDAL_GET_OPERATE_IN_BUF_TYPE(*psExtraArgIn);
    1724             :     }
    1725             : 
    1726      345080 :     GByte *pabyOut = static_cast<unsigned char *>(pData) +
    1727      345080 :                      nOutXOff * nPixelSpace +
    1728      345080 :                      static_cast<GPtrDiff_t>(nOutYOff) * nLineSpace;
    1729             : 
    1730      345080 :     auto l_band = GetRasterBand();
    1731      345080 :     if (!l_band)
    1732           0 :         return CE_Failure;
    1733             : 
    1734      345080 :     CPLErr eErr = CE_Failure;
    1735      345080 :     if (GDALDataTypeIsConversionLossy(l_band->GetRasterDataType(),
    1736      345080 :                                       eVRTBandDataType))
    1737             :     {
    1738        1230 :         if (!psExtraArg->bOperateInBufType)
    1739             :         {
    1740           1 :             const int nBandDTSize = GDALGetDataTypeSizeBytes(eVRTBandDataType);
    1741             :             void *pTemp =
    1742           1 :                 VSI_MALLOC3_VERBOSE(nOutXSize, nOutYSize, nBandDTSize);
    1743           1 :             if (pTemp)
    1744             :             {
    1745           1 :                 eErr = l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
    1746             :                                         nReqYSize, pTemp, nOutXSize, nOutYSize,
    1747             :                                         eVRTBandDataType, 0, 0, psExtraArg);
    1748           1 :                 if (eErr == CE_None)
    1749             :                 {
    1750           1 :                     GByte *pabyTemp = static_cast<GByte *>(pTemp);
    1751           3 :                     for (int iY = 0; iY < nOutYSize; iY++)
    1752             :                     {
    1753           2 :                         GDALCopyWords(
    1754           2 :                             pabyTemp + static_cast<size_t>(iY) * nBandDTSize *
    1755           2 :                                            nOutXSize,
    1756             :                             eVRTBandDataType, nBandDTSize,
    1757           2 :                             pabyOut + static_cast<GPtrDiff_t>(iY * nLineSpace),
    1758             :                             eBufType, static_cast<int>(nPixelSpace), nOutXSize);
    1759             :                     }
    1760             :                 }
    1761           1 :                 VSIFree(pTemp);
    1762             :             }
    1763             :         }
    1764             :         else
    1765             :         {
    1766             :             eErr =
    1767        1229 :                 l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
    1768             :                                  nReqYSize, pabyOut, nOutXSize, nOutYSize,
    1769             :                                  eBufType, nPixelSpace, nLineSpace, psExtraArg);
    1770        1229 :             if (eErr == CE_None)
    1771             :             {
    1772        4383 :                 for (int j = 0; j < nOutYSize; j++)
    1773             :                 {
    1774      189834 :                     for (int i = 0; i < nOutXSize; i++)
    1775             :                     {
    1776      186680 :                         void *pDst = pabyOut + j * nLineSpace + i * nPixelSpace;
    1777      186680 :                         GDALClampValueToType(pDst, eBufType, eVRTBandDataType);
    1778             :                     }
    1779             :                 }
    1780             :             }
    1781             :         }
    1782             :     }
    1783             :     else
    1784             :     {
    1785      343850 :         eErr = l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
    1786             :                                 nReqYSize, pabyOut, nOutXSize, nOutYSize,
    1787             :                                 eBufType, nPixelSpace, nLineSpace, psExtraArg);
    1788             :     }
    1789             : 
    1790      345080 :     if (NeedMaxValAdjustment())
    1791             :     {
    1792         234 :         for (int j = 0; j < nOutYSize; j++)
    1793             :         {
    1794        4620 :             for (int i = 0; i < nOutXSize; i++)
    1795             :             {
    1796        4400 :                 GByte *pDst = pabyOut + j * nLineSpace + i * nPixelSpace;
    1797        4400 :                 int nVal = 0;
    1798        4400 :                 CopyWordIn(pDst, eBufType, &nVal, GDT_Int32);
    1799        4400 :                 if (nVal > m_nMaxValue)
    1800         800 :                     nVal = m_nMaxValue;
    1801        4400 :                 CopyWordOut(&nVal, GDT_Int32, pDst, eBufType);
    1802             :             }
    1803             :         }
    1804             :     }
    1805             : 
    1806      345080 :     if (psExtraArg->pfnProgress)
    1807         518 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    1808             : 
    1809      345080 :     return eErr;
    1810             : }
    1811             : 
    1812             : /************************************************************************/
    1813             : /*                             GetMinimum()                             */
    1814             : /************************************************************************/
    1815             : 
    1816          52 : double VRTSimpleSource::GetMinimum(int nXSize, int nYSize, int *pbSuccess)
    1817             : {
    1818             :     // The window we will actually request from the source raster band.
    1819          52 :     double dfReqXOff = 0.0;
    1820          52 :     double dfReqYOff = 0.0;
    1821          52 :     double dfReqXSize = 0.0;
    1822          52 :     double dfReqYSize = 0.0;
    1823          52 :     int nReqXOff = 0;
    1824          52 :     int nReqYOff = 0;
    1825          52 :     int nReqXSize = 0;
    1826          52 :     int nReqYSize = 0;
    1827             : 
    1828             :     // The window we will actual set _within_ the pData buffer.
    1829          52 :     int nOutXOff = 0;
    1830          52 :     int nOutYOff = 0;
    1831          52 :     int nOutXSize = 0;
    1832          52 :     int nOutYSize = 0;
    1833             : 
    1834          52 :     bool bError = false;
    1835          52 :     auto l_band = GetRasterBand();
    1836         104 :     if (!l_band ||
    1837          52 :         !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize,
    1838             :                          GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff,
    1839             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    1840             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    1841          52 :                          &nOutXSize, &nOutYSize, bError) ||
    1842         156 :         nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
    1843          52 :         nReqYSize != l_band->GetYSize())
    1844             :     {
    1845           0 :         *pbSuccess = FALSE;
    1846           0 :         return 0;
    1847             :     }
    1848             : 
    1849          52 :     const double dfVal = l_band->GetMinimum(pbSuccess);
    1850          52 :     if (NeedMaxValAdjustment() && dfVal > m_nMaxValue)
    1851           2 :         return m_nMaxValue;
    1852          50 :     return dfVal;
    1853             : }
    1854             : 
    1855             : /************************************************************************/
    1856             : /*                             GetMaximum()                             */
    1857             : /************************************************************************/
    1858             : 
    1859          51 : double VRTSimpleSource::GetMaximum(int nXSize, int nYSize, int *pbSuccess)
    1860             : {
    1861             :     // The window we will actually request from the source raster band.
    1862          51 :     double dfReqXOff = 0.0;
    1863          51 :     double dfReqYOff = 0.0;
    1864          51 :     double dfReqXSize = 0.0;
    1865          51 :     double dfReqYSize = 0.0;
    1866          51 :     int nReqXOff = 0;
    1867          51 :     int nReqYOff = 0;
    1868          51 :     int nReqXSize = 0;
    1869          51 :     int nReqYSize = 0;
    1870             : 
    1871             :     // The window we will actual set _within_ the pData buffer.
    1872          51 :     int nOutXOff = 0;
    1873          51 :     int nOutYOff = 0;
    1874          51 :     int nOutXSize = 0;
    1875          51 :     int nOutYSize = 0;
    1876             : 
    1877          51 :     bool bError = false;
    1878          51 :     auto l_band = GetRasterBand();
    1879         102 :     if (!l_band ||
    1880          51 :         !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize,
    1881             :                          GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff,
    1882             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    1883             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    1884          51 :                          &nOutXSize, &nOutYSize, bError) ||
    1885         153 :         nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
    1886          51 :         nReqYSize != l_band->GetYSize())
    1887             :     {
    1888           0 :         *pbSuccess = FALSE;
    1889           0 :         return 0;
    1890             :     }
    1891             : 
    1892          51 :     const double dfVal = l_band->GetMaximum(pbSuccess);
    1893          51 :     if (NeedMaxValAdjustment() && dfVal > m_nMaxValue)
    1894           2 :         return m_nMaxValue;
    1895          49 :     return dfVal;
    1896             : }
    1897             : 
    1898             : /************************************************************************/
    1899             : /*                            GetHistogram()                            */
    1900             : /************************************************************************/
    1901             : 
    1902           4 : CPLErr VRTSimpleSource::GetHistogram(int nXSize, int nYSize, double dfMin,
    1903             :                                      double dfMax, int nBuckets,
    1904             :                                      GUIntBig *panHistogram,
    1905             :                                      int bIncludeOutOfRange, int bApproxOK,
    1906             :                                      GDALProgressFunc pfnProgress,
    1907             :                                      void *pProgressData)
    1908             : {
    1909             :     // The window we will actually request from the source raster band.
    1910           4 :     double dfReqXOff = 0.0;
    1911           4 :     double dfReqYOff = 0.0;
    1912           4 :     double dfReqXSize = 0.0;
    1913           4 :     double dfReqYSize = 0.0;
    1914           4 :     int nReqXOff = 0;
    1915           4 :     int nReqYOff = 0;
    1916           4 :     int nReqXSize = 0;
    1917           4 :     int nReqYSize = 0;
    1918             : 
    1919             :     // The window we will actual set _within_ the pData buffer.
    1920           4 :     int nOutXOff = 0;
    1921           4 :     int nOutYOff = 0;
    1922           4 :     int nOutXSize = 0;
    1923           4 :     int nOutYSize = 0;
    1924             : 
    1925           4 :     bool bError = false;
    1926           4 :     auto l_band = GetRasterBand();
    1927           4 :     if (!l_band || NeedMaxValAdjustment() ||
    1928           4 :         !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize,
    1929             :                          GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff,
    1930             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    1931             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    1932           4 :                          &nOutXSize, &nOutYSize, bError) ||
    1933          12 :         nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
    1934           4 :         nReqYSize != l_band->GetYSize())
    1935             :     {
    1936           0 :         return CE_Failure;
    1937             :     }
    1938             : 
    1939           4 :     return l_band->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
    1940             :                                 bIncludeOutOfRange, bApproxOK, pfnProgress,
    1941           4 :                                 pProgressData);
    1942             : }
    1943             : 
    1944             : /************************************************************************/
    1945             : /*                          DatasetRasterIO()                           */
    1946             : /************************************************************************/
    1947             : 
    1948       10554 : CPLErr VRTSimpleSource::DatasetRasterIO(
    1949             :     GDALDataType eVRTBandDataType, int nXOff, int nYOff, int nXSize, int nYSize,
    1950             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1951             :     int nBandCount, const int *panBandMap, GSpacing nPixelSpace,
    1952             :     GSpacing nLineSpace, GSpacing nBandSpace,
    1953             :     GDALRasterIOExtraArg *psExtraArgIn)
    1954             : {
    1955       10554 :     if (GetType() != VRTSimpleSource::GetTypeStatic())
    1956             :     {
    1957           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1958           0 :                  "DatasetRasterIO() not implemented for %s", GetType());
    1959           0 :         return CE_Failure;
    1960             :     }
    1961             : 
    1962             :     GDALRasterIOExtraArg sExtraArg;
    1963       10554 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1964       10554 :     GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
    1965             : 
    1966       10554 :     double dfXOff = nXOff;
    1967       10554 :     double dfYOff = nYOff;
    1968       10554 :     double dfXSize = nXSize;
    1969       10554 :     double dfYSize = nYSize;
    1970       10554 :     if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
    1971             :     {
    1972          11 :         dfXOff = psExtraArgIn->dfXOff;
    1973          11 :         dfYOff = psExtraArgIn->dfYOff;
    1974          11 :         dfXSize = psExtraArgIn->dfXSize;
    1975          11 :         dfYSize = psExtraArgIn->dfYSize;
    1976             :     }
    1977             : 
    1978             :     // The window we will actually request from the source raster band.
    1979       10554 :     double dfReqXOff = 0.0;
    1980       10554 :     double dfReqYOff = 0.0;
    1981       10554 :     double dfReqXSize = 0.0;
    1982       10554 :     double dfReqYSize = 0.0;
    1983       10554 :     int nReqXOff = 0;
    1984       10554 :     int nReqYOff = 0;
    1985       10554 :     int nReqXSize = 0;
    1986       10554 :     int nReqYSize = 0;
    1987             : 
    1988             :     // The window we will actual set _within_ the pData buffer.
    1989       10554 :     int nOutXOff = 0;
    1990       10554 :     int nOutYOff = 0;
    1991       10554 :     int nOutXSize = 0;
    1992       10554 :     int nOutYSize = 0;
    1993             : 
    1994       10554 :     if (!m_osResampling.empty())
    1995             :     {
    1996        6676 :         psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    1997             :     }
    1998        3878 :     else if (psExtraArgIn != nullptr)
    1999             :     {
    2000        3878 :         psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
    2001             :     }
    2002             : 
    2003       10554 :     bool bError = false;
    2004       10554 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    2005             :                          psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    2006             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    2007             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    2008             :                          &nOutXSize, &nOutYSize, bError))
    2009             :     {
    2010        2759 :         return bError ? CE_Failure : CE_None;
    2011             :     }
    2012             : 
    2013        7795 :     auto l_band = GetRasterBand();
    2014        7795 :     if (!l_band)
    2015           0 :         return CE_Failure;
    2016             : 
    2017        7795 :     GDALDataset *poDS = l_band->GetDataset();
    2018        7795 :     if (poDS == nullptr)
    2019           0 :         return CE_Failure;
    2020             : 
    2021        7795 :     psExtraArg->bFloatingPointWindowValidity = TRUE;
    2022        7795 :     psExtraArg->dfXOff = dfReqXOff;
    2023        7795 :     psExtraArg->dfYOff = dfReqYOff;
    2024        7795 :     psExtraArg->dfXSize = dfReqXSize;
    2025        7795 :     psExtraArg->dfYSize = dfReqYSize;
    2026        7795 :     if (psExtraArgIn)
    2027             :     {
    2028        7795 :         psExtraArg->pfnProgress = psExtraArgIn->pfnProgress;
    2029        7795 :         psExtraArg->pProgressData = psExtraArgIn->pProgressData;
    2030        7795 :         if (psExtraArgIn->nVersion >= 2)
    2031             :         {
    2032        7795 :             psExtraArg->bUseOnlyThisScale = psExtraArgIn->bUseOnlyThisScale;
    2033             :         }
    2034        7795 :         psExtraArg->bOperateInBufType =
    2035        7795 :             GDAL_GET_OPERATE_IN_BUF_TYPE(*psExtraArgIn);
    2036             :     }
    2037             : 
    2038        7795 :     GByte *pabyOut = static_cast<unsigned char *>(pData) +
    2039        7795 :                      nOutXOff * nPixelSpace +
    2040        7795 :                      static_cast<GPtrDiff_t>(nOutYOff) * nLineSpace;
    2041             : 
    2042        7795 :     CPLErr eErr = CE_Failure;
    2043             : 
    2044        7795 :     if (GDALDataTypeIsConversionLossy(l_band->GetRasterDataType(),
    2045        7795 :                                       eVRTBandDataType))
    2046             :     {
    2047           4 :         if (!psExtraArg->bOperateInBufType)
    2048             :         {
    2049           1 :             const int nBandDTSize = GDALGetDataTypeSizeBytes(eVRTBandDataType);
    2050           1 :             void *pTemp = VSI_MALLOC3_VERBOSE(
    2051             :                 nOutXSize, nOutYSize,
    2052             :                 cpl::fits_on<int>(nBandDTSize * nBandCount));
    2053           1 :             if (pTemp)
    2054             :             {
    2055           1 :                 eErr = poDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
    2056             :                                       nReqYSize, pTemp, nOutXSize, nOutYSize,
    2057             :                                       eVRTBandDataType, nBandCount, panBandMap,
    2058             :                                       0, 0, 0, psExtraArg);
    2059           1 :                 if (eErr == CE_None)
    2060             :                 {
    2061           1 :                     GByte *pabyTemp = static_cast<GByte *>(pTemp);
    2062           1 :                     const size_t nSrcBandSpace =
    2063           1 :                         static_cast<size_t>(nOutYSize) * nOutXSize *
    2064           1 :                         nBandDTSize;
    2065           3 :                     for (int iBand = 0; iBand < nBandCount; iBand++)
    2066             :                     {
    2067           6 :                         for (int iY = 0; iY < nOutYSize; iY++)
    2068             :                         {
    2069           4 :                             GDALCopyWords(pabyTemp + iBand * nSrcBandSpace +
    2070           4 :                                               static_cast<size_t>(iY) *
    2071           4 :                                                   nBandDTSize * nOutXSize,
    2072             :                                           eVRTBandDataType, nBandDTSize,
    2073           4 :                                           pabyOut + static_cast<GPtrDiff_t>(
    2074           4 :                                                         iY * nLineSpace +
    2075           4 :                                                         iBand * nBandSpace),
    2076             :                                           eBufType,
    2077             :                                           static_cast<int>(nPixelSpace),
    2078             :                                           nOutXSize);
    2079             :                         }
    2080             :                     }
    2081             :                 }
    2082           1 :                 VSIFree(pTemp);
    2083             :             }
    2084             :         }
    2085             :         else
    2086             :         {
    2087           3 :             eErr = poDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
    2088             :                                   nReqYSize, pabyOut, nOutXSize, nOutYSize,
    2089             :                                   eBufType, nBandCount, panBandMap, nPixelSpace,
    2090             :                                   nLineSpace, nBandSpace, psExtraArg);
    2091           3 :             if (eErr == CE_None)
    2092             :             {
    2093           9 :                 for (int k = 0; k < nBandCount; k++)
    2094             :                 {
    2095         180 :                     for (int j = 0; j < nOutYSize; j++)
    2096             :                     {
    2097        8082 :                         for (int i = 0; i < nOutXSize; i++)
    2098             :                         {
    2099        7908 :                             void *pDst = pabyOut + k * nBandSpace +
    2100        7908 :                                          j * nLineSpace + i * nPixelSpace;
    2101        7908 :                             GDALClampValueToType(pDst, eBufType,
    2102             :                                                  eVRTBandDataType);
    2103             :                         }
    2104             :                     }
    2105             :                 }
    2106             :             }
    2107             :         }
    2108             :     }
    2109             :     else
    2110             :     {
    2111        7791 :         eErr = poDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    2112             :                               pabyOut, nOutXSize, nOutYSize, eBufType,
    2113             :                               nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2114             :                               nBandSpace, psExtraArg);
    2115             :     }
    2116             : 
    2117        7795 :     if (NeedMaxValAdjustment())
    2118             :     {
    2119           0 :         for (int k = 0; k < nBandCount; k++)
    2120             :         {
    2121           0 :             for (int j = 0; j < nOutYSize; j++)
    2122             :             {
    2123           0 :                 for (int i = 0; i < nOutXSize; i++)
    2124             :                 {
    2125           0 :                     void *pDst = pabyOut + k * nBandSpace + j * nLineSpace +
    2126           0 :                                  i * nPixelSpace;
    2127           0 :                     int nVal = 0;
    2128           0 :                     CopyWordIn(pDst, eBufType, &nVal, GDT_Int32);
    2129             : 
    2130           0 :                     if (nVal > m_nMaxValue)
    2131           0 :                         nVal = m_nMaxValue;
    2132             : 
    2133           0 :                     CopyWordOut(&nVal, GDT_Int32, pDst, eBufType);
    2134             :                 }
    2135             :             }
    2136             :         }
    2137             :     }
    2138             : 
    2139        7795 :     if (psExtraArg->pfnProgress)
    2140        7306 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    2141             : 
    2142        7795 :     return eErr;
    2143             : }
    2144             : 
    2145             : /************************************************************************/
    2146             : /*                           SetResampling()                            */
    2147             : /************************************************************************/
    2148             : 
    2149       70502 : void VRTSimpleSource::SetResampling(const char *pszResampling)
    2150             : {
    2151       70502 :     m_osResampling = (pszResampling) ? pszResampling : "";
    2152       70502 : }
    2153             : 
    2154             : /************************************************************************/
    2155             : /* ==================================================================== */
    2156             : /*                         VRTAveragedSource                            */
    2157             : /* ==================================================================== */
    2158             : /************************************************************************/
    2159             : 
    2160             : /************************************************************************/
    2161             : /*                         VRTAveragedSource()                          */
    2162             : /************************************************************************/
    2163             : 
    2164          16 : VRTAveragedSource::VRTAveragedSource()
    2165             : {
    2166          16 : }
    2167             : 
    2168             : /************************************************************************/
    2169             : /*                           GetTypeStatic()                            */
    2170             : /************************************************************************/
    2171             : 
    2172      104139 : const char *VRTAveragedSource::GetTypeStatic()
    2173             : {
    2174             :     static const char *TYPE = "AveragedSource";
    2175      104139 :     return TYPE;
    2176             : }
    2177             : 
    2178             : /************************************************************************/
    2179             : /*                              GetType()                               */
    2180             : /************************************************************************/
    2181             : 
    2182          11 : const char *VRTAveragedSource::GetType() const
    2183             : {
    2184          11 :     return GetTypeStatic();
    2185             : }
    2186             : 
    2187             : /************************************************************************/
    2188             : /*                           SerializeToXML()                           */
    2189             : /************************************************************************/
    2190             : 
    2191           0 : CPLXMLNode *VRTAveragedSource::SerializeToXML(const char *pszVRTPath)
    2192             : 
    2193             : {
    2194           0 :     CPLXMLNode *const psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
    2195             : 
    2196           0 :     if (psSrc == nullptr)
    2197           0 :         return nullptr;
    2198             : 
    2199           0 :     CPLFree(psSrc->pszValue);
    2200           0 :     psSrc->pszValue = CPLStrdup(GetTypeStatic());
    2201             : 
    2202           0 :     return psSrc;
    2203             : }
    2204             : 
    2205             : /************************************************************************/
    2206             : /*                           SetNoDataValue()                           */
    2207             : /************************************************************************/
    2208             : 
    2209           0 : void VRTAveragedSource::SetNoDataValue(double dfNewNoDataValue)
    2210             : 
    2211             : {
    2212           0 :     if (dfNewNoDataValue == VRT_NODATA_UNSET)
    2213             :     {
    2214           0 :         m_bNoDataSet = FALSE;
    2215           0 :         m_dfNoDataValue = VRT_NODATA_UNSET;
    2216           0 :         return;
    2217             :     }
    2218             : 
    2219           0 :     m_bNoDataSet = TRUE;
    2220           0 :     m_dfNoDataValue = dfNewNoDataValue;
    2221             : }
    2222             : 
    2223             : /************************************************************************/
    2224             : /*                              RasterIO()                              */
    2225             : /************************************************************************/
    2226             : 
    2227          33 : CPLErr VRTAveragedSource::RasterIO(GDALDataType /*eVRTBandDataType*/, int nXOff,
    2228             :                                    int nYOff, int nXSize, int nYSize,
    2229             :                                    void *pData, int nBufXSize, int nBufYSize,
    2230             :                                    GDALDataType eBufType, GSpacing nPixelSpace,
    2231             :                                    GSpacing nLineSpace,
    2232             :                                    GDALRasterIOExtraArg *psExtraArgIn,
    2233             :                                    WorkingState & /*oWorkingState*/)
    2234             : 
    2235             : {
    2236             :     GDALRasterIOExtraArg sExtraArg;
    2237          33 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    2238          33 :     GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
    2239             : 
    2240          33 :     double dfXOff = nXOff;
    2241          33 :     double dfYOff = nYOff;
    2242          33 :     double dfXSize = nXSize;
    2243          33 :     double dfYSize = nYSize;
    2244          33 :     if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
    2245             :     {
    2246           0 :         dfXOff = psExtraArgIn->dfXOff;
    2247           0 :         dfYOff = psExtraArgIn->dfYOff;
    2248           0 :         dfXSize = psExtraArgIn->dfXSize;
    2249           0 :         dfYSize = psExtraArgIn->dfYSize;
    2250             :     }
    2251             : 
    2252             :     // The window we will actually request from the source raster band.
    2253          33 :     double dfReqXOff = 0.0;
    2254          33 :     double dfReqYOff = 0.0;
    2255          33 :     double dfReqXSize = 0.0;
    2256          33 :     double dfReqYSize = 0.0;
    2257          33 :     int nReqXOff = 0;
    2258          33 :     int nReqYOff = 0;
    2259          33 :     int nReqXSize = 0;
    2260          33 :     int nReqYSize = 0;
    2261             : 
    2262             :     // The window we will actual set _within_ the pData buffer.
    2263          33 :     int nOutXOff = 0;
    2264          33 :     int nOutYOff = 0;
    2265          33 :     int nOutXSize = 0;
    2266          33 :     int nOutYSize = 0;
    2267             : 
    2268          33 :     if (!m_osResampling.empty())
    2269             :     {
    2270          28 :         psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    2271             :     }
    2272           5 :     else if (psExtraArgIn != nullptr)
    2273             :     {
    2274           5 :         psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
    2275             :     }
    2276             : 
    2277          33 :     bool bError = false;
    2278          33 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    2279             :                          psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    2280             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    2281             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    2282             :                          &nOutXSize, &nOutYSize, bError))
    2283             :     {
    2284           0 :         return bError ? CE_Failure : CE_None;
    2285             :     }
    2286             : 
    2287          33 :     auto l_band = GetRasterBand();
    2288          33 :     if (!l_band)
    2289           0 :         return CE_Failure;
    2290             : 
    2291             :     /* -------------------------------------------------------------------- */
    2292             :     /*      Allocate a temporary buffer to whole the full resolution        */
    2293             :     /*      data from the area of interest.                                 */
    2294             :     /* -------------------------------------------------------------------- */
    2295             :     float *const pafSrc = static_cast<float *>(
    2296          33 :         VSI_MALLOC3_VERBOSE(sizeof(float), nReqXSize, nReqYSize));
    2297          33 :     if (pafSrc == nullptr)
    2298             :     {
    2299           0 :         return CE_Failure;
    2300             :     }
    2301             : 
    2302             :     /* -------------------------------------------------------------------- */
    2303             :     /*      Load it.                                                        */
    2304             :     /* -------------------------------------------------------------------- */
    2305          33 :     psExtraArg->bFloatingPointWindowValidity = TRUE;
    2306          33 :     psExtraArg->dfXOff = dfReqXOff;
    2307          33 :     psExtraArg->dfYOff = dfReqYOff;
    2308          33 :     psExtraArg->dfXSize = dfReqXSize;
    2309          33 :     psExtraArg->dfYSize = dfReqYSize;
    2310          33 :     if (psExtraArgIn)
    2311             :     {
    2312          33 :         psExtraArg->pfnProgress = psExtraArgIn->pfnProgress;
    2313          33 :         psExtraArg->pProgressData = psExtraArgIn->pProgressData;
    2314          33 :         if (psExtraArgIn->nVersion >= 2)
    2315             :         {
    2316          33 :             psExtraArg->bUseOnlyThisScale = psExtraArgIn->bUseOnlyThisScale;
    2317             :         }
    2318             :     }
    2319             : 
    2320          33 :     const CPLErr eErr = l_band->RasterIO(
    2321             :         GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pafSrc, nReqXSize,
    2322             :         nReqYSize, GDT_Float32, 0, 0, psExtraArg);
    2323             : 
    2324          33 :     if (eErr != CE_None)
    2325             :     {
    2326           0 :         VSIFree(pafSrc);
    2327           0 :         return eErr;
    2328             :     }
    2329             : 
    2330             :     /* -------------------------------------------------------------------- */
    2331             :     /*      Do the averaging.                                               */
    2332             :     /* -------------------------------------------------------------------- */
    2333        5956 :     for (int iBufLine = nOutYOff; iBufLine < nOutYOff + nOutYSize; iBufLine++)
    2334             :     {
    2335        5923 :         const double dfYDst =
    2336        5923 :             (iBufLine / static_cast<double>(nBufYSize)) * nYSize + nYOff;
    2337             : 
    2338     2017080 :         for (int iBufPixel = nOutXOff; iBufPixel < nOutXOff + nOutXSize;
    2339             :              iBufPixel++)
    2340             :         {
    2341             :             double dfXSrcStart, dfXSrcEnd, dfYSrcStart, dfYSrcEnd;
    2342             :             int iXSrcStart, iYSrcStart, iXSrcEnd, iYSrcEnd;
    2343             : 
    2344     2011160 :             const double dfXDst =
    2345     2011160 :                 (iBufPixel / static_cast<double>(nBufXSize)) * nXSize + nXOff;
    2346             : 
    2347             :             // Compute the source image rectangle needed for this pixel.
    2348     2011160 :             DstToSrc(dfXDst, dfYDst, dfXSrcStart, dfYSrcStart);
    2349     2011160 :             DstToSrc(dfXDst + 1.0, dfYDst + 1.0, dfXSrcEnd, dfYSrcEnd);
    2350             : 
    2351             :             // Convert to integers, assuming that the center of the source
    2352             :             // pixel must be in our rect to get included.
    2353     2011160 :             if (dfXSrcEnd >= dfXSrcStart + 1)
    2354             :             {
    2355     1049560 :                 iXSrcStart = static_cast<int>(floor(dfXSrcStart + 0.5));
    2356     1049560 :                 iXSrcEnd = static_cast<int>(floor(dfXSrcEnd + 0.5));
    2357             :             }
    2358             :             else
    2359             :             {
    2360             :                 /* If the resampling factor is less than 100%, the distance */
    2361             :                 /* between the source pixel is < 1, so we stick to nearest */
    2362             :                 /* neighbour */
    2363      961600 :                 iXSrcStart = static_cast<int>(floor(dfXSrcStart));
    2364      961600 :                 iXSrcEnd = iXSrcStart + 1;
    2365             :             }
    2366     2011160 :             if (dfYSrcEnd >= dfYSrcStart + 1)
    2367             :             {
    2368     1049560 :                 iYSrcStart = static_cast<int>(floor(dfYSrcStart + 0.5));
    2369     1049560 :                 iYSrcEnd = static_cast<int>(floor(dfYSrcEnd + 0.5));
    2370             :             }
    2371             :             else
    2372             :             {
    2373      961600 :                 iYSrcStart = static_cast<int>(floor(dfYSrcStart));
    2374      961600 :                 iYSrcEnd = iYSrcStart + 1;
    2375             :             }
    2376             : 
    2377             :             // Transform into the coordinate system of the source *buffer*
    2378     2011160 :             iXSrcStart -= nReqXOff;
    2379     2011160 :             iYSrcStart -= nReqYOff;
    2380     2011160 :             iXSrcEnd -= nReqXOff;
    2381     2011160 :             iYSrcEnd -= nReqYOff;
    2382             : 
    2383     2011160 :             double dfSum = 0.0;
    2384     2011160 :             int nPixelCount = 0;
    2385             : 
    2386     4022510 :             for (int iY = iYSrcStart; iY < iYSrcEnd; iY++)
    2387             :             {
    2388     2011360 :                 if (iY < 0 || iY >= nReqYSize)
    2389           0 :                     continue;
    2390             : 
    2391     4023130 :                 for (int iX = iXSrcStart; iX < iXSrcEnd; iX++)
    2392             :                 {
    2393     2011780 :                     if (iX < 0 || iX >= nReqXSize)
    2394           0 :                         continue;
    2395             : 
    2396     2011780 :                     const float fSampledValue =
    2397     2011780 :                         pafSrc[iX + static_cast<size_t>(iY) * nReqXSize];
    2398     2011780 :                     if (std::isnan(fSampledValue))
    2399           0 :                         continue;
    2400             : 
    2401     4023550 :                     if (m_bNoDataSet &&
    2402     2011780 :                         GDALIsValueInRange<float>(m_dfNoDataValue) &&
    2403           0 :                         ARE_REAL_EQUAL(fSampledValue,
    2404           0 :                                        static_cast<float>(m_dfNoDataValue)))
    2405           0 :                         continue;
    2406             : 
    2407     2011780 :                     nPixelCount++;
    2408     2011780 :                     dfSum += pafSrc[iX + static_cast<size_t>(iY) * nReqXSize];
    2409             :                 }
    2410             :             }
    2411             : 
    2412     2011160 :             if (nPixelCount == 0)
    2413           0 :                 continue;
    2414             : 
    2415             :             // Compute output value.
    2416     2011160 :             const float dfOutputValue = static_cast<float>(dfSum / nPixelCount);
    2417             : 
    2418             :             // Put it in the output buffer.
    2419     2011160 :             GByte *pDstLocation =
    2420     2011160 :                 static_cast<GByte *>(pData) + nPixelSpace * iBufPixel +
    2421     2011160 :                 static_cast<GPtrDiff_t>(nLineSpace) * iBufLine;
    2422             : 
    2423     2011160 :             if (eBufType == GDT_UInt8)
    2424     2008660 :                 *pDstLocation = static_cast<GByte>(
    2425     2008660 :                     std::min(255.0, std::max(0.0, dfOutputValue + 0.5)));
    2426             :             else
    2427        2500 :                 GDALCopyWords(&dfOutputValue, GDT_Float32, 4, pDstLocation,
    2428             :                               eBufType, 8, 1);
    2429             :         }
    2430             :     }
    2431             : 
    2432          33 :     VSIFree(pafSrc);
    2433             : 
    2434          33 :     if (psExtraArg->pfnProgress)
    2435           0 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    2436             : 
    2437          33 :     return CE_None;
    2438             : }
    2439             : 
    2440             : /************************************************************************/
    2441             : /*                             GetMinimum()                             */
    2442             : /************************************************************************/
    2443             : 
    2444           0 : double VRTAveragedSource::GetMinimum(int /* nXSize */, int /* nYSize */,
    2445             :                                      int *pbSuccess)
    2446             : {
    2447           0 :     *pbSuccess = FALSE;
    2448           0 :     return 0.0;
    2449             : }
    2450             : 
    2451             : /************************************************************************/
    2452             : /*                             GetMaximum()                             */
    2453             : /************************************************************************/
    2454             : 
    2455           0 : double VRTAveragedSource::GetMaximum(int /* nXSize */, int /* nYSize */,
    2456             :                                      int *pbSuccess)
    2457             : {
    2458           0 :     *pbSuccess = FALSE;
    2459           0 :     return 0.0;
    2460             : }
    2461             : 
    2462             : /************************************************************************/
    2463             : /*                            GetHistogram()                            */
    2464             : /************************************************************************/
    2465             : 
    2466           0 : CPLErr VRTAveragedSource::GetHistogram(
    2467             :     int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
    2468             :     int /* nBuckets */, GUIntBig * /* panHistogram */,
    2469             :     int /* bIncludeOutOfRange */, int /* bApproxOK */,
    2470             :     GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
    2471             : {
    2472           0 :     return CE_Failure;
    2473             : }
    2474             : 
    2475             : /************************************************************************/
    2476             : /* ==================================================================== */
    2477             : /*                     VRTNoDataFromMaskSource                          */
    2478             : /* ==================================================================== */
    2479             : /************************************************************************/
    2480             : 
    2481             : /************************************************************************/
    2482             : /*                      VRTNoDataFromMaskSource()                       */
    2483             : /************************************************************************/
    2484             : 
    2485          23 : VRTNoDataFromMaskSource::VRTNoDataFromMaskSource()
    2486             : {
    2487          23 : }
    2488             : 
    2489             : /************************************************************************/
    2490             : /*                              XMLInit()                               */
    2491             : /************************************************************************/
    2492             : 
    2493             : CPLErr
    2494           8 : VRTNoDataFromMaskSource::XMLInit(const CPLXMLNode *psSrc,
    2495             :                                  const char *pszVRTPath,
    2496             :                                  VRTMapSharedResources &oMapSharedSources)
    2497             : 
    2498             : {
    2499             :     /* -------------------------------------------------------------------- */
    2500             :     /*      Do base initialization.                                         */
    2501             :     /* -------------------------------------------------------------------- */
    2502             :     {
    2503             :         const CPLErr eErr =
    2504           8 :             VRTSimpleSource::XMLInit(psSrc, pszVRTPath, oMapSharedSources);
    2505           8 :         if (eErr != CE_None)
    2506           0 :             return eErr;
    2507             :     }
    2508             : 
    2509           8 :     if (const char *pszNODATA = CPLGetXMLValue(psSrc, "NODATA", nullptr))
    2510             :     {
    2511           8 :         m_bNoDataSet = true;
    2512           8 :         m_dfNoDataValue = CPLAtofM(pszNODATA);
    2513             :     }
    2514             : 
    2515           8 :     m_dfMaskValueThreshold =
    2516           8 :         CPLAtofM(CPLGetXMLValue(psSrc, "MaskValueThreshold", "0"));
    2517             : 
    2518           8 :     if (const char *pszRemappedValue =
    2519           8 :             CPLGetXMLValue(psSrc, "RemappedValue", nullptr))
    2520             :     {
    2521           0 :         m_bHasRemappedValue = true;
    2522           0 :         m_dfRemappedValue = CPLAtofM(pszRemappedValue);
    2523             :     }
    2524             : 
    2525           8 :     return CE_None;
    2526             : }
    2527             : 
    2528             : /************************************************************************/
    2529             : /*                           GetTypeStatic()                            */
    2530             : /************************************************************************/
    2531             : 
    2532          27 : const char *VRTNoDataFromMaskSource::GetTypeStatic()
    2533             : {
    2534             :     static const char *TYPE = "NoDataFromMaskSource";
    2535          27 :     return TYPE;
    2536             : }
    2537             : 
    2538             : /************************************************************************/
    2539             : /*                              GetType()                               */
    2540             : /************************************************************************/
    2541             : 
    2542          11 : const char *VRTNoDataFromMaskSource::GetType() const
    2543             : {
    2544          11 :     return GetTypeStatic();
    2545             : }
    2546             : 
    2547             : /************************************************************************/
    2548             : /*                           SerializeToXML()                           */
    2549             : /************************************************************************/
    2550             : 
    2551           8 : CPLXMLNode *VRTNoDataFromMaskSource::SerializeToXML(const char *pszVRTPath)
    2552             : 
    2553             : {
    2554           8 :     CPLXMLNode *const psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
    2555             : 
    2556           8 :     if (psSrc == nullptr)
    2557           0 :         return nullptr;
    2558             : 
    2559           8 :     CPLFree(psSrc->pszValue);
    2560           8 :     psSrc->pszValue = CPLStrdup(GetTypeStatic());
    2561             : 
    2562           8 :     if (m_bNoDataSet)
    2563             :     {
    2564           8 :         CPLSetXMLValue(psSrc, "MaskValueThreshold",
    2565             :                        CPLSPrintf("%.17g", m_dfMaskValueThreshold));
    2566             : 
    2567           8 :         GDALDataType eBandDT = GDT_Unknown;
    2568           8 :         double dfNoDataValue = m_dfNoDataValue;
    2569           8 :         const auto kMaxFloat = std::numeric_limits<float>::max();
    2570           8 :         if (std::fabs(std::fabs(m_dfNoDataValue) - kMaxFloat) <
    2571             :             1e-10 * kMaxFloat)
    2572             :         {
    2573           0 :             auto l_band = GetRasterBand();
    2574           0 :             if (l_band)
    2575             :             {
    2576           0 :                 eBandDT = l_band->GetRasterDataType();
    2577           0 :                 if (eBandDT == GDT_Float32)
    2578             :                 {
    2579             :                     dfNoDataValue =
    2580           0 :                         GDALAdjustNoDataCloseToFloatMax(m_dfNoDataValue);
    2581             :                 }
    2582             :             }
    2583             :         }
    2584           8 :         CPLSetXMLValue(psSrc, "NODATA",
    2585          16 :                        VRTSerializeNoData(dfNoDataValue, eBandDT, 18).c_str());
    2586             :     }
    2587             : 
    2588           8 :     if (m_bHasRemappedValue)
    2589             :     {
    2590           0 :         CPLSetXMLValue(psSrc, "RemappedValue",
    2591             :                        CPLSPrintf("%.17g", m_dfRemappedValue));
    2592             :     }
    2593             : 
    2594           8 :     return psSrc;
    2595             : }
    2596             : 
    2597             : /************************************************************************/
    2598             : /*                           SetParameters()                            */
    2599             : /************************************************************************/
    2600             : 
    2601          15 : void VRTNoDataFromMaskSource::SetParameters(double dfNoDataValue,
    2602             :                                             double dfMaskValueThreshold)
    2603             : {
    2604          15 :     m_bNoDataSet = true;
    2605          15 :     m_dfNoDataValue = dfNoDataValue;
    2606          15 :     m_dfMaskValueThreshold = dfMaskValueThreshold;
    2607          15 :     if (!m_bHasRemappedValue)
    2608          15 :         m_dfRemappedValue = m_dfNoDataValue;
    2609          15 : }
    2610             : 
    2611             : /************************************************************************/
    2612             : /*                           SetParameters()                            */
    2613             : /************************************************************************/
    2614             : 
    2615           0 : void VRTNoDataFromMaskSource::SetParameters(double dfNoDataValue,
    2616             :                                             double dfMaskValueThreshold,
    2617             :                                             double dfRemappedValue)
    2618             : {
    2619           0 :     SetParameters(dfNoDataValue, dfMaskValueThreshold);
    2620           0 :     m_bHasRemappedValue = true;
    2621           0 :     m_dfRemappedValue = dfRemappedValue;
    2622           0 : }
    2623             : 
    2624             : /************************************************************************/
    2625             : /*                              RasterIO()                              */
    2626             : /************************************************************************/
    2627             : 
    2628          13 : CPLErr VRTNoDataFromMaskSource::RasterIO(
    2629             :     GDALDataType eVRTBandDataType, int nXOff, int nYOff, int nXSize, int nYSize,
    2630             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    2631             :     GSpacing nPixelSpace, GSpacing nLineSpace,
    2632             :     GDALRasterIOExtraArg *psExtraArgIn, WorkingState &oWorkingState)
    2633             : 
    2634             : {
    2635          13 :     if (!m_bNoDataSet)
    2636             :     {
    2637           0 :         return VRTSimpleSource::RasterIO(eVRTBandDataType, nXOff, nYOff, nXSize,
    2638             :                                          nYSize, pData, nBufXSize, nBufYSize,
    2639             :                                          eBufType, nPixelSpace, nLineSpace,
    2640           0 :                                          psExtraArgIn, oWorkingState);
    2641             :     }
    2642             : 
    2643             :     GDALRasterIOExtraArg sExtraArg;
    2644          13 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    2645          13 :     GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
    2646             : 
    2647          13 :     double dfXOff = nXOff;
    2648          13 :     double dfYOff = nYOff;
    2649          13 :     double dfXSize = nXSize;
    2650          13 :     double dfYSize = nYSize;
    2651          13 :     if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
    2652             :     {
    2653           0 :         dfXOff = psExtraArgIn->dfXOff;
    2654           0 :         dfYOff = psExtraArgIn->dfYOff;
    2655           0 :         dfXSize = psExtraArgIn->dfXSize;
    2656           0 :         dfYSize = psExtraArgIn->dfYSize;
    2657             :     }
    2658             : 
    2659             :     // The window we will actually request from the source raster band.
    2660          13 :     double dfReqXOff = 0.0;
    2661          13 :     double dfReqYOff = 0.0;
    2662          13 :     double dfReqXSize = 0.0;
    2663          13 :     double dfReqYSize = 0.0;
    2664          13 :     int nReqXOff = 0;
    2665          13 :     int nReqYOff = 0;
    2666          13 :     int nReqXSize = 0;
    2667          13 :     int nReqYSize = 0;
    2668             : 
    2669             :     // The window we will actual set _within_ the pData buffer.
    2670          13 :     int nOutXOff = 0;
    2671          13 :     int nOutYOff = 0;
    2672          13 :     int nOutXSize = 0;
    2673          13 :     int nOutYSize = 0;
    2674             : 
    2675          13 :     if (!m_osResampling.empty())
    2676             :     {
    2677           0 :         psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    2678             :     }
    2679          13 :     else if (psExtraArgIn != nullptr)
    2680             :     {
    2681          13 :         psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
    2682             :     }
    2683             : 
    2684          13 :     bool bError = false;
    2685          13 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    2686             :                          psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    2687             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    2688             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    2689             :                          &nOutXSize, &nOutYSize, bError))
    2690             :     {
    2691           0 :         return bError ? CE_Failure : CE_None;
    2692             :     }
    2693             : 
    2694          13 :     auto l_band = GetRasterBand();
    2695          13 :     if (!l_band)
    2696           0 :         return CE_Failure;
    2697             : 
    2698             :     /* -------------------------------------------------------------------- */
    2699             :     /*      Allocate temporary buffer(s).                                   */
    2700             :     /* -------------------------------------------------------------------- */
    2701          13 :     const auto eSrcBandDT = l_band->GetRasterDataType();
    2702          13 :     const int nSrcBandDTSize = GDALGetDataTypeSizeBytes(eSrcBandDT);
    2703          13 :     const auto eSrcMaskBandDT = l_band->GetMaskBand()->GetRasterDataType();
    2704          13 :     const int nSrcMaskBandDTSize = GDALGetDataTypeSizeBytes(eSrcMaskBandDT);
    2705          13 :     double dfRemappedValue = m_dfRemappedValue;
    2706          13 :     if (!m_bHasRemappedValue)
    2707             :     {
    2708          19 :         if (eSrcBandDT == GDT_UInt8 &&
    2709           6 :             m_dfNoDataValue >= std::numeric_limits<GByte>::min() &&
    2710          25 :             m_dfNoDataValue <= std::numeric_limits<GByte>::max() &&
    2711           6 :             static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
    2712             :         {
    2713           6 :             if (m_dfNoDataValue == std::numeric_limits<GByte>::max())
    2714           1 :                 dfRemappedValue = m_dfNoDataValue - 1;
    2715             :             else
    2716           5 :                 dfRemappedValue = m_dfNoDataValue + 1;
    2717             :         }
    2718          10 :         else if (eSrcBandDT == GDT_UInt16 &&
    2719           3 :                  m_dfNoDataValue >= std::numeric_limits<uint16_t>::min() &&
    2720          13 :                  m_dfNoDataValue <= std::numeric_limits<uint16_t>::max() &&
    2721           3 :                  static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
    2722             :         {
    2723           3 :             if (m_dfNoDataValue == std::numeric_limits<uint16_t>::max())
    2724           1 :                 dfRemappedValue = m_dfNoDataValue - 1;
    2725             :             else
    2726           2 :                 dfRemappedValue = m_dfNoDataValue + 1;
    2727             :         }
    2728           6 :         else if (eSrcBandDT == GDT_Int16 &&
    2729           2 :                  m_dfNoDataValue >= std::numeric_limits<int16_t>::min() &&
    2730           8 :                  m_dfNoDataValue <= std::numeric_limits<int16_t>::max() &&
    2731           2 :                  static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
    2732             :         {
    2733           2 :             if (m_dfNoDataValue == std::numeric_limits<int16_t>::max())
    2734           1 :                 dfRemappedValue = m_dfNoDataValue - 1;
    2735             :             else
    2736           1 :                 dfRemappedValue = m_dfNoDataValue + 1;
    2737             :         }
    2738             :         else
    2739             :         {
    2740           2 :             constexpr double EPS = 1e-3;
    2741           2 :             if (m_dfNoDataValue == 0)
    2742           1 :                 dfRemappedValue = EPS;
    2743             :             else
    2744           1 :                 dfRemappedValue = m_dfNoDataValue * (1 + EPS);
    2745             :         }
    2746             :     }
    2747          13 :     const bool bByteOptim =
    2748           6 :         (eSrcBandDT == GDT_UInt8 && eBufType == GDT_UInt8 &&
    2749           5 :          eSrcMaskBandDT == GDT_UInt8 && m_dfMaskValueThreshold >= 0 &&
    2750           5 :          m_dfMaskValueThreshold <= 255 &&
    2751           5 :          static_cast<int>(m_dfMaskValueThreshold) == m_dfMaskValueThreshold &&
    2752           4 :          m_dfNoDataValue >= 0 && m_dfNoDataValue <= 255 &&
    2753           4 :          static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue &&
    2754          23 :          dfRemappedValue >= 0 && dfRemappedValue <= 255 &&
    2755           4 :          static_cast<int>(dfRemappedValue) == dfRemappedValue);
    2756             :     GByte *pabyWrkBuffer;
    2757             :     try
    2758             :     {
    2759          13 :         if (bByteOptim && nOutXOff == 0 && nOutYOff == 0 &&
    2760           4 :             nOutXSize == nBufXSize && nOutYSize == nBufYSize &&
    2761           4 :             eSrcBandDT == eBufType && nPixelSpace == nSrcBandDTSize &&
    2762           4 :             nLineSpace == nPixelSpace * nBufXSize)
    2763             :         {
    2764           4 :             pabyWrkBuffer = static_cast<GByte *>(pData);
    2765             :         }
    2766             :         else
    2767             :         {
    2768           9 :             oWorkingState.m_abyWrkBuffer.resize(static_cast<size_t>(nOutXSize) *
    2769           9 :                                                 nOutYSize * nSrcBandDTSize);
    2770             :             pabyWrkBuffer =
    2771           9 :                 reinterpret_cast<GByte *>(oWorkingState.m_abyWrkBuffer.data());
    2772             :         }
    2773          13 :         oWorkingState.m_abyWrkBufferMask.resize(static_cast<size_t>(nOutXSize) *
    2774          13 :                                                 nOutYSize * nSrcMaskBandDTSize);
    2775             :     }
    2776           0 :     catch (const std::exception &)
    2777             :     {
    2778           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2779             :                  "Out of memory when allocating buffers");
    2780           0 :         return CE_Failure;
    2781             :     }
    2782             : 
    2783             :     /* -------------------------------------------------------------------- */
    2784             :     /*      Load data.                                                      */
    2785             :     /* -------------------------------------------------------------------- */
    2786          13 :     psExtraArg->bFloatingPointWindowValidity = TRUE;
    2787          13 :     psExtraArg->dfXOff = dfReqXOff;
    2788          13 :     psExtraArg->dfYOff = dfReqYOff;
    2789          13 :     psExtraArg->dfXSize = dfReqXSize;
    2790          13 :     psExtraArg->dfYSize = dfReqYSize;
    2791          13 :     if (psExtraArgIn)
    2792             :     {
    2793          13 :         psExtraArg->pfnProgress = psExtraArgIn->pfnProgress;
    2794          13 :         psExtraArg->pProgressData = psExtraArgIn->pProgressData;
    2795             :     }
    2796             : 
    2797          13 :     if (l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    2798             :                          pabyWrkBuffer, nOutXSize, nOutYSize, eSrcBandDT, 0, 0,
    2799          13 :                          psExtraArg) != CE_None)
    2800             :     {
    2801           0 :         return CE_Failure;
    2802             :     }
    2803             : 
    2804          26 :     if (l_band->GetMaskBand()->RasterIO(
    2805             :             GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    2806          13 :             oWorkingState.m_abyWrkBufferMask.data(), nOutXSize, nOutYSize,
    2807          13 :             eSrcMaskBandDT, 0, 0, psExtraArg) != CE_None)
    2808             :     {
    2809           0 :         return CE_Failure;
    2810             :     }
    2811             : 
    2812             :     /* -------------------------------------------------------------------- */
    2813             :     /*      Do the processing.                                              */
    2814             :     /* -------------------------------------------------------------------- */
    2815             : 
    2816          13 :     GByte *const pabyOut = static_cast<GByte *>(pData) +
    2817          13 :                            nPixelSpace * nOutXOff +
    2818          13 :                            static_cast<GPtrDiff_t>(nLineSpace) * nOutYOff;
    2819          13 :     if (bByteOptim)
    2820             :     {
    2821             :         // Special case when everything fits on Byte
    2822           4 :         const GByte nMaskValueThreshold =
    2823           4 :             static_cast<GByte>(m_dfMaskValueThreshold);
    2824           4 :         const GByte nNoDataValue = static_cast<GByte>(m_dfNoDataValue);
    2825           4 :         const GByte nRemappedValue = static_cast<GByte>(dfRemappedValue);
    2826           4 :         size_t nSrcIdx = 0;
    2827           8 :         for (int iY = 0; iY < nOutYSize; iY++)
    2828             :         {
    2829           4 :             GSpacing nDstOffset = iY * nLineSpace;
    2830          12 :             for (int iX = 0; iX < nOutXSize; iX++)
    2831             :             {
    2832             :                 const GByte nMaskVal =
    2833           8 :                     oWorkingState.m_abyWrkBufferMask[nSrcIdx];
    2834           8 :                 if (nMaskVal <= nMaskValueThreshold)
    2835             :                 {
    2836           4 :                     pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] = nNoDataValue;
    2837             :                 }
    2838             :                 else
    2839             :                 {
    2840           4 :                     if (pabyWrkBuffer[nSrcIdx] == nNoDataValue)
    2841             :                     {
    2842           2 :                         pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] =
    2843             :                             nRemappedValue;
    2844             :                     }
    2845             :                     else
    2846             :                     {
    2847           2 :                         pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] =
    2848           2 :                             pabyWrkBuffer[nSrcIdx];
    2849             :                     }
    2850             :                 }
    2851           8 :                 nDstOffset += nPixelSpace;
    2852           8 :                 nSrcIdx++;
    2853             :             }
    2854             :         }
    2855             :     }
    2856             :     else
    2857             :     {
    2858           9 :         size_t nSrcIdx = 0;
    2859           9 :         double dfMaskVal = 0;
    2860           9 :         const int nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
    2861          18 :         std::vector<GByte> abyDstNoData(nBufDTSize);
    2862           9 :         GDALCopyWords(&m_dfNoDataValue, GDT_Float64, 0, abyDstNoData.data(),
    2863             :                       eBufType, 0, 1);
    2864          18 :         std::vector<GByte> abyRemappedValue(nBufDTSize);
    2865           9 :         GDALCopyWords(&dfRemappedValue, GDT_Float64, 0, abyRemappedValue.data(),
    2866             :                       eBufType, 0, 1);
    2867          18 :         for (int iY = 0; iY < nOutYSize; iY++)
    2868             :         {
    2869           9 :             GSpacing nDstOffset = iY * nLineSpace;
    2870          28 :             for (int iX = 0; iX < nOutXSize; iX++)
    2871             :             {
    2872          19 :                 if (eSrcMaskBandDT == GDT_UInt8)
    2873             :                 {
    2874          19 :                     dfMaskVal = oWorkingState.m_abyWrkBufferMask[nSrcIdx];
    2875             :                 }
    2876             :                 else
    2877             :                 {
    2878           0 :                     GDALCopyWords(oWorkingState.m_abyWrkBufferMask.data() +
    2879           0 :                                       nSrcIdx * nSrcMaskBandDTSize,
    2880             :                                   eSrcMaskBandDT, 0, &dfMaskVal, GDT_Float64, 0,
    2881             :                                   1);
    2882             :                 }
    2883          19 :                 void *const pDst =
    2884          19 :                     pabyOut + static_cast<GPtrDiff_t>(nDstOffset);
    2885          19 :                 if (!(dfMaskVal > m_dfMaskValueThreshold))
    2886             :                 {
    2887           9 :                     memcpy(pDst, abyDstNoData.data(), nBufDTSize);
    2888             :                 }
    2889             :                 else
    2890             :                 {
    2891          10 :                     const void *const pSrc =
    2892          10 :                         pabyWrkBuffer + nSrcIdx * nSrcBandDTSize;
    2893          10 :                     if (eSrcBandDT == eBufType)
    2894             :                     {
    2895             :                         // coverity[overrun-buffer-arg]
    2896           8 :                         memcpy(pDst, pSrc, nBufDTSize);
    2897             :                     }
    2898             :                     else
    2899             :                     {
    2900           2 :                         GDALCopyWords(pSrc, eSrcBandDT, 0, pDst, eBufType, 0,
    2901             :                                       1);
    2902             :                     }
    2903          10 :                     if (memcmp(pDst, abyDstNoData.data(), nBufDTSize) == 0)
    2904           9 :                         memcpy(pDst, abyRemappedValue.data(), nBufDTSize);
    2905             :                 }
    2906          19 :                 nDstOffset += nPixelSpace;
    2907          19 :                 nSrcIdx++;
    2908             :             }
    2909             :         }
    2910             :     }
    2911             : 
    2912          13 :     if (psExtraArg->pfnProgress)
    2913           0 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    2914             : 
    2915          13 :     return CE_None;
    2916             : }
    2917             : 
    2918             : /************************************************************************/
    2919             : /*                             GetMinimum()                             */
    2920             : /************************************************************************/
    2921             : 
    2922           0 : double VRTNoDataFromMaskSource::GetMinimum(int /* nXSize */, int /* nYSize */,
    2923             :                                            int *pbSuccess)
    2924             : {
    2925           0 :     *pbSuccess = FALSE;
    2926           0 :     return 0.0;
    2927             : }
    2928             : 
    2929             : /************************************************************************/
    2930             : /*                             GetMaximum()                             */
    2931             : /************************************************************************/
    2932             : 
    2933           0 : double VRTNoDataFromMaskSource::GetMaximum(int /* nXSize */, int /* nYSize */,
    2934             :                                            int *pbSuccess)
    2935             : {
    2936           0 :     *pbSuccess = FALSE;
    2937           0 :     return 0.0;
    2938             : }
    2939             : 
    2940             : /************************************************************************/
    2941             : /*                            GetHistogram()                            */
    2942             : /************************************************************************/
    2943             : 
    2944           0 : CPLErr VRTNoDataFromMaskSource::GetHistogram(
    2945             :     int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
    2946             :     int /* nBuckets */, GUIntBig * /* panHistogram */,
    2947             :     int /* bIncludeOutOfRange */, int /* bApproxOK */,
    2948             :     GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
    2949             : {
    2950           0 :     return CE_Failure;
    2951             : }
    2952             : 
    2953             : /************************************************************************/
    2954             : /* ==================================================================== */
    2955             : /*                          VRTComplexSource                            */
    2956             : /* ==================================================================== */
    2957             : /************************************************************************/
    2958             : 
    2959             : /************************************************************************/
    2960             : /*                          VRTComplexSource()                          */
    2961             : /************************************************************************/
    2962             : 
    2963           5 : VRTComplexSource::VRTComplexSource(const VRTComplexSource *poSrcSource,
    2964           5 :                                    double dfXDstRatio, double dfYDstRatio)
    2965             :     : VRTSimpleSource(poSrcSource, dfXDstRatio, dfYDstRatio),
    2966           5 :       m_nProcessingFlags(poSrcSource->m_nProcessingFlags),
    2967           5 :       m_dfNoDataValue(poSrcSource->m_dfNoDataValue),
    2968           5 :       m_osNoDataValueOri(poSrcSource->m_osNoDataValueOri),
    2969           5 :       m_dfScaleOff(poSrcSource->m_dfScaleOff),
    2970           5 :       m_dfScaleRatio(poSrcSource->m_dfScaleRatio),
    2971           5 :       m_bSrcMinMaxDefined(poSrcSource->m_bSrcMinMaxDefined),
    2972           5 :       m_dfSrcMin(poSrcSource->m_dfSrcMin), m_dfSrcMax(poSrcSource->m_dfSrcMax),
    2973           5 :       m_dfDstMin(poSrcSource->m_dfDstMin), m_dfDstMax(poSrcSource->m_dfDstMax),
    2974           5 :       m_dfExponent(poSrcSource->m_dfExponent), m_bClip(poSrcSource->m_bClip),
    2975           5 :       m_nColorTableComponent(poSrcSource->m_nColorTableComponent),
    2976           5 :       m_adfLUTInputs(poSrcSource->m_adfLUTInputs),
    2977           5 :       m_adfLUTOutputs(poSrcSource->m_adfLUTOutputs)
    2978             : {
    2979           5 : }
    2980             : 
    2981             : /************************************************************************/
    2982             : /*                           GetTypeStatic()                            */
    2983             : /************************************************************************/
    2984             : 
    2985       13954 : const char *VRTComplexSource::GetTypeStatic()
    2986             : {
    2987             :     static const char *TYPE = "ComplexSource";
    2988       13954 :     return TYPE;
    2989             : }
    2990             : 
    2991             : /************************************************************************/
    2992             : /*                              GetType()                               */
    2993             : /************************************************************************/
    2994             : 
    2995        4106 : const char *VRTComplexSource::GetType() const
    2996             : {
    2997        4106 :     return GetTypeStatic();
    2998             : }
    2999             : 
    3000             : /************************************************************************/
    3001             : /*                           SetNoDataValue()                           */
    3002             : /************************************************************************/
    3003             : 
    3004         108 : void VRTComplexSource::SetNoDataValue(double dfNewNoDataValue)
    3005             : 
    3006             : {
    3007         108 :     if (dfNewNoDataValue == VRT_NODATA_UNSET)
    3008             :     {
    3009           0 :         m_nProcessingFlags &= ~PROCESSING_FLAG_NODATA;
    3010           0 :         m_dfNoDataValue = VRT_NODATA_UNSET;
    3011           0 :         return;
    3012             :     }
    3013             : 
    3014         108 :     m_nProcessingFlags |= PROCESSING_FLAG_NODATA;
    3015         108 :     m_dfNoDataValue = dfNewNoDataValue;
    3016             : }
    3017             : 
    3018             : /************************************************************************/
    3019             : /*                       GetAdjustedNoDataValue()                       */
    3020             : /************************************************************************/
    3021             : 
    3022       10676 : double VRTComplexSource::GetAdjustedNoDataValue() const
    3023             : {
    3024       10676 :     if ((m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0)
    3025             :     {
    3026        4925 :         auto l_band = GetRasterBand();
    3027        4925 :         if (l_band && l_band->GetRasterDataType() == GDT_Float32)
    3028             :         {
    3029          18 :             return GDALAdjustNoDataCloseToFloatMax(m_dfNoDataValue);
    3030             :         }
    3031             :     }
    3032       10658 :     return m_dfNoDataValue;
    3033             : }
    3034             : 
    3035             : /************************************************************************/
    3036             : /*                           SerializeToXML()                           */
    3037             : /************************************************************************/
    3038             : 
    3039          97 : CPLXMLNode *VRTComplexSource::SerializeToXML(const char *pszVRTPath)
    3040             : 
    3041             : {
    3042          97 :     CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
    3043             : 
    3044          97 :     if (psSrc == nullptr)
    3045           0 :         return nullptr;
    3046             : 
    3047          97 :     CPLFree(psSrc->pszValue);
    3048          97 :     psSrc->pszValue = CPLStrdup(GetTypeStatic());
    3049             : 
    3050          97 :     if ((m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0)
    3051             :     {
    3052          33 :         CPLSetXMLValue(psSrc, "UseMaskBand", "true");
    3053             :     }
    3054             : 
    3055          97 :     if ((m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0)
    3056             :     {
    3057          24 :         if (!m_osNoDataValueOri.empty() && GetRasterBandNoOpen() == nullptr)
    3058             :         {
    3059           1 :             CPLSetXMLValue(psSrc, "NODATA", m_osNoDataValueOri.c_str());
    3060             :         }
    3061             :         else
    3062             :         {
    3063          23 :             GDALDataType eBandDT = GDT_Unknown;
    3064          23 :             double dfNoDataValue = m_dfNoDataValue;
    3065          23 :             const auto kMaxFloat = std::numeric_limits<float>::max();
    3066          23 :             if (std::fabs(std::fabs(m_dfNoDataValue) - kMaxFloat) <
    3067             :                 1e-10 * kMaxFloat)
    3068             :             {
    3069           1 :                 auto l_band = GetRasterBand();
    3070           1 :                 if (l_band)
    3071             :                 {
    3072           1 :                     dfNoDataValue = GetAdjustedNoDataValue();
    3073           1 :                     eBandDT = l_band->GetRasterDataType();
    3074             :                 }
    3075             :             }
    3076          23 :             CPLSetXMLValue(
    3077             :                 psSrc, "NODATA",
    3078          46 :                 VRTSerializeNoData(dfNoDataValue, eBandDT, 18).c_str());
    3079             :         }
    3080             :     }
    3081             : 
    3082          97 :     if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
    3083             :     {
    3084           1 :         CPLSetXMLValue(psSrc, "ScaleOffset", CPLSPrintf("%g", m_dfScaleOff));
    3085           1 :         CPLSetXMLValue(psSrc, "ScaleRatio", CPLSPrintf("%g", m_dfScaleRatio));
    3086             :     }
    3087          96 :     else if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_EXPONENTIAL) != 0)
    3088             :     {
    3089           0 :         CPLSetXMLValue(psSrc, "Exponent", CPLSPrintf("%g", m_dfExponent));
    3090           0 :         if (m_bSrcMinMaxDefined)
    3091             :         {
    3092           0 :             CPLSetXMLValue(psSrc, "SrcMin", CPLSPrintf("%g", m_dfSrcMin));
    3093           0 :             CPLSetXMLValue(psSrc, "SrcMax", CPLSPrintf("%g", m_dfSrcMax));
    3094             :         }
    3095           0 :         CPLSetXMLValue(psSrc, "DstMin", CPLSPrintf("%g", m_dfDstMin));
    3096           0 :         CPLSetXMLValue(psSrc, "DstMax", CPLSPrintf("%g", m_dfDstMax));
    3097           0 :         CPLSetXMLValue(psSrc, "Clip", m_bClip ? "true" : "false");
    3098             :     }
    3099             : 
    3100          97 :     if (!m_adfLUTInputs.empty())
    3101             :     {
    3102             :         // Make sure we print with sufficient precision to address really close
    3103             :         // entries (#6422).
    3104          60 :         CPLString osLUT;
    3105          60 :         if (m_adfLUTInputs.size() >= 2 &&
    3106          60 :             CPLString().Printf("%g", m_adfLUTInputs[0]) ==
    3107          60 :                 CPLString().Printf("%g", m_adfLUTInputs[1]))
    3108             :         {
    3109           0 :             osLUT = CPLString().Printf("%.17g:%g", m_adfLUTInputs[0],
    3110           0 :                                        m_adfLUTOutputs[0]);
    3111             :         }
    3112             :         else
    3113             :         {
    3114          60 :             osLUT = CPLString().Printf("%g:%g", m_adfLUTInputs[0],
    3115          30 :                                        m_adfLUTOutputs[0]);
    3116             :         }
    3117         252 :         for (size_t i = 1; i < m_adfLUTInputs.size(); i++)
    3118             :         {
    3119         444 :             if (CPLString().Printf("%g", m_adfLUTInputs[i]) ==
    3120         927 :                     CPLString().Printf("%g", m_adfLUTInputs[i - 1]) ||
    3121         261 :                 (i + 1 < m_adfLUTInputs.size() &&
    3122         345 :                  CPLString().Printf("%g", m_adfLUTInputs[i]) ==
    3123         345 :                      CPLString().Printf("%g", m_adfLUTInputs[i + 1])))
    3124             :             {
    3125             :                 // TODO(schwehr): An explanation of the 18 would be helpful.
    3126             :                 // Can someone distill the issue down to a quick comment?
    3127             :                 // https://trac.osgeo.org/gdal/ticket/6422
    3128         294 :                 osLUT += CPLString().Printf(",%.17g:%g", m_adfLUTInputs[i],
    3129         147 :                                             m_adfLUTOutputs[i]);
    3130             :             }
    3131             :             else
    3132             :             {
    3133         150 :                 osLUT += CPLString().Printf(",%g:%g", m_adfLUTInputs[i],
    3134          75 :                                             m_adfLUTOutputs[i]);
    3135             :             }
    3136             :         }
    3137          30 :         CPLSetXMLValue(psSrc, "LUT", osLUT);
    3138             :     }
    3139             : 
    3140          97 :     if (m_nColorTableComponent)
    3141             :     {
    3142           7 :         CPLSetXMLValue(psSrc, "ColorTableComponent",
    3143             :                        CPLSPrintf("%d", m_nColorTableComponent));
    3144             :     }
    3145             : 
    3146          97 :     return psSrc;
    3147             : }
    3148             : 
    3149             : /************************************************************************/
    3150             : /*                              XMLInit()                               */
    3151             : /************************************************************************/
    3152             : 
    3153         263 : CPLErr VRTComplexSource::XMLInit(const CPLXMLNode *psSrc,
    3154             :                                  const char *pszVRTPath,
    3155             :                                  VRTMapSharedResources &oMapSharedSources)
    3156             : 
    3157             : {
    3158             :     /* -------------------------------------------------------------------- */
    3159             :     /*      Do base initialization.                                         */
    3160             :     /* -------------------------------------------------------------------- */
    3161             :     {
    3162             :         const CPLErr eErr =
    3163         263 :             VRTSimpleSource::XMLInit(psSrc, pszVRTPath, oMapSharedSources);
    3164         263 :         if (eErr != CE_None)
    3165           0 :             return eErr;
    3166             :     }
    3167             : 
    3168             :     /* -------------------------------------------------------------------- */
    3169             :     /*      Complex parameters.                                             */
    3170             :     /* -------------------------------------------------------------------- */
    3171         263 :     const char *pszScaleOffset = CPLGetXMLValue(psSrc, "ScaleOffset", nullptr);
    3172         263 :     const char *pszScaleRatio = CPLGetXMLValue(psSrc, "ScaleRatio", nullptr);
    3173         263 :     if (pszScaleOffset || pszScaleRatio)
    3174             :     {
    3175          32 :         m_nProcessingFlags |= PROCESSING_FLAG_SCALING_LINEAR;
    3176          32 :         if (pszScaleOffset)
    3177          32 :             m_dfScaleOff = CPLAtof(pszScaleOffset);
    3178          32 :         if (pszScaleRatio)
    3179          29 :             m_dfScaleRatio = CPLAtof(pszScaleRatio);
    3180             :     }
    3181         231 :     else if (CPLGetXMLValue(psSrc, "Exponent", nullptr) != nullptr &&
    3182         232 :              CPLGetXMLValue(psSrc, "DstMin", nullptr) != nullptr &&
    3183           1 :              CPLGetXMLValue(psSrc, "DstMax", nullptr) != nullptr)
    3184             :     {
    3185           1 :         m_nProcessingFlags |= PROCESSING_FLAG_SCALING_EXPONENTIAL;
    3186           1 :         m_dfExponent = CPLAtof(CPLGetXMLValue(psSrc, "Exponent", "1.0"));
    3187             : 
    3188           1 :         const char *pszSrcMin = CPLGetXMLValue(psSrc, "SrcMin", nullptr);
    3189           1 :         const char *pszSrcMax = CPLGetXMLValue(psSrc, "SrcMax", nullptr);
    3190           1 :         if (pszSrcMin && pszSrcMax)
    3191             :         {
    3192           0 :             m_dfSrcMin = CPLAtof(pszSrcMin);
    3193           0 :             m_dfSrcMax = CPLAtof(pszSrcMax);
    3194           0 :             m_bSrcMinMaxDefined = true;
    3195             :         }
    3196             : 
    3197           1 :         m_dfDstMin = CPLAtof(CPLGetXMLValue(psSrc, "DstMin", "0.0"));
    3198           1 :         m_dfDstMax = CPLAtof(CPLGetXMLValue(psSrc, "DstMax", "0.0"));
    3199           1 :         m_bClip = CPLTestBool(CPLGetXMLValue(psSrc, "Clip", "true"));
    3200             :     }
    3201             : 
    3202         263 :     if (const char *pszNODATA = CPLGetXMLValue(psSrc, "NODATA", nullptr))
    3203             :     {
    3204          76 :         m_nProcessingFlags |= PROCESSING_FLAG_NODATA;
    3205          76 :         m_osNoDataValueOri = pszNODATA;
    3206          76 :         m_dfNoDataValue = CPLAtofM(m_osNoDataValueOri.c_str());
    3207             :     }
    3208             : 
    3209         263 :     const char *pszUseMaskBand = CPLGetXMLValue(psSrc, "UseMaskBand", nullptr);
    3210         263 :     if (pszUseMaskBand && CPLTestBool(pszUseMaskBand))
    3211             :     {
    3212          41 :         m_nProcessingFlags |= PROCESSING_FLAG_USE_MASK_BAND;
    3213             :     }
    3214             : 
    3215         263 :     const char *pszLUT = CPLGetXMLValue(psSrc, "LUT", nullptr);
    3216         263 :     if (pszLUT)
    3217             :     {
    3218             :         const CPLStringList aosValues(
    3219          62 :             CSLTokenizeString2(pszLUT, ",:", CSLT_ALLOWEMPTYTOKENS));
    3220             : 
    3221          62 :         const int nLUTItemCount = aosValues.size() / 2;
    3222             :         try
    3223             :         {
    3224          62 :             m_adfLUTInputs.resize(nLUTItemCount);
    3225          62 :             m_adfLUTOutputs.resize(nLUTItemCount);
    3226             :         }
    3227           0 :         catch (const std::bad_alloc &e)
    3228             :         {
    3229           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    3230           0 :             m_adfLUTInputs.clear();
    3231           0 :             m_adfLUTOutputs.clear();
    3232           0 :             return CE_Failure;
    3233             :         }
    3234             : 
    3235         568 :         for (int nIndex = 0; nIndex < nLUTItemCount; nIndex++)
    3236             :         {
    3237         506 :             m_adfLUTInputs[nIndex] = CPLAtof(aosValues[nIndex * 2]);
    3238         506 :             m_adfLUTOutputs[nIndex] = CPLAtof(aosValues[nIndex * 2 + 1]);
    3239             : 
    3240             :             // Enforce the requirement that the LUT input array is
    3241             :             // monotonically non-decreasing.
    3242         506 :             if (std::isnan(m_adfLUTInputs[nIndex]) && nIndex != 0)
    3243             :             {
    3244           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3245             :                          "A Not-A-Number (NaN) source value should be the "
    3246             :                          "first one of the LUT.");
    3247           0 :                 m_adfLUTInputs.clear();
    3248           0 :                 m_adfLUTOutputs.clear();
    3249           0 :                 return CE_Failure;
    3250             :             }
    3251         950 :             else if (nIndex > 0 &&
    3252         444 :                      m_adfLUTInputs[nIndex] < m_adfLUTInputs[nIndex - 1])
    3253             :             {
    3254           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3255             :                          "Source values of the LUT are not listed in a "
    3256             :                          "monotonically non-decreasing order");
    3257           0 :                 m_adfLUTInputs.clear();
    3258           0 :                 m_adfLUTOutputs.clear();
    3259           0 :                 return CE_Failure;
    3260             :             }
    3261             :         }
    3262             : 
    3263          62 :         m_nProcessingFlags |= PROCESSING_FLAG_LUT;
    3264             :     }
    3265             : 
    3266             :     const char *pszColorTableComponent =
    3267         263 :         CPLGetXMLValue(psSrc, "ColorTableComponent", nullptr);
    3268         263 :     if (pszColorTableComponent)
    3269             :     {
    3270          15 :         m_nColorTableComponent = atoi(pszColorTableComponent);
    3271          15 :         m_nProcessingFlags |= PROCESSING_FLAG_COLOR_TABLE_EXPANSION;
    3272             :     }
    3273             : 
    3274         263 :     return CE_None;
    3275             : }
    3276             : 
    3277             : /************************************************************************/
    3278             : /*                               SetLUT()                               */
    3279             : /************************************************************************/
    3280             : 
    3281          52 : void VRTComplexSource::SetLUT(const std::vector<double> &adfLUTInputs,
    3282             :                               const std::vector<double> &adfLUTOutputs)
    3283             : {
    3284          52 :     m_adfLUTInputs = adfLUTInputs;
    3285          52 :     m_adfLUTOutputs = adfLUTOutputs;
    3286          52 :     m_nProcessingFlags |= PROCESSING_FLAG_LUT;
    3287          52 : }
    3288             : 
    3289             : /************************************************************************/
    3290             : /*                            LookupValue()                             */
    3291             : /************************************************************************/
    3292             : 
    3293      994484 : double VRTComplexSource::LookupValue(double dfInput)
    3294             : {
    3295      994484 :     auto beginIter = m_adfLUTInputs.begin();
    3296      994484 :     auto endIter = m_adfLUTInputs.end();
    3297      994484 :     size_t offset = 0;
    3298      994484 :     if (std::isnan(m_adfLUTInputs[0]))
    3299             :     {
    3300           6 :         if (std::isnan(dfInput) || m_adfLUTInputs.size() == 1)
    3301           1 :             return m_adfLUTOutputs[0];
    3302           5 :         ++beginIter;
    3303           5 :         offset = 1;
    3304             :     }
    3305             : 
    3306             :     // Find the index of the first element in the LUT input array that
    3307             :     // is not smaller than the input value.
    3308             :     const size_t i =
    3309             :         offset +
    3310      994483 :         std::distance(beginIter, std::lower_bound(beginIter, endIter, dfInput));
    3311             : 
    3312      994483 :     if (i == offset)
    3313      129039 :         return m_adfLUTOutputs[offset];
    3314             : 
    3315             :     // If the index is beyond the end of the LUT input array, the input
    3316             :     // value is larger than all the values in the array.
    3317      865444 :     if (i == m_adfLUTInputs.size())
    3318           8 :         return m_adfLUTOutputs.back();
    3319             : 
    3320      865436 :     if (m_adfLUTInputs[i] == dfInput)
    3321      296380 :         return m_adfLUTOutputs[i];
    3322             : 
    3323             :     // Otherwise, interpolate.
    3324      569056 :     return m_adfLUTOutputs[i - 1] +
    3325      569056 :            (dfInput - m_adfLUTInputs[i - 1]) *
    3326      569056 :                ((m_adfLUTOutputs[i] - m_adfLUTOutputs[i - 1]) /
    3327      569056 :                 (m_adfLUTInputs[i] - m_adfLUTInputs[i - 1]));
    3328             : }
    3329             : 
    3330             : /************************************************************************/
    3331             : /*                          SetLinearScaling()                          */
    3332             : /************************************************************************/
    3333             : 
    3334         124 : void VRTComplexSource::SetLinearScaling(double dfOffset, double dfScale)
    3335             : {
    3336         124 :     m_nProcessingFlags &= ~PROCESSING_FLAG_SCALING_EXPONENTIAL;
    3337         124 :     m_nProcessingFlags |= PROCESSING_FLAG_SCALING_LINEAR;
    3338         124 :     m_dfScaleOff = dfOffset;
    3339         124 :     m_dfScaleRatio = dfScale;
    3340         124 : }
    3341             : 
    3342             : /************************************************************************/
    3343             : /*                          SetPowerScaling()                           */
    3344             : /************************************************************************/
    3345             : 
    3346          26 : void VRTComplexSource::SetPowerScaling(double dfExponentIn, double dfSrcMinIn,
    3347             :                                        double dfSrcMaxIn, double dfDstMinIn,
    3348             :                                        double dfDstMaxIn, bool bClip)
    3349             : {
    3350          26 :     m_nProcessingFlags &= ~PROCESSING_FLAG_SCALING_LINEAR;
    3351          26 :     m_nProcessingFlags |= PROCESSING_FLAG_SCALING_EXPONENTIAL;
    3352          26 :     m_dfExponent = dfExponentIn;
    3353          26 :     m_dfSrcMin = dfSrcMinIn;
    3354          26 :     m_dfSrcMax = dfSrcMaxIn;
    3355          26 :     m_dfDstMin = dfDstMinIn;
    3356          26 :     m_dfDstMax = dfDstMaxIn;
    3357          26 :     m_bSrcMinMaxDefined = true;
    3358          26 :     m_bClip = bClip;
    3359          26 : }
    3360             : 
    3361             : /************************************************************************/
    3362             : /*                       SetColorTableComponent()                       */
    3363             : /************************************************************************/
    3364             : 
    3365         290 : void VRTComplexSource::SetColorTableComponent(int nComponent)
    3366             : {
    3367         290 :     m_nProcessingFlags |= PROCESSING_FLAG_COLOR_TABLE_EXPANSION;
    3368         290 :     m_nColorTableComponent = nComponent;
    3369         290 : }
    3370             : 
    3371             : /************************************************************************/
    3372             : /*                              RasterIO()                              */
    3373             : /************************************************************************/
    3374             : 
    3375       23755 : CPLErr VRTComplexSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
    3376             :                                   int nYOff, int nXSize, int nYSize,
    3377             :                                   void *pData, int nBufXSize, int nBufYSize,
    3378             :                                   GDALDataType eBufType, GSpacing nPixelSpace,
    3379             :                                   GSpacing nLineSpace,
    3380             :                                   GDALRasterIOExtraArg *psExtraArgIn,
    3381             :                                   WorkingState &oWorkingState)
    3382             : 
    3383             : {
    3384             :     GDALRasterIOExtraArg sExtraArg;
    3385       23755 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    3386       23755 :     GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
    3387             : 
    3388       23755 :     double dfXOff = nXOff;
    3389       23755 :     double dfYOff = nYOff;
    3390       23755 :     double dfXSize = nXSize;
    3391       23755 :     double dfYSize = nYSize;
    3392       23755 :     if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
    3393             :     {
    3394          87 :         dfXOff = psExtraArgIn->dfXOff;
    3395          87 :         dfYOff = psExtraArgIn->dfYOff;
    3396          87 :         dfXSize = psExtraArgIn->dfXSize;
    3397          87 :         dfYSize = psExtraArgIn->dfYSize;
    3398             :     }
    3399             : 
    3400             :     // The window we will actually request from the source raster band.
    3401       23755 :     double dfReqXOff = 0.0;
    3402       23755 :     double dfReqYOff = 0.0;
    3403       23755 :     double dfReqXSize = 0.0;
    3404       23755 :     double dfReqYSize = 0.0;
    3405       23755 :     int nReqXOff = 0;
    3406       23755 :     int nReqYOff = 0;
    3407       23755 :     int nReqXSize = 0;
    3408       23755 :     int nReqYSize = 0;
    3409             : 
    3410             :     // The window we will actual set _within_ the pData buffer.
    3411       23755 :     int nOutXOff = 0;
    3412       23755 :     int nOutYOff = 0;
    3413       23755 :     int nOutXSize = 0;
    3414       23755 :     int nOutYSize = 0;
    3415             : 
    3416       23755 :     if (!m_osResampling.empty())
    3417             :     {
    3418          28 :         psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    3419             :     }
    3420       23727 :     else if (psExtraArgIn != nullptr)
    3421             :     {
    3422       23727 :         psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
    3423             :     }
    3424       23755 :     if (psExtraArgIn)
    3425       23755 :         psExtraArg->bOperateInBufType =
    3426       23755 :             GDAL_GET_OPERATE_IN_BUF_TYPE(*psExtraArgIn);
    3427             : 
    3428       23755 :     bool bError = false;
    3429       23755 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
    3430             :                          psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    3431             :                          &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff,
    3432             :                          &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
    3433             :                          &nOutXSize, &nOutYSize, bError))
    3434             :     {
    3435       13061 :         return bError ? CE_Failure : CE_None;
    3436             :     }
    3437             : #if DEBUG_VERBOSE
    3438             :     CPLDebug("VRT",
    3439             :              "nXOff=%d, nYOff=%d, nXSize=%d, nYSize=%d, nBufXSize=%d, "
    3440             :              "nBufYSize=%d,\n"
    3441             :              "dfReqXOff=%g, dfReqYOff=%g, dfReqXSize=%g, dfReqYSize=%g,\n"
    3442             :              "nReqXOff=%d, nReqYOff=%d, nReqXSize=%d, nReqYSize=%d,\n"
    3443             :              "nOutXOff=%d, nOutYOff=%d, nOutXSize=%d, nOutYSize=%d",
    3444             :              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, dfReqXOff,
    3445             :              dfReqYOff, dfReqXSize, dfReqYSize, nReqXOff, nReqYOff, nReqXSize,
    3446             :              nReqYSize, nOutXOff, nOutYOff, nOutXSize, nOutYSize);
    3447             : #endif
    3448             : 
    3449       10694 :     auto poSourceBand = GetRasterBand();
    3450       10694 :     if (!poSourceBand)
    3451           0 :         return CE_Failure;
    3452             : 
    3453       10694 :     psExtraArg->bFloatingPointWindowValidity = TRUE;
    3454       10694 :     psExtraArg->dfXOff = dfReqXOff;
    3455       10694 :     psExtraArg->dfYOff = dfReqYOff;
    3456       10694 :     psExtraArg->dfXSize = dfReqXSize;
    3457       10694 :     psExtraArg->dfYSize = dfReqYSize;
    3458       10694 :     if (psExtraArgIn)
    3459             :     {
    3460       10694 :         psExtraArg->pfnProgress = psExtraArgIn->pfnProgress;
    3461       10694 :         psExtraArg->pProgressData = psExtraArgIn->pProgressData;
    3462       10694 :         if (psExtraArgIn->nVersion >= 2)
    3463             :         {
    3464       10694 :             psExtraArg->bUseOnlyThisScale = psExtraArgIn->bUseOnlyThisScale;
    3465             :         }
    3466       10694 :         psExtraArg->bOperateInBufType =
    3467       10694 :             GDAL_GET_OPERATE_IN_BUF_TYPE(*psExtraArgIn);
    3468             :     }
    3469             : 
    3470       10694 :     GByte *const pabyOut = static_cast<GByte *>(pData) +
    3471       10694 :                            nPixelSpace * nOutXOff +
    3472       10694 :                            static_cast<GPtrDiff_t>(nLineSpace) * nOutYOff;
    3473       10694 :     const auto eSourceType = poSourceBand->GetRasterDataType();
    3474       10694 :     if (m_nProcessingFlags == PROCESSING_FLAG_NODATA)
    3475             :     {
    3476             :         // Optimization if doing only nodata processing
    3477          98 :         if (eBufType != eSourceType && psExtraArg->bOperateInBufType)
    3478             :         {
    3479          56 :             if (eBufType == GDT_Float32 &&
    3480          13 :                 GDALDataTypeUnion(eBufType, eSourceType) == eBufType)
    3481             :             {
    3482          11 :                 return RasterIOProcessNoData<float, GDT_Float32>(
    3483             :                     poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff,
    3484             :                     nReqXSize, nReqYSize, pabyOut, nOutXSize, nOutYSize,
    3485             :                     eBufType, nPixelSpace, nLineSpace, psExtraArg,
    3486          11 :                     oWorkingState);
    3487             :             }
    3488          48 :             else if (eBufType == GDT_Float64 &&
    3489          16 :                      GDALDataTypeUnion(eBufType, eSourceType) == eBufType)
    3490             :             {
    3491          16 :                 return RasterIOProcessNoData<double, GDT_Float64>(
    3492             :                     poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff,
    3493             :                     nReqXSize, nReqYSize, pabyOut, nOutXSize, nOutYSize,
    3494             :                     eBufType, nPixelSpace, nLineSpace, psExtraArg,
    3495          16 :                     oWorkingState);
    3496             :             }
    3497             :         }
    3498          55 :         else if (eSourceType == GDT_UInt8)
    3499             :         {
    3500          30 :             if (!GDALIsValueInRange<GByte>(m_dfNoDataValue))
    3501             :             {
    3502           1 :                 return VRTSimpleSource::RasterIO(
    3503             :                     eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
    3504             :                     nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
    3505           1 :                     psExtraArgIn, oWorkingState);
    3506             :             }
    3507          29 :             return RasterIOProcessNoData<GByte, GDT_UInt8>(
    3508             :                 poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
    3509             :                 nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
    3510          29 :                 nLineSpace, psExtraArg, oWorkingState);
    3511             :         }
    3512          25 :         else if (eSourceType == GDT_Int16)
    3513             :         {
    3514           2 :             if (!GDALIsValueInRange<int16_t>(m_dfNoDataValue))
    3515             :             {
    3516           1 :                 return VRTSimpleSource::RasterIO(
    3517             :                     eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
    3518             :                     nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
    3519           1 :                     psExtraArgIn, oWorkingState);
    3520             :             }
    3521             : 
    3522           1 :             return RasterIOProcessNoData<int16_t, GDT_Int16>(
    3523             :                 poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
    3524             :                 nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
    3525           1 :                 nLineSpace, psExtraArg, oWorkingState);
    3526             :         }
    3527          23 :         else if (eSourceType == GDT_UInt16)
    3528             :         {
    3529           5 :             if (!GDALIsValueInRange<uint16_t>(m_dfNoDataValue))
    3530             :             {
    3531           1 :                 return VRTSimpleSource::RasterIO(
    3532             :                     eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
    3533             :                     nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
    3534           1 :                     psExtraArgIn, oWorkingState);
    3535             :             }
    3536             : 
    3537           4 :             return RasterIOProcessNoData<uint16_t, GDT_UInt16>(
    3538             :                 poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
    3539             :                 nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
    3540           4 :                 nLineSpace, psExtraArg, oWorkingState);
    3541             :         }
    3542             :     }
    3543             : 
    3544       10630 :     const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eBufType));
    3545             :     CPLErr eErr;
    3546             :     // For Int32, float32 isn't sufficiently precise as working data type
    3547       10630 :     if (eVRTBandDataType == GDT_CInt32 || eVRTBandDataType == GDT_CFloat64 ||
    3548       10626 :         eVRTBandDataType == GDT_Int32 || eVRTBandDataType == GDT_UInt32 ||
    3549       10622 :         eVRTBandDataType == GDT_Int64 || eVRTBandDataType == GDT_UInt64 ||
    3550       10599 :         eVRTBandDataType == GDT_Float64 || eSourceType == GDT_Int32 ||
    3551       10599 :         eSourceType == GDT_UInt32 || eSourceType == GDT_Int64 ||
    3552             :         eSourceType == GDT_UInt64)
    3553             :     {
    3554          32 :         eErr = RasterIOInternal<double>(
    3555             :             poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
    3556             :             nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
    3557             :             nLineSpace, psExtraArg, bIsComplex ? GDT_CFloat64 : GDT_Float64,
    3558             :             oWorkingState);
    3559             :     }
    3560             :     else
    3561             :     {
    3562       10598 :         eErr = RasterIOInternal<float>(
    3563             :             poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
    3564             :             nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
    3565             :             nLineSpace, psExtraArg, bIsComplex ? GDT_CFloat32 : GDT_Float32,
    3566             :             oWorkingState);
    3567             :     }
    3568             : 
    3569       10630 :     if (psExtraArg->pfnProgress)
    3570          56 :         psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
    3571             : 
    3572       10630 :     return eErr;
    3573             : }
    3574             : 
    3575             : /************************************************************************/
    3576             : /*                            hasZeroByte()                             */
    3577             : /************************************************************************/
    3578             : 
    3579             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    3580     4718060 : static inline bool hasZeroByte(uint32_t v)
    3581             : {
    3582             :     // Cf https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
    3583     4718060 :     return (((v)-0x01010101U) & ~(v) & 0x80808080U) != 0;
    3584             : }
    3585             : 
    3586             : /************************************************************************/
    3587             : /*                       RasterIOProcessNoData()                        */
    3588             : /************************************************************************/
    3589             : 
    3590             : // This method is an optimization of the generic RasterIOInternal()
    3591             : // that deals with a VRTComplexSource with only a NODATA value in it, and
    3592             : // no other processing flags.
    3593             : 
    3594             : // nReqXOff, nReqYOff, nReqXSize, nReqYSize are expressed in source band
    3595             : // referential.
    3596             : template <class WorkingDT, GDALDataType eWorkingDT>
    3597          61 : CPLErr VRTComplexSource::RasterIOProcessNoData(
    3598             :     GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
    3599             :     int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
    3600             :     int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
    3601             :     GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
    3602             :     WorkingState &oWorkingState)
    3603             : {
    3604          61 :     CPLAssert(m_nProcessingFlags == PROCESSING_FLAG_NODATA);
    3605          61 :     CPLAssert(GDALIsValueExactAs<WorkingDT>(m_dfNoDataValue));
    3606          61 :     const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkingDT);
    3607          61 :     assert(nWorkDTSize != 0);
    3608             : 
    3609             :     /* -------------------------------------------------------------------- */
    3610             :     /*      Read into a temporary buffer.                                   */
    3611             :     /* -------------------------------------------------------------------- */
    3612             :     try
    3613             :     {
    3614             :         // Cannot overflow since pData should at least have that number of
    3615             :         // elements
    3616          61 :         const size_t nPixelCount = static_cast<size_t>(nOutXSize) * nOutYSize;
    3617          61 :         if (nPixelCount >
    3618          61 :             static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) /
    3619          61 :                 nWorkDTSize)
    3620             :         {
    3621           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    3622             :                      "Too large temporary buffer");
    3623           0 :             return CE_Failure;
    3624             :         }
    3625          61 :         oWorkingState.m_abyWrkBuffer.resize(nWorkDTSize * nPixelCount);
    3626             :     }
    3627           0 :     catch (const std::bad_alloc &e)
    3628             :     {
    3629           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    3630           0 :         return CE_Failure;
    3631             :     }
    3632             :     auto paSrcData =
    3633          61 :         reinterpret_cast<WorkingDT *>(oWorkingState.m_abyWrkBuffer.data());
    3634             : 
    3635             :     GDALRasterIOExtraArg sExtraArg;
    3636          61 :     GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    3637          61 :     if (!m_osResampling.empty())
    3638             :     {
    3639           0 :         sExtraArg.eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    3640             :     }
    3641          61 :     sExtraArg.bOperateInBufType = true;
    3642             : 
    3643          61 :     const CPLErr eErr = poSourceBand->RasterIO(
    3644             :         GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    3645          61 :         oWorkingState.m_abyWrkBuffer.data(), nOutXSize, nOutYSize, eWorkingDT,
    3646          61 :         nWorkDTSize, nWorkDTSize * static_cast<GSpacing>(nOutXSize),
    3647             :         &sExtraArg);
    3648             : 
    3649          61 :     if (eErr != CE_None)
    3650             :     {
    3651           0 :         return eErr;
    3652             :     }
    3653             : 
    3654          61 :     const auto nNoDataValue = static_cast<WorkingDT>(m_dfNoDataValue);
    3655          61 :     size_t idxBuffer = 0;
    3656         122 :     if (eWorkingDT == eBufType &&
    3657          61 :         !GDALDataTypeIsConversionLossy(eWorkingDT, eVRTBandDataType))
    3658             :     {
    3659             :         // Most optimized case: the output type is the same as the working type,
    3660             :         // and conversion from the working type to the VRT band data type is
    3661             :         // not lossy
    3662       19739 :         for (int iY = 0; iY < nOutYSize; iY++)
    3663             :         {
    3664       19683 :             GByte *pDstLocation = static_cast<GByte *>(pData) +
    3665       19683 :                                   static_cast<GPtrDiff_t>(nLineSpace) * iY;
    3666             : 
    3667       19683 :             int iX = 0;
    3668       19605 :             if (sizeof(WorkingDT) == 1 && nPixelSpace == 1)
    3669             :             {
    3670             :                 // Optimization to detect more quickly if source pixels are
    3671             :                 // at nodata.
    3672       19605 :                 const GByte byNoDataValue = static_cast<GByte>(nNoDataValue);
    3673       19605 :                 const uint32_t wordNoData =
    3674       19605 :                     (static_cast<uint32_t>(byNoDataValue) << 24) |
    3675       19605 :                     (byNoDataValue << 16) | (byNoDataValue << 8) |
    3676       19605 :                     byNoDataValue;
    3677             : 
    3678             :                 // Warning: hasZeroByte() assumes WORD_SIZE = 4
    3679       19605 :                 constexpr int WORD_SIZE = 4;
    3680     4737670 :                 for (; iX < nOutXSize - (WORD_SIZE - 1); iX += WORD_SIZE)
    3681             :                 {
    3682             :                     uint32_t v;
    3683             :                     static_assert(sizeof(v) == WORD_SIZE,
    3684             :                                   "sizeof(v) == WORD_SIZE");
    3685     4718060 :                     memcpy(&v, paSrcData + idxBuffer, sizeof(v));
    3686             :                     // Cf https://graphics.stanford.edu/~seander/bithacks.html#ValueInWord
    3687     4718060 :                     if (!hasZeroByte(v ^ wordNoData))
    3688             :                     {
    3689             :                         // No bytes are at nodata
    3690     4586980 :                         memcpy(pDstLocation, &v, WORD_SIZE);
    3691     4586980 :                         idxBuffer += WORD_SIZE;
    3692     4586980 :                         pDstLocation += WORD_SIZE;
    3693             :                     }
    3694      131087 :                     else if (v == wordNoData)
    3695             :                     {
    3696             :                         // All bytes are at nodata
    3697      131075 :                         idxBuffer += WORD_SIZE;
    3698      131075 :                         pDstLocation += WORD_SIZE;
    3699             :                     }
    3700             :                     else
    3701             :                     {
    3702             :                         // There are both bytes at nodata and valid bytes
    3703          60 :                         for (int k = 0; k < WORD_SIZE; ++k)
    3704             :                         {
    3705          48 :                             if (paSrcData[idxBuffer] != nNoDataValue)
    3706             :                             {
    3707          36 :                                 memcpy(pDstLocation, &paSrcData[idxBuffer],
    3708             :                                        sizeof(WorkingDT));
    3709             :                             }
    3710          48 :                             idxBuffer++;
    3711          48 :                             pDstLocation += nPixelSpace;
    3712             :                         }
    3713             :                     }
    3714             :                 }
    3715             :             }
    3716             : 
    3717       23990 :             for (; iX < nOutXSize;
    3718        4307 :                  iX++, pDstLocation += nPixelSpace, idxBuffer++)
    3719             :             {
    3720             :                 if constexpr (eWorkingDT == GDT_Float32 ||
    3721             :                               eWorkingDT == GDT_Float64)
    3722             :                 {
    3723         463 :                     if (std::isnan(paSrcData[idxBuffer]))
    3724             :                     {
    3725           0 :                         continue;
    3726             :                     }
    3727             :                 }
    3728        4307 :                 if (paSrcData[idxBuffer] != nNoDataValue)
    3729             :                 {
    3730        4260 :                     memcpy(pDstLocation, &paSrcData[idxBuffer],
    3731             :                            sizeof(WorkingDT));
    3732             :                 }
    3733             :             }
    3734             :         }
    3735             :     }
    3736           5 :     else if (!GDALDataTypeIsConversionLossy(eWorkingDT, eVRTBandDataType))
    3737             :     {
    3738             :         // Conversion from the work type to the VRT band data type is
    3739             :         // not lossy, so we can directly convert from the work type to
    3740             :         // the the output type
    3741           0 :         for (int iY = 0; iY < nOutYSize; iY++)
    3742             :         {
    3743           0 :             GByte *pDstLocation = static_cast<GByte *>(pData) +
    3744           0 :                                   static_cast<GPtrDiff_t>(nLineSpace) * iY;
    3745             : 
    3746           0 :             for (int iX = 0; iX < nOutXSize;
    3747           0 :                  iX++, pDstLocation += nPixelSpace, idxBuffer++)
    3748             :             {
    3749             :                 if constexpr (eWorkingDT == GDT_Float32 ||
    3750             :                               eWorkingDT == GDT_Float64)
    3751             :                 {
    3752           0 :                     if (std::isnan(paSrcData[idxBuffer]))
    3753             :                     {
    3754           0 :                         continue;
    3755             :                     }
    3756             :                 }
    3757           0 :                 if (paSrcData[idxBuffer] != nNoDataValue)
    3758             :                 {
    3759           0 :                     CopyWordOut(&paSrcData[idxBuffer], eWorkingDT, pDstLocation,
    3760             :                                 eBufType);
    3761             :                 }
    3762             :             }
    3763             :         }
    3764             :     }
    3765             :     else
    3766             :     {
    3767           5 :         const bool bClampToVRTBandType =
    3768           5 :             GDAL_GET_OPERATE_IN_BUF_TYPE(*psExtraArg);
    3769             : 
    3770             :         GByte abyTemp[2 * sizeof(double)];
    3771          43 :         for (int iY = 0; iY < nOutYSize; iY++)
    3772             :         {
    3773          38 :             GByte *pDstLocation = static_cast<GByte *>(pData) +
    3774          38 :                                   static_cast<GPtrDiff_t>(nLineSpace) * iY;
    3775             : 
    3776         328 :             for (int iX = 0; iX < nOutXSize;
    3777         290 :                  iX++, pDstLocation += nPixelSpace, idxBuffer++)
    3778             :             {
    3779             :                 if constexpr (eWorkingDT == GDT_Float32 ||
    3780             :                               eWorkingDT == GDT_Float64)
    3781             :                 {
    3782         260 :                     if (std::isnan(paSrcData[idxBuffer]))
    3783             :                     {
    3784          20 :                         continue;
    3785             :                     }
    3786             :                 }
    3787         270 :                 if (paSrcData[idxBuffer] != nNoDataValue)
    3788             :                 {
    3789         240 :                     if (bClampToVRTBandType)
    3790             :                     {
    3791         240 :                         GDALClampValueToType(&paSrcData[idxBuffer],
    3792             :                                              eVRTBandDataType);
    3793         240 :                         CopyWordOut(&paSrcData[idxBuffer], eWorkingDT,
    3794             :                                     pDstLocation, eBufType);
    3795             :                     }
    3796             :                     else
    3797             :                     {
    3798             :                         // Convert first to the VRTRasterBand data type
    3799             :                         // to get its clamping, before outputting to buffer data type
    3800           0 :                         CopyWordOut(&paSrcData[idxBuffer], eWorkingDT, abyTemp,
    3801             :                                     eVRTBandDataType);
    3802           0 :                         GDALCopyWords(abyTemp, eVRTBandDataType, 0,
    3803             :                                       pDstLocation, eBufType, 0, 1);
    3804             :                     }
    3805             :                 }
    3806             :             }
    3807             :         }
    3808             :     }
    3809             : 
    3810          61 :     return CE_None;
    3811             : }
    3812             : 
    3813             : /************************************************************************/
    3814             : /*                          RasterIOInternal()                          */
    3815             : /************************************************************************/
    3816             : 
    3817             : // nReqXOff, nReqYOff, nReqXSize, nReqYSize are expressed in source band
    3818             : // referential.
    3819             : template <class WorkingDT>
    3820       10675 : CPLErr VRTComplexSource::RasterIOInternal(
    3821             :     GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
    3822             :     int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
    3823             :     int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
    3824             :     GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
    3825             :     GDALDataType eWrkDataType, WorkingState &oWorkingState)
    3826             : {
    3827       10675 :     const GDALColorTable *poColorTable = nullptr;
    3828       10675 :     const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eBufType));
    3829       10675 :     const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWrkDataType);
    3830       10675 :     assert(nWorkDTSize != 0);
    3831             : 
    3832             :     // If no explicit <NODATA> is set, but UseMaskBand is set, and the band
    3833             :     // has a nodata value, then use it as if it was set as <NODATA>
    3834       10675 :     int bNoDataSet = (m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0;
    3835       10675 :     double dfNoDataValue = GetAdjustedNoDataValue();
    3836             : 
    3837       10782 :     if ((m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0 &&
    3838         107 :         poSourceBand->GetMaskFlags() == GMF_NODATA)
    3839             :     {
    3840           0 :         dfNoDataValue = poSourceBand->GetNoDataValue(&bNoDataSet);
    3841             :     }
    3842             : 
    3843       10675 :     const bool bNoDataSetIsNan = bNoDataSet && std::isnan(dfNoDataValue);
    3844       10675 :     const bool bNoDataSetAndNotNan =
    3845       15592 :         bNoDataSet && !std::isnan(dfNoDataValue) &&
    3846        4917 :         GDALIsValueInRange<WorkingDT>(dfNoDataValue);
    3847       10675 :     const auto fWorkingDataTypeNoData = static_cast<WorkingDT>(dfNoDataValue);
    3848             : 
    3849       10675 :     const GByte *pabyMask = nullptr;
    3850       10675 :     const WorkingDT *pafData = nullptr;
    3851       10675 :     if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0 &&
    3852        4996 :         m_dfScaleRatio == 0 && bNoDataSet == FALSE &&
    3853        4579 :         (m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) == 0)
    3854             :     {
    3855             :         /* ------------------------------------------------------------------ */
    3856             :         /*      Optimization when writing a constant value */
    3857             :         /*      (used by the -addalpha option of gdalbuildvrt) */
    3858             :         /* ------------------------------------------------------------------ */
    3859             :         // Already set to NULL when defined.
    3860             :         // pafData = NULL;
    3861             :     }
    3862             :     else
    3863             :     {
    3864             :         /* ---------------------------------------------------------------- */
    3865             :         /*      Read into a temporary buffer.                               */
    3866             :         /* ---------------------------------------------------------------- */
    3867        6096 :         const size_t nPixelCount = static_cast<size_t>(nOutXSize) * nOutYSize;
    3868             :         try
    3869             :         {
    3870             :             // Cannot overflow since pData should at least have that number of
    3871             :             // elements
    3872        6096 :             if (nPixelCount >
    3873        6096 :                 static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) /
    3874        6096 :                     static_cast<size_t>(nWorkDTSize))
    3875             :             {
    3876           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    3877             :                          "Too large temporary buffer");
    3878           0 :                 return CE_Failure;
    3879             :             }
    3880        6096 :             oWorkingState.m_abyWrkBuffer.resize(nWorkDTSize * nPixelCount);
    3881             :         }
    3882           0 :         catch (const std::bad_alloc &e)
    3883             :         {
    3884           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    3885           0 :             return CE_Failure;
    3886             :         }
    3887             :         pafData = reinterpret_cast<const WorkingDT *>(
    3888        6096 :             oWorkingState.m_abyWrkBuffer.data());
    3889             : 
    3890             :         GDALRasterIOExtraArg sExtraArg;
    3891        6096 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    3892        6096 :         if (!m_osResampling.empty())
    3893             :         {
    3894          28 :             sExtraArg.eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
    3895             :         }
    3896        6096 :         sExtraArg.bOperateInBufType = true;
    3897             : 
    3898        6096 :         const CPLErr eErr = poSourceBand->RasterIO(
    3899             :             GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    3900        6096 :             oWorkingState.m_abyWrkBuffer.data(), nOutXSize, nOutYSize,
    3901             :             eWrkDataType, nWorkDTSize,
    3902        6096 :             nWorkDTSize * static_cast<GSpacing>(nOutXSize), &sExtraArg);
    3903             : 
    3904        6096 :         if (eErr != CE_None)
    3905             :         {
    3906           0 :             return eErr;
    3907             :         }
    3908             : 
    3909             :         // Allocate and read mask band if needed
    3910       13364 :         if (!bNoDataSet &&
    3911        6203 :             (m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0 &&
    3912         107 :             (poSourceBand->GetMaskFlags() != GMF_ALL_VALID ||
    3913          52 :              poSourceBand->GetColorInterpretation() == GCI_AlphaBand ||
    3914          32 :              GetMaskBandMainBand() != nullptr))
    3915             :         {
    3916             :             try
    3917             :             {
    3918         107 :                 oWorkingState.m_abyWrkBufferMask.resize(nPixelCount);
    3919             :             }
    3920           0 :             catch (const std::exception &)
    3921             :             {
    3922           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    3923             :                          "Out of memory when allocating mask buffer");
    3924           0 :                 return CE_Failure;
    3925             :             }
    3926             :             pabyMask = reinterpret_cast<const GByte *>(
    3927         107 :                 oWorkingState.m_abyWrkBufferMask.data());
    3928          52 :             auto poMaskBand =
    3929         107 :                 (poSourceBand->GetColorInterpretation() == GCI_AlphaBand ||
    3930          87 :                  GetMaskBandMainBand() != nullptr)
    3931             :                     ? poSourceBand
    3932          55 :                     : poSourceBand->GetMaskBand();
    3933         107 :             if (poMaskBand->RasterIO(
    3934             :                     GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
    3935         107 :                     oWorkingState.m_abyWrkBufferMask.data(), nOutXSize,
    3936             :                     nOutYSize, GDT_UInt8, 1, static_cast<GSpacing>(nOutXSize),
    3937         107 :                     psExtraArg) != CE_None)
    3938             :             {
    3939           0 :                 return CE_Failure;
    3940             :             }
    3941             :         }
    3942             : 
    3943        6096 :         if (m_nColorTableComponent != 0)
    3944             :         {
    3945        5254 :             poColorTable = poSourceBand->GetColorTable();
    3946        5254 :             if (poColorTable == nullptr)
    3947             :             {
    3948           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3949             :                          "Source band has no color table.");
    3950           0 :                 return CE_Failure;
    3951             :             }
    3952             :         }
    3953             :     }
    3954             : 
    3955             :     /* -------------------------------------------------------------------- */
    3956             :     /*      Selectively copy into output buffer with nodata masking,        */
    3957             :     /*      and/or scaling.                                                 */
    3958             :     /* -------------------------------------------------------------------- */
    3959             : 
    3960       10675 :     const bool bTwoStepDataTypeConversion =
    3961       10675 :         CPL_TO_BOOL(
    3962       21251 :             GDALDataTypeIsConversionLossy(eWrkDataType, eVRTBandDataType)) &&
    3963       10576 :         !CPL_TO_BOOL(GDALDataTypeIsConversionLossy(eVRTBandDataType, eBufType));
    3964             : 
    3965       10675 :     size_t idxBuffer = 0;
    3966      160955 :     for (int iY = 0; iY < nOutYSize; iY++)
    3967             :     {
    3968      150280 :         GByte *pDstLocation = static_cast<GByte *>(pData) +
    3969      150280 :                               static_cast<GPtrDiff_t>(nLineSpace) * iY;
    3970             : 
    3971    37818792 :         for (int iX = 0; iX < nOutXSize;
    3972    37668499 :              iX++, pDstLocation += nPixelSpace, idxBuffer++)
    3973             :         {
    3974             :             WorkingDT afResult[2];
    3975    37668499 :             if (pafData && !bIsComplex)
    3976             :             {
    3977    29871096 :                 WorkingDT fResult = pafData[idxBuffer];
    3978    29871096 :                 if (bNoDataSetIsNan && std::isnan(fResult))
    3979    20978382 :                     continue;
    3980    52059236 :                 if (bNoDataSetAndNotNan &&
    3981    22188046 :                     ARE_REAL_EQUAL(fResult, fWorkingDataTypeNoData))
    3982    19790076 :                     continue;
    3983    10081114 :                 if (pabyMask && pabyMask[idxBuffer] == 0)
    3984     1188350 :                     continue;
    3985             : 
    3986     8892734 :                 if (poColorTable)
    3987             :                 {
    3988             :                     const GDALColorEntry *poEntry =
    3989     4071050 :                         poColorTable->GetColorEntry(static_cast<int>(fResult));
    3990     4071050 :                     if (poEntry)
    3991             :                     {
    3992     4071050 :                         if (m_nColorTableComponent == 1)
    3993     1398360 :                             fResult = poEntry->c1;
    3994     2672700 :                         else if (m_nColorTableComponent == 2)
    3995     1243780 :                             fResult = poEntry->c2;
    3996     1428910 :                         else if (m_nColorTableComponent == 3)
    3997     1243780 :                             fResult = poEntry->c3;
    3998      185131 :                         else if (m_nColorTableComponent == 4)
    3999      185131 :                             fResult = poEntry->c4;
    4000             :                     }
    4001             :                     else
    4002             :                     {
    4003           0 :                         CPLErrorOnce(CE_Failure, CPLE_AppDefined,
    4004             :                                      "No entry %d.", static_cast<int>(fResult));
    4005           0 :                         continue;
    4006             :                     }
    4007             :                 }
    4008             : 
    4009     8892734 :                 if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
    4010             :                 {
    4011     2629352 :                     fResult = static_cast<WorkingDT>(fResult * m_dfScaleRatio +
    4012     2629352 :                                                      m_dfScaleOff);
    4013             :                 }
    4014     6263382 :                 else if ((m_nProcessingFlags &
    4015             :                           PROCESSING_FLAG_SCALING_EXPONENTIAL) != 0)
    4016             :                 {
    4017        3579 :                     if (!m_bSrcMinMaxDefined)
    4018             :                     {
    4019           1 :                         int bSuccessMin = FALSE;
    4020           1 :                         int bSuccessMax = FALSE;
    4021           2 :                         double adfMinMax[2] = {
    4022           1 :                             poSourceBand->GetMinimum(&bSuccessMin),
    4023           1 :                             poSourceBand->GetMaximum(&bSuccessMax)};
    4024           2 :                         if ((bSuccessMin && bSuccessMax) ||
    4025           1 :                             poSourceBand->ComputeRasterMinMax(
    4026             :                                 TRUE, adfMinMax) == CE_None)
    4027             :                         {
    4028           1 :                             m_dfSrcMin = adfMinMax[0];
    4029           1 :                             m_dfSrcMax = adfMinMax[1];
    4030           1 :                             m_bSrcMinMaxDefined = true;
    4031             :                         }
    4032             :                         else
    4033             :                         {
    4034           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    4035             :                                      "Cannot determine source min/max value");
    4036           0 :                             return CE_Failure;
    4037             :                         }
    4038             :                     }
    4039             : 
    4040        7158 :                     double dfPowVal = (m_dfSrcMin == m_dfSrcMax)
    4041        3579 :                                           ? 0
    4042        3579 :                                           : (fResult - m_dfSrcMin) /
    4043        3579 :                                                 (m_dfSrcMax - m_dfSrcMin);
    4044        3579 :                     if (m_bClip)
    4045             :                     {
    4046        3574 :                         if (dfPowVal < 0.0)
    4047           1 :                             dfPowVal = 0.0;
    4048        3573 :                         else if (dfPowVal > 1.0)
    4049         700 :                             dfPowVal = 1.0;
    4050             :                     }
    4051        3579 :                     fResult =
    4052        3579 :                         static_cast<WorkingDT>((m_dfDstMax - m_dfDstMin) *
    4053        3579 :                                                    pow(dfPowVal, m_dfExponent) +
    4054        3579 :                                                m_dfDstMin);
    4055             :                 }
    4056             : 
    4057     8892734 :                 if (!m_adfLUTInputs.empty())
    4058      994484 :                     fResult = static_cast<WorkingDT>(LookupValue(fResult));
    4059             : 
    4060     8892734 :                 if (m_nMaxValue != 0 && fResult > m_nMaxValue)
    4061         800 :                     fResult = static_cast<WorkingDT>(m_nMaxValue);
    4062             : 
    4063     8892734 :                 afResult[0] = fResult;
    4064     8892734 :                 afResult[1] = 0;
    4065             :             }
    4066     7797343 :             else if (pafData && bIsComplex)
    4067             :             {
    4068        1018 :                 afResult[0] = pafData[2 * idxBuffer];
    4069        1018 :                 afResult[1] = pafData[2 * idxBuffer + 1];
    4070             : 
    4071             :                 // Do not use color table.
    4072        1018 :                 if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
    4073             :                 {
    4074           4 :                     afResult[0] = static_cast<WorkingDT>(
    4075           4 :                         afResult[0] * m_dfScaleRatio + m_dfScaleOff);
    4076           4 :                     afResult[1] = static_cast<WorkingDT>(
    4077           4 :                         afResult[1] * m_dfScaleRatio + m_dfScaleOff);
    4078             :                 }
    4079             : 
    4080             :                 /* Do not use LUT */
    4081             :             }
    4082             :             else
    4083             :             {
    4084     7796332 :                 afResult[0] = static_cast<WorkingDT>(m_dfScaleOff);
    4085     7796332 :                 afResult[1] = 0;
    4086             : 
    4087     7796332 :                 if (!m_adfLUTInputs.empty())
    4088           0 :                     afResult[0] =
    4089           0 :                         static_cast<WorkingDT>(LookupValue(afResult[0]));
    4090             : 
    4091     7796332 :                 if (m_nMaxValue != 0 && afResult[0] > m_nMaxValue)
    4092           0 :                     afResult[0] = static_cast<WorkingDT>(m_nMaxValue);
    4093             :             }
    4094             : 
    4095    16690117 :             if (eBufType == GDT_UInt8 && eVRTBandDataType == GDT_UInt8)
    4096             :             {
    4097     6623790 :                 *pDstLocation = static_cast<GByte>(std::min(
    4098    13247600 :                     255.0f,
    4099     6623790 :                     std::max(0.0f, static_cast<float>(afResult[0]) + 0.5f)));
    4100             :             }
    4101    10066317 :             else if (!bTwoStepDataTypeConversion)
    4102             :             {
    4103      259914 :                 CopyWordOut(afResult, eWrkDataType, pDstLocation, eBufType);
    4104             :             }
    4105     9806378 :             else if (eVRTBandDataType == GDT_Float32 && eBufType == GDT_Float64)
    4106             :             {
    4107             :                 // Particular case of the below 2-step conversion.
    4108             :                 // Helps a bit for some geolocation based warping with Sentinel3
    4109             :                 // data where the longitude/latitude arrays are Int32 bands,
    4110             :                 // rescaled in VRT as Float32 and requested as Float64
    4111             :                 float fVal;
    4112           0 :                 GDALCopyWord(afResult[0], fVal);
    4113           0 :                 *reinterpret_cast<double *>(pDstLocation) = fVal;
    4114             :             }
    4115             :             else
    4116             :             {
    4117             :                 GByte abyTemp[2 * sizeof(double)];
    4118             :                 // Convert first to the VRTRasterBand data type
    4119             :                 // to get its clamping, before outputting to buffer data type
    4120     9806378 :                 CopyWordOut(afResult, eWrkDataType, abyTemp, eVRTBandDataType);
    4121     9806378 :                 GDALCopyWords(abyTemp, eVRTBandDataType, 0, pDstLocation,
    4122             :                               eBufType, 0, 1);
    4123             :             }
    4124             :         }
    4125             :     }
    4126             : 
    4127       10675 :     return CE_None;
    4128             : }
    4129             : 
    4130             : // Explicitly instantiate template method, as it is used in another file.
    4131             : template CPLErr VRTComplexSource::RasterIOInternal<float>(
    4132             :     GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
    4133             :     int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
    4134             :     int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
    4135             :     GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
    4136             :     GDALDataType eWrkDataType, WorkingState &oWorkingState);
    4137             : 
    4138             : /************************************************************************/
    4139             : /*                         AreValuesUnchanged()                         */
    4140             : /************************************************************************/
    4141             : 
    4142           9 : bool VRTComplexSource::AreValuesUnchanged() const
    4143             : {
    4144          10 :     return m_dfScaleOff == 0.0 && m_dfScaleRatio == 1.0 &&
    4145          24 :            m_adfLUTInputs.empty() && m_nColorTableComponent == 0 &&
    4146          14 :            (m_nProcessingFlags & PROCESSING_FLAG_SCALING_EXPONENTIAL) == 0;
    4147             : }
    4148             : 
    4149             : /************************************************************************/
    4150             : /*                             GetMinimum()                             */
    4151             : /************************************************************************/
    4152             : 
    4153           2 : double VRTComplexSource::GetMinimum(int nXSize, int nYSize, int *pbSuccess)
    4154             : {
    4155           2 :     if (AreValuesUnchanged())
    4156             :     {
    4157           1 :         return VRTSimpleSource::GetMinimum(nXSize, nYSize, pbSuccess);
    4158             :     }
    4159             : 
    4160           1 :     *pbSuccess = FALSE;
    4161           1 :     return 0;
    4162             : }
    4163             : 
    4164             : /************************************************************************/
    4165             : /*                             GetMaximum()                             */
    4166             : /************************************************************************/
    4167             : 
    4168           2 : double VRTComplexSource::GetMaximum(int nXSize, int nYSize, int *pbSuccess)
    4169             : {
    4170           2 :     if (AreValuesUnchanged())
    4171             :     {
    4172           1 :         return VRTSimpleSource::GetMaximum(nXSize, nYSize, pbSuccess);
    4173             :     }
    4174             : 
    4175           1 :     *pbSuccess = FALSE;
    4176           1 :     return 0;
    4177             : }
    4178             : 
    4179             : /************************************************************************/
    4180             : /*                            GetHistogram()                            */
    4181             : /************************************************************************/
    4182             : 
    4183           0 : CPLErr VRTComplexSource::GetHistogram(int nXSize, int nYSize, double dfMin,
    4184             :                                       double dfMax, int nBuckets,
    4185             :                                       GUIntBig *panHistogram,
    4186             :                                       int bIncludeOutOfRange, int bApproxOK,
    4187             :                                       GDALProgressFunc pfnProgress,
    4188             :                                       void *pProgressData)
    4189             : {
    4190           0 :     if (AreValuesUnchanged())
    4191             :     {
    4192           0 :         return VRTSimpleSource::GetHistogram(
    4193             :             nXSize, nYSize, dfMin, dfMax, nBuckets, panHistogram,
    4194           0 :             bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
    4195             :     }
    4196             : 
    4197           0 :     return CE_Failure;
    4198             : }
    4199             : 
    4200             : /************************************************************************/
    4201             : /* ==================================================================== */
    4202             : /*                          VRTFuncSource                               */
    4203             : /* ==================================================================== */
    4204             : /************************************************************************/
    4205             : 
    4206             : /************************************************************************/
    4207             : /*                           VRTFuncSource()                            */
    4208             : /************************************************************************/
    4209             : 
    4210          12 : VRTFuncSource::VRTFuncSource()
    4211             :     : pfnReadFunc(nullptr), pCBData(nullptr), eType(GDT_UInt8),
    4212          12 :       fNoDataValue(static_cast<float>(VRT_NODATA_UNSET))
    4213             : {
    4214          12 : }
    4215             : 
    4216             : /************************************************************************/
    4217             : /*                           ~VRTFuncSource()                           */
    4218             : /************************************************************************/
    4219             : 
    4220          24 : VRTFuncSource::~VRTFuncSource()
    4221             : {
    4222          24 : }
    4223             : 
    4224             : /************************************************************************/
    4225             : /*                              GetType()                               */
    4226             : /************************************************************************/
    4227             : 
    4228           0 : const char *VRTFuncSource::GetType() const
    4229             : {
    4230             :     static const char *TYPE = "FuncSource";
    4231           0 :     return TYPE;
    4232             : }
    4233             : 
    4234             : /************************************************************************/
    4235             : /*                           SerializeToXML()                           */
    4236             : /************************************************************************/
    4237             : 
    4238           0 : CPLXMLNode *VRTFuncSource::SerializeToXML(CPL_UNUSED const char *pszVRTPath)
    4239             : {
    4240           0 :     return nullptr;
    4241             : }
    4242             : 
    4243             : /************************************************************************/
    4244             : /*                              RasterIO()                              */
    4245             : /************************************************************************/
    4246             : 
    4247          10 : CPLErr VRTFuncSource::RasterIO(GDALDataType /*eVRTBandDataType*/, int nXOff,
    4248             :                                int nYOff, int nXSize, int nYSize, void *pData,
    4249             :                                int nBufXSize, int nBufYSize,
    4250             :                                GDALDataType eBufType, GSpacing nPixelSpace,
    4251             :                                GSpacing nLineSpace,
    4252             :                                GDALRasterIOExtraArg * /* psExtraArg */,
    4253             :                                WorkingState & /* oWorkingState */)
    4254             : {
    4255          10 :     if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
    4256          10 :         nLineSpace == nPixelSpace * nXSize && nBufXSize == nXSize &&
    4257          20 :         nBufYSize == nYSize && eBufType == eType)
    4258             :     {
    4259          10 :         return pfnReadFunc(pCBData, nXOff, nYOff, nXSize, nYSize, pData);
    4260             :     }
    4261             :     else
    4262             :     {
    4263           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4264             :                  "VRTFuncSource::RasterIO() - Irregular request.");
    4265           0 :         CPLDebug("VRT", "Irregular request: %d,%d  %d,%d, %d,%d %d,%d %d,%d",
    4266             :                  static_cast<int>(nPixelSpace),
    4267             :                  GDALGetDataTypeSizeBytes(eBufType),
    4268             :                  static_cast<int>(nLineSpace),
    4269             :                  static_cast<int>(nPixelSpace) * nXSize, nBufXSize, nXSize,
    4270             :                  nBufYSize, nYSize, static_cast<int>(eBufType),
    4271           0 :                  static_cast<int>(eType));
    4272             : 
    4273           0 :         return CE_Failure;
    4274             :     }
    4275             : }
    4276             : 
    4277             : /************************************************************************/
    4278             : /*                             GetMinimum()                             */
    4279             : /************************************************************************/
    4280             : 
    4281           0 : double VRTFuncSource::GetMinimum(int /* nXSize */, int /* nYSize */,
    4282             :                                  int *pbSuccess)
    4283             : {
    4284           0 :     *pbSuccess = FALSE;
    4285           0 :     return 0;
    4286             : }
    4287             : 
    4288             : /************************************************************************/
    4289             : /*                             GetMaximum()                             */
    4290             : /************************************************************************/
    4291             : 
    4292           0 : double VRTFuncSource::GetMaximum(int /* nXSize */, int /* nYSize */,
    4293             :                                  int *pbSuccess)
    4294             : {
    4295           0 :     *pbSuccess = FALSE;
    4296           0 :     return 0;
    4297             : }
    4298             : 
    4299             : /************************************************************************/
    4300             : /*                            GetHistogram()                            */
    4301             : /************************************************************************/
    4302             : 
    4303           0 : CPLErr VRTFuncSource::GetHistogram(
    4304             :     int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
    4305             :     int /* nBuckets */, GUIntBig * /* panHistogram */,
    4306             :     int /* bIncludeOutOfRange */, int /* bApproxOK */,
    4307             :     GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
    4308             : {
    4309           0 :     return CE_Failure;
    4310             : }
    4311             : 
    4312             : /************************************************************************/
    4313             : /*                        VRTParseCoreSources()                         */
    4314             : /************************************************************************/
    4315             : 
    4316      104128 : VRTSource *VRTParseCoreSources(const CPLXMLNode *psChild,
    4317             :                                const char *pszVRTPath,
    4318             :                                VRTMapSharedResources &oMapSharedSources)
    4319             : 
    4320             : {
    4321      104128 :     VRTSource *poSource = nullptr;
    4322             : 
    4323      208249 :     if (EQUAL(psChild->pszValue, VRTAveragedSource::GetTypeStatic()) ||
    4324      104121 :         (EQUAL(psChild->pszValue, VRTSimpleSource::GetTypeStatic()) &&
    4325      103864 :          STARTS_WITH_CI(CPLGetXMLValue(psChild, "Resampling", "Nearest"),
    4326             :                         "Aver")))
    4327             :     {
    4328          16 :         poSource = new VRTAveragedSource();
    4329             :     }
    4330      104112 :     else if (EQUAL(psChild->pszValue, VRTSimpleSource::GetTypeStatic()))
    4331             :     {
    4332      103855 :         poSource = new VRTSimpleSource();
    4333             :     }
    4334         257 :     else if (EQUAL(psChild->pszValue, VRTComplexSource::GetTypeStatic()))
    4335             :     {
    4336         249 :         poSource = new VRTComplexSource();
    4337             :     }
    4338           8 :     else if (EQUAL(psChild->pszValue, VRTNoDataFromMaskSource::GetTypeStatic()))
    4339             :     {
    4340           8 :         poSource = new VRTNoDataFromMaskSource();
    4341             :     }
    4342             :     else
    4343             :     {
    4344           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4345             :                  "VRTParseCoreSources() - Unknown source : %s",
    4346           0 :                  psChild->pszValue);
    4347           0 :         return nullptr;
    4348             :     }
    4349             : 
    4350      104128 :     if (poSource->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
    4351      104125 :         return poSource;
    4352             : 
    4353           3 :     delete poSource;
    4354           3 :     return nullptr;
    4355             : }
    4356             : 
    4357             : /*! @endcond */

Generated by: LCOV version 1.14