LCOV - code coverage report
Current view: top level - frmts/vrt - vrtprocesseddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 694 843 82.3 %
Date: 2025-07-11 10:11:13 Functions: 23 24 95.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTProcessedDataset.
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_minixml.h"
      14             : #include "cpl_string.h"
      15             : #include "gdal_utils.h"
      16             : #include "vrtdataset.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <limits>
      20             : #include <map>
      21             : #include <vector>
      22             : 
      23             : /************************************************************************/
      24             : /*                        VRTProcessedDatasetFunc                       */
      25             : /************************************************************************/
      26             : 
      27             : //! Structure holding information for a VRTProcessedDataset function.
      28             : struct VRTProcessedDatasetFunc
      29             : {
      30             :     //! Processing function name
      31             :     std::string osFuncName{};
      32             : 
      33             :     //! User data to provide to pfnInit, pfnFree, pfnProcess callbacks.
      34             :     void *pUserData = nullptr;
      35             : 
      36             :     //! Whether XML metadata has been specified
      37             :     bool bMetadataSpecified = false;
      38             : 
      39             :     //! Map of (constant argument name, constant value)
      40             :     std::map<std::string, std::string> oMapConstantArguments{};
      41             : 
      42             :     //! Set of builtin argument names (e.g "offset", "scale", "nodata")
      43             :     std::set<std::string> oSetBuiltinArguments{};
      44             : 
      45             :     //! Arguments defined in the VRT
      46             :     struct OtherArgument
      47             :     {
      48             :         std::string osType{};
      49             :         bool bRequired = false;
      50             :     };
      51             : 
      52             :     std::map<std::string, OtherArgument> oOtherArguments{};
      53             : 
      54             :     //! Requested input data type.
      55             :     GDALDataType eRequestedInputDT = GDT_Unknown;
      56             : 
      57             :     //! List of supported input datatypes. Empty if no restriction.
      58             :     std::vector<GDALDataType> aeSupportedInputDT{};
      59             : 
      60             :     //! List of supported input band counts. Empty if no restriction.
      61             :     std::vector<int> anSupportedInputBandCount{};
      62             : 
      63             :     //! Optional initialization function
      64             :     GDALVRTProcessedDatasetFuncInit pfnInit = nullptr;
      65             : 
      66             :     //! Optional free function
      67             :     GDALVRTProcessedDatasetFuncFree pfnFree = nullptr;
      68             : 
      69             :     //! Required processing function
      70             :     GDALVRTProcessedDatasetFuncProcess pfnProcess = nullptr;
      71             : };
      72             : 
      73             : /************************************************************************/
      74             : /*                      GetGlobalMapProcessedDatasetFunc()              */
      75             : /************************************************************************/
      76             : 
      77             : /** Return the registry of VRTProcessedDatasetFunc functions */
      78             : static std::map<std::string, VRTProcessedDatasetFunc> &
      79        7479 : GetGlobalMapProcessedDatasetFunc()
      80             : {
      81        7479 :     static std::map<std::string, VRTProcessedDatasetFunc> goMap;
      82        7479 :     return goMap;
      83             : }
      84             : 
      85             : /************************************************************************/
      86             : /*                            Step::~Step()                             */
      87             : /************************************************************************/
      88             : 
      89             : /*! @cond Doxygen_Suppress */
      90             : 
      91             : /** Step destructor */
      92         150 : VRTProcessedDataset::Step::~Step()
      93             : {
      94         150 :     deinit();
      95         150 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                           Step::deinit()                             */
      99             : /************************************************************************/
     100             : 
     101             : /** Free pWorkingData */
     102         150 : void VRTProcessedDataset::Step::deinit()
     103             : {
     104         150 :     if (pWorkingData)
     105             :     {
     106          58 :         const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
     107          58 :         const auto oIterFunc = oMapFunctions.find(osAlgorithm);
     108          58 :         if (oIterFunc != oMapFunctions.end())
     109             :         {
     110          58 :             if (oIterFunc->second.pfnFree)
     111             :             {
     112         116 :                 oIterFunc->second.pfnFree(osAlgorithm.c_str(),
     113          58 :                                           oIterFunc->second.pUserData,
     114             :                                           pWorkingData);
     115             :             }
     116             :         }
     117             :         else
     118             :         {
     119           0 :             CPLAssert(false);
     120             :         }
     121          58 :         pWorkingData = nullptr;
     122             :     }
     123         150 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                        Step::Step(Step&& other)                      */
     127             : /************************************************************************/
     128             : 
     129             : /** Move constructor */
     130          61 : VRTProcessedDataset::Step::Step(Step &&other)
     131          61 :     : osAlgorithm(std::move(other.osAlgorithm)),
     132         122 :       aosArguments(std::move(other.aosArguments)), eInDT(other.eInDT),
     133          61 :       eOutDT(other.eOutDT), nInBands(other.nInBands),
     134          61 :       nOutBands(other.nOutBands), adfInNoData(other.adfInNoData),
     135          61 :       adfOutNoData(other.adfOutNoData), pWorkingData(other.pWorkingData)
     136             : {
     137          61 :     other.pWorkingData = nullptr;
     138          61 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                      Step operator=(Step&& other)                    */
     142             : /************************************************************************/
     143             : 
     144             : /** Move assignment operator */
     145           0 : VRTProcessedDataset::Step &VRTProcessedDataset::Step::operator=(Step &&other)
     146             : {
     147           0 :     if (&other != this)
     148             :     {
     149           0 :         deinit();
     150           0 :         osAlgorithm = std::move(other.osAlgorithm);
     151           0 :         aosArguments = std::move(other.aosArguments);
     152           0 :         eInDT = other.eInDT;
     153           0 :         eOutDT = other.eOutDT;
     154           0 :         nInBands = other.nInBands;
     155           0 :         nOutBands = other.nOutBands;
     156           0 :         adfInNoData = std::move(other.adfInNoData);
     157           0 :         adfOutNoData = std::move(other.adfOutNoData);
     158           0 :         std::swap(pWorkingData, other.pWorkingData);
     159             :     }
     160           0 :     return *this;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                        VRTProcessedDataset()                         */
     165             : /************************************************************************/
     166             : 
     167             : /** Constructor */
     168          99 : VRTProcessedDataset::VRTProcessedDataset(int nXSize, int nYSize)
     169          99 :     : VRTDataset(nXSize, nYSize)
     170             : {
     171          99 : }
     172             : 
     173             : /************************************************************************/
     174             : /*                       ~VRTProcessedDataset()                         */
     175             : /************************************************************************/
     176             : 
     177         198 : VRTProcessedDataset::~VRTProcessedDataset()
     178             : 
     179             : {
     180          99 :     VRTProcessedDataset::FlushCache(true);
     181          99 :     VRTProcessedDataset::CloseDependentDatasets();
     182         198 : }
     183             : 
     184             : /************************************************************************/
     185             : /*                              XMLInit()                               */
     186             : /************************************************************************/
     187             : 
     188             : /** Instantiate object from XML tree */
     189          96 : CPLErr VRTProcessedDataset::XMLInit(const CPLXMLNode *psTree,
     190             :                                     const char *pszVRTPathIn)
     191             : 
     192             : {
     193          96 :     if (Init(psTree, pszVRTPathIn, nullptr, nullptr, -1) != CE_None)
     194          43 :         return CE_Failure;
     195             : 
     196          53 :     const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
     197          53 :     const int nOvrCount = poSrcFirstBand->GetOverviewCount();
     198          56 :     for (int i = 0; i < nOvrCount; ++i)
     199             :     {
     200           3 :         auto poOvrDS = std::make_unique<VRTProcessedDataset>(0, 0);
     201           3 :         if (poOvrDS->Init(psTree, pszVRTPathIn, this, m_poSrcDS.get(), i) !=
     202             :             CE_None)
     203           0 :             break;
     204           3 :         m_apoOverviewDatasets.emplace_back(std::move(poOvrDS));
     205             :     }
     206             : 
     207          53 :     return CE_None;
     208             : }
     209             : 
     210          84 : static bool HasScaleOffset(GDALDataset &oSrcDS)
     211             : {
     212         372 :     for (int i = 1; i <= oSrcDS.GetRasterCount(); i++)
     213             :     {
     214             :         int pbSuccess;
     215         290 :         GDALRasterBand &oBand = *oSrcDS.GetRasterBand(i);
     216         290 :         double scale = oBand.GetScale(&pbSuccess);
     217         290 :         if (pbSuccess && scale != 1)
     218             :         {
     219           2 :             return true;
     220             :         }
     221         288 :         double offset = oBand.GetOffset(&pbSuccess);
     222         288 :         if (pbSuccess && offset != 0)
     223             :         {
     224           0 :             return true;
     225             :         }
     226             :     }
     227             : 
     228          82 :     return false;
     229             : }
     230             : 
     231             : /** Instantiate object from XML tree */
     232          99 : CPLErr VRTProcessedDataset::Init(const CPLXMLNode *psTree,
     233             :                                  const char *pszVRTPathIn,
     234             :                                  const VRTProcessedDataset *poParentDS,
     235             :                                  GDALDataset *poParentSrcDS, int iOvrLevel)
     236             : 
     237             : {
     238          99 :     const CPLXMLNode *psInput = CPLGetXMLNode(psTree, "Input");
     239          99 :     if (!psInput)
     240             :     {
     241           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Input element missing");
     242           1 :         return CE_Failure;
     243             :     }
     244             : 
     245          98 :     if (pszVRTPathIn)
     246          12 :         m_osVRTPath = pszVRTPathIn;
     247             : 
     248          98 :     if (poParentSrcDS)
     249             :     {
     250           3 :         m_poSrcDS.reset(
     251             :             GDALCreateOverviewDataset(poParentSrcDS, iOvrLevel, true));
     252             :     }
     253          95 :     else if (const CPLXMLNode *psSourceFileNameNode =
     254          95 :                  CPLGetXMLNode(psInput, "SourceFilename"))
     255             :     {
     256          93 :         const bool bRelativeToVRT = CPL_TO_BOOL(
     257             :             atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0")));
     258             :         const std::string osFilename = GDALDataset::BuildFilename(
     259             :             CPLGetXMLValue(psInput, "SourceFilename", ""), pszVRTPathIn,
     260         186 :             bRelativeToVRT);
     261          93 :         m_poSrcDS.reset(GDALDataset::Open(
     262             :             osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
     263             :             nullptr, nullptr));
     264             :     }
     265           2 :     else if (const CPLXMLNode *psVRTDataset =
     266           2 :                  CPLGetXMLNode(psInput, "VRTDataset"))
     267             :     {
     268           1 :         CPLXMLNode sVRTDatasetTmp = *psVRTDataset;
     269           1 :         sVRTDatasetTmp.psNext = nullptr;
     270           1 :         char *pszXML = CPLSerializeXMLTree(&sVRTDatasetTmp);
     271           1 :         m_poSrcDS = VRTDataset::OpenXML(pszXML, pszVRTPathIn, GA_ReadOnly);
     272           1 :         CPLFree(pszXML);
     273             :     }
     274             :     else
     275             :     {
     276           1 :         CPLError(
     277             :             CE_Failure, CPLE_AppDefined,
     278             :             "Input element should have a SourceFilename or VRTDataset element");
     279           1 :         return CE_Failure;
     280             :     }
     281             : 
     282          97 :     if (!m_poSrcDS)
     283           2 :         return CE_Failure;
     284             : 
     285          95 :     const char *pszUnscale = CPLGetXMLValue(psInput, "unscale", "AUTO");
     286          95 :     bool bUnscale = false;
     287          95 :     if (EQUAL(pszUnscale, "AUTO"))
     288             :     {
     289          84 :         if (HasScaleOffset(*m_poSrcDS))
     290             :         {
     291           2 :             bUnscale = true;
     292             :         }
     293             :     }
     294          11 :     else if (EQUAL(pszUnscale, "YES") || EQUAL(pszUnscale, "ON") ||
     295          11 :              EQUAL(pszUnscale, "TRUE") || EQUAL(pszUnscale, "1"))
     296             :     {
     297           6 :         bUnscale = true;
     298             :     }
     299           5 :     else if (!(EQUAL(pszUnscale, "NO") || EQUAL(pszUnscale, "OFF") ||
     300           5 :                EQUAL(pszUnscale, "FALSE") || EQUAL(pszUnscale, "0")))
     301             :     {
     302           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value of 'unscale'");
     303           1 :         return CE_Failure;
     304             :     }
     305             : 
     306          94 :     if (bUnscale)
     307             :     {
     308           8 :         CPLStringList oArgs;
     309           8 :         oArgs.AddString("-unscale");
     310           8 :         oArgs.AddString("-ot");
     311           8 :         oArgs.AddString("Float64");
     312           8 :         oArgs.AddString("-of");
     313           8 :         oArgs.AddString("VRT");
     314           8 :         oArgs.AddString("-a_nodata");
     315           8 :         oArgs.AddString("nan");
     316           8 :         auto *poArgs = GDALTranslateOptionsNew(oArgs.List(), nullptr);
     317             :         int pbUsageError;
     318           8 :         CPLAssert(poArgs);
     319           8 :         m_poVRTSrcDS.reset(m_poSrcDS.release());
     320             :         // https://trac.cppcheck.net/ticket/11325
     321             :         // cppcheck-suppress accessMoved
     322           8 :         m_poSrcDS.reset(GDALDataset::FromHandle(
     323           8 :             GDALTranslate("", m_poVRTSrcDS.get(), poArgs, &pbUsageError)));
     324           8 :         GDALTranslateOptionsFree(poArgs);
     325             : 
     326           8 :         if (pbUsageError || !m_poSrcDS)
     327             :         {
     328           0 :             return CE_Failure;
     329             :         }
     330             :     }
     331             : 
     332          94 :     if (nRasterXSize == 0 && nRasterYSize == 0)
     333             :     {
     334          92 :         nRasterXSize = m_poSrcDS->GetRasterXSize();
     335          92 :         nRasterYSize = m_poSrcDS->GetRasterYSize();
     336             :     }
     337           2 :     else if (nRasterXSize != m_poSrcDS->GetRasterXSize() ||
     338           0 :              nRasterYSize != m_poSrcDS->GetRasterYSize())
     339             :     {
     340           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     341             :                  "Inconsistent declared VRT dimensions with input dataset");
     342           2 :         return CE_Failure;
     343             :     }
     344             : 
     345          92 :     if (m_poSrcDS->GetRasterCount() == 0)
     346           0 :         return CE_Failure;
     347             : 
     348             :     // Inherit SRS from source if not explicitly defined in VRT
     349          92 :     if (!CPLGetXMLNode(psTree, "SRS"))
     350             :     {
     351          92 :         const OGRSpatialReference *poSRS = m_poSrcDS->GetSpatialRef();
     352          92 :         if (poSRS)
     353             :         {
     354          10 :             m_poSRS.reset(poSRS->Clone());
     355             :         }
     356             :     }
     357             : 
     358             :     // Inherit GeoTransform from source if not explicitly defined in VRT
     359          92 :     if (iOvrLevel < 0 && !CPLGetXMLNode(psTree, "GeoTransform"))
     360             :     {
     361          89 :         if (m_poSrcDS->GetGeoTransform(m_gt) == CE_None)
     362          52 :             m_bGeoTransformSet = true;
     363             :     }
     364             : 
     365             :     /* -------------------------------------------------------------------- */
     366             :     /*      Initialize blocksize before calling sub-init so that the        */
     367             :     /*      band initializers can get it from the dataset object when       */
     368             :     /*      they are created.                                               */
     369             :     /* -------------------------------------------------------------------- */
     370             : 
     371          92 :     const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
     372          92 :     poSrcFirstBand->GetBlockSize(&m_nBlockXSize, &m_nBlockYSize);
     373          92 :     bool bUserBlockSize = false;
     374          92 :     if (const char *pszBlockXSize =
     375          92 :             CPLGetXMLValue(psTree, "BlockXSize", nullptr))
     376             :     {
     377           0 :         bUserBlockSize = true;
     378           0 :         m_nBlockXSize = atoi(pszBlockXSize);
     379           0 :         if (m_nBlockXSize <= 1)
     380             :         {
     381           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockXSize");
     382           0 :             return CE_Failure;
     383             :         }
     384             :     }
     385          92 :     if (const char *pszBlockYSize =
     386          92 :             CPLGetXMLValue(psTree, "BlockYSize", nullptr))
     387             :     {
     388           0 :         bUserBlockSize = true;
     389           0 :         m_nBlockYSize = atoi(pszBlockYSize);
     390           0 :         if (m_nBlockYSize <= 1)
     391             :         {
     392           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid BlockYSize");
     393           0 :             return CE_Failure;
     394             :         }
     395             :     }
     396             : 
     397             :     // Initialize all the general VRT stuff.
     398          92 :     if (VRTDataset::XMLInit(psTree, pszVRTPathIn) != CE_None)
     399             :     {
     400           0 :         return CE_Failure;
     401             :     }
     402             : 
     403             :     // Use geotransform from parent for overviews
     404          92 :     if (iOvrLevel >= 0 && poParentDS->m_bGeoTransformSet)
     405             :     {
     406           1 :         m_bGeoTransformSet = true;
     407           1 :         m_gt = poParentDS->m_gt;
     408           2 :         m_gt[1] *=
     409           1 :             static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
     410           2 :         m_gt[2] *=
     411           1 :             static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
     412           2 :         m_gt[4] *=
     413           1 :             static_cast<double>(poParentDS->GetRasterXSize()) / nRasterXSize;
     414           1 :         m_gt[5] *=
     415           1 :             static_cast<double>(poParentDS->GetRasterYSize()) / nRasterYSize;
     416             :     }
     417             : 
     418          92 :     const CPLXMLNode *psOutputBands = CPLGetXMLNode(psTree, "OutputBands");
     419          92 :     if (psOutputBands)
     420             :     {
     421          14 :         if (const char *pszCount =
     422          14 :                 CPLGetXMLValue(psOutputBands, "count", nullptr))
     423             :         {
     424          14 :             if (EQUAL(pszCount, "FROM_LAST_STEP"))
     425             :             {
     426           8 :                 m_outputBandCountProvenance = ValueProvenance::FROM_LAST_STEP;
     427             :             }
     428           6 :             else if (!EQUAL(pszCount, "FROM_SOURCE"))
     429             :             {
     430           4 :                 if (CPLGetValueType(pszCount) == CPL_VALUE_INTEGER)
     431             :                 {
     432           3 :                     m_outputBandCountProvenance =
     433             :                         ValueProvenance::USER_PROVIDED;
     434           3 :                     m_outputBandCountValue = atoi(pszCount);
     435           3 :                     if (!GDALCheckBandCount(m_outputBandCountValue,
     436             :                                             /* bIsZeroAllowed = */ false))
     437             :                     {
     438             :                         // Error emitted by above function
     439           1 :                         return CE_Failure;
     440             :                     }
     441             :                 }
     442             :                 else
     443             :                 {
     444           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     445             :                              "Invalid value for OutputBands.count");
     446           1 :                     return CE_Failure;
     447             :                 }
     448             :             }
     449             :         }
     450             : 
     451          12 :         if (const char *pszType =
     452          12 :                 CPLGetXMLValue(psOutputBands, "dataType", nullptr))
     453             :         {
     454          12 :             if (EQUAL(pszType, "FROM_LAST_STEP"))
     455             :             {
     456           4 :                 m_outputBandDataTypeProvenance =
     457             :                     ValueProvenance::FROM_LAST_STEP;
     458             :             }
     459           8 :             else if (!EQUAL(pszType, "FROM_SOURCE"))
     460             :             {
     461           6 :                 m_outputBandDataTypeProvenance = ValueProvenance::USER_PROVIDED;
     462           6 :                 m_outputBandDataTypeValue = GDALGetDataTypeByName(pszType);
     463           6 :                 if (m_outputBandDataTypeValue == GDT_Unknown)
     464             :                 {
     465           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     466             :                              "Invalid value for OutputBands.dataType");
     467           1 :                     return CE_Failure;
     468             :                 }
     469             :             }
     470             :         }
     471             :     }
     472          78 :     else if (CPLGetXMLNode(psTree, "VRTRasterBand"))
     473             :     {
     474          17 :         m_outputBandCountProvenance = ValueProvenance::FROM_VRTRASTERBAND;
     475          17 :         m_outputBandDataTypeProvenance = ValueProvenance::FROM_VRTRASTERBAND;
     476             :     }
     477             : 
     478          89 :     int nOutputBandCount = 0;
     479          89 :     switch (m_outputBandCountProvenance)
     480             :     {
     481           1 :         case ValueProvenance::USER_PROVIDED:
     482           1 :             nOutputBandCount = m_outputBandCountValue;
     483           1 :             break;
     484          63 :         case ValueProvenance::FROM_SOURCE:
     485          63 :             nOutputBandCount = m_poSrcDS->GetRasterCount();
     486          63 :             break;
     487          17 :         case ValueProvenance::FROM_VRTRASTERBAND:
     488          17 :             nOutputBandCount = nBands;
     489          17 :             break;
     490           8 :         case ValueProvenance::FROM_LAST_STEP:
     491           8 :             break;
     492             :     }
     493             : 
     494             :     const CPLXMLNode *psProcessingSteps =
     495          89 :         CPLGetXMLNode(psTree, "ProcessingSteps");
     496          89 :     if (!psProcessingSteps)
     497             :     {
     498           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     499             :                  "ProcessingSteps element missing");
     500           1 :         return CE_Failure;
     501             :     }
     502             : 
     503          88 :     const auto eInDT = poSrcFirstBand->GetRasterDataType();
     504         298 :     for (int i = 1; i < m_poSrcDS->GetRasterCount(); ++i)
     505             :     {
     506         210 :         const auto eDT = m_poSrcDS->GetRasterBand(i + 1)->GetRasterDataType();
     507         210 :         if (eDT != eInDT)
     508             :         {
     509           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     510             :                      "Not all bands of the input dataset have the same data "
     511             :                      "type. The data type of the first band will be used as "
     512             :                      "the reference one.");
     513           0 :             break;
     514             :         }
     515             :     }
     516             : 
     517          88 :     GDALDataType eCurrentDT = eInDT;
     518          88 :     int nCurrentBandCount = m_poSrcDS->GetRasterCount();
     519             : 
     520         176 :     std::vector<double> adfNoData;
     521         386 :     for (int i = 1; i <= nCurrentBandCount; ++i)
     522             :     {
     523         298 :         int bHasVal = FALSE;
     524             :         const double dfVal =
     525         298 :             m_poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasVal);
     526             :         adfNoData.emplace_back(
     527         298 :             bHasVal ? dfVal : std::numeric_limits<double>::quiet_NaN());
     528             :     }
     529             : 
     530          88 :     int nStepCount = 0;
     531         177 :     for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
     532          89 :          psStep = psStep->psNext)
     533             :     {
     534          89 :         if (psStep->eType == CXT_Element &&
     535          89 :             strcmp(psStep->pszValue, "Step") == 0)
     536             :         {
     537          89 :             ++nStepCount;
     538             :         }
     539             :     }
     540             : 
     541          88 :     int iStep = 0;
     542         146 :     for (const CPLXMLNode *psStep = psProcessingSteps->psChild; psStep;
     543          58 :          psStep = psStep->psNext)
     544             :     {
     545          89 :         if (psStep->eType == CXT_Element &&
     546          89 :             strcmp(psStep->pszValue, "Step") == 0)
     547             :         {
     548          89 :             ++iStep;
     549          89 :             const bool bIsFinalStep = (iStep == nStepCount);
     550          89 :             std::vector<double> adfOutNoData;
     551          89 :             if (bIsFinalStep)
     552             :             {
     553             :                 // Initialize adfOutNoData with nodata value of *output* bands
     554             :                 // for final step
     555          87 :                 if (m_outputBandCountProvenance ==
     556             :                     ValueProvenance::FROM_VRTRASTERBAND)
     557             :                 {
     558          48 :                     for (int i = 1; i <= nBands; ++i)
     559             :                     {
     560          31 :                         int bHasVal = FALSE;
     561             :                         const double dfVal =
     562          31 :                             GetRasterBand(i)->GetNoDataValue(&bHasVal);
     563             :                         adfOutNoData.emplace_back(
     564          31 :                             bHasVal ? dfVal
     565          31 :                                     : std::numeric_limits<double>::quiet_NaN());
     566             :                     }
     567             :                 }
     568          70 :                 else if (m_outputBandCountProvenance ==
     569             :                          ValueProvenance::FROM_SOURCE)
     570             :                 {
     571         213 :                     for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
     572             :                     {
     573         152 :                         int bHasVal = FALSE;
     574             :                         const double dfVal =
     575         152 :                             m_poSrcDS->GetRasterBand(i)->GetNoDataValue(
     576         152 :                                 &bHasVal);
     577             :                         adfOutNoData.emplace_back(
     578         182 :                             bHasVal ? dfVal
     579         182 :                                     : std::numeric_limits<double>::quiet_NaN());
     580             :                     }
     581             :                 }
     582           9 :                 else if (m_outputBandCountProvenance ==
     583             :                          ValueProvenance::USER_PROVIDED)
     584             :                 {
     585           1 :                     adfOutNoData.resize(
     586           1 :                         m_outputBandCountValue,
     587           1 :                         std::numeric_limits<double>::quiet_NaN());
     588             :                 }
     589             :             }
     590          89 :             if (!ParseStep(psStep, bIsFinalStep, eCurrentDT, nCurrentBandCount,
     591             :                            adfNoData, adfOutNoData))
     592          31 :                 return CE_Failure;
     593          58 :             adfNoData = std::move(adfOutNoData);
     594             :         }
     595             :     }
     596             : 
     597          57 :     if (m_aoSteps.empty())
     598             :     {
     599           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     600             :                  "At least one step should be defined");
     601           1 :         return CE_Failure;
     602             :     }
     603             : 
     604          56 :     int nLargestInDTSizeTimesBand = 1;
     605          56 :     int nLargestOutDTSizeTimesBand = 1;
     606         114 :     for (const auto &oStep : m_aoSteps)
     607             :     {
     608             :         const int nInDTSizeTimesBand =
     609          58 :             GDALGetDataTypeSizeBytes(oStep.eInDT) * oStep.nInBands;
     610          58 :         nLargestInDTSizeTimesBand =
     611          58 :             std::max(nLargestInDTSizeTimesBand, nInDTSizeTimesBand);
     612             :         const int nOutDTSizeTimesBand =
     613          58 :             GDALGetDataTypeSizeBytes(oStep.eOutDT) * oStep.nOutBands;
     614          58 :         nLargestOutDTSizeTimesBand =
     615          58 :             std::max(nLargestOutDTSizeTimesBand, nOutDTSizeTimesBand);
     616             :     }
     617          56 :     m_nWorkingBytesPerPixel =
     618          56 :         nLargestInDTSizeTimesBand + nLargestOutDTSizeTimesBand;
     619             : 
     620             :     // Use only up to 40% of RAM to acquire source bands and generate the output
     621             :     // buffer.
     622          56 :     m_nAllowedRAMUsage = CPLGetUsablePhysicalRAM() / 10 * 4;
     623             :     // Only for tests now
     624          56 :     const char *pszMAX_RAM = "VRT_PROCESSED_DATASET_ALLOWED_RAM_USAGE";
     625          56 :     if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
     626             :     {
     627           3 :         CPL_IGNORE_RET_VAL(
     628           3 :             CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
     629             :     }
     630             : 
     631          56 :     if (m_nAllowedRAMUsage > 0)
     632             :     {
     633          56 :         bool bBlockSizeModified = false;
     634          88 :         while ((m_nBlockXSize >= 2 || m_nBlockYSize >= 2) &&
     635          81 :                static_cast<GIntBig>(m_nBlockXSize) * m_nBlockYSize >
     636          81 :                    m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
     637             :         {
     638          32 :             if ((m_nBlockXSize == nRasterXSize ||
     639          30 :                  m_nBlockYSize >= m_nBlockXSize) &&
     640          17 :                 m_nBlockYSize >= 2)
     641             :             {
     642          16 :                 m_nBlockYSize /= 2;
     643             :             }
     644             :             else
     645             :             {
     646          16 :                 m_nBlockXSize /= 2;
     647             :             }
     648          32 :             bBlockSizeModified = true;
     649             :         }
     650          56 :         if (bBlockSizeModified)
     651             :         {
     652           3 :             if (bUserBlockSize)
     653             :             {
     654           0 :                 CPLError(
     655             :                     CE_Warning, CPLE_AppDefined,
     656             :                     "Reducing block size to %d x %d to avoid consuming too "
     657             :                     "much RAM",
     658             :                     m_nBlockXSize, m_nBlockYSize);
     659             :             }
     660             :             else
     661             :             {
     662           3 :                 CPLDebug(
     663             :                     "VRT",
     664             :                     "Reducing block size to %d x %d to avoid consuming too "
     665             :                     "much RAM",
     666             :                     m_nBlockXSize, m_nBlockYSize);
     667             :             }
     668             :         }
     669             :     }
     670             : 
     671          56 :     if (m_outputBandCountProvenance == ValueProvenance::FROM_LAST_STEP)
     672             :     {
     673           7 :         nOutputBandCount = nCurrentBandCount;
     674             :     }
     675          49 :     else if (nOutputBandCount != nCurrentBandCount)
     676             :     {
     677             :         // Should not happen frequently as pixel init functions are expected
     678             :         // to validate that they can accept the number of output bands provided
     679             :         // to them
     680           0 :         CPLError(
     681             :             CE_Failure, CPLE_AppDefined,
     682             :             "Number of output bands of last step (%d) is not consistent with "
     683             :             "number of VRTProcessedRasterBand's (%d)",
     684             :             nCurrentBandCount, nBands);
     685           0 :         return CE_Failure;
     686             :     }
     687             : 
     688          56 :     if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
     689             :     {
     690           3 :         m_outputBandDataTypeValue = eCurrentDT;
     691             :     }
     692             : 
     693          34 :     const auto ClearBands = [this]()
     694             :     {
     695           9 :         for (int i = 0; i < nBands; ++i)
     696           1 :             delete papoBands[i];
     697           8 :         CPLFree(papoBands);
     698           8 :         papoBands = nullptr;
     699           8 :         nBands = 0;
     700          64 :     };
     701             : 
     702          73 :     if (nBands != 0 &&
     703          17 :         (nBands != nOutputBandCount ||
     704          16 :          (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP &&
     705           1 :           m_outputBandDataTypeValue != papoBands[0]->GetRasterDataType())))
     706             :     {
     707           1 :         ClearBands();
     708             :     }
     709             : 
     710         176 :     const auto GetOutputBandType = [this, eCurrentDT](GDALDataType eSourceDT)
     711             :     {
     712          87 :         if (m_outputBandDataTypeProvenance == ValueProvenance::FROM_LAST_STEP)
     713           3 :             return eCurrentDT;
     714          84 :         else if (m_outputBandDataTypeProvenance ==
     715             :                  ValueProvenance::USER_PROVIDED)
     716           5 :             return m_outputBandDataTypeValue;
     717             :         else
     718          79 :             return eSourceDT;
     719          56 :     };
     720             : 
     721          56 :     if (m_outputBandCountProvenance == ValueProvenance::FROM_SOURCE)
     722             :     {
     723         112 :         for (int i = 0; i < m_poSrcDS->GetRasterCount(); ++i)
     724             :         {
     725          79 :             const auto poSrcBand = m_poSrcDS->GetRasterBand(i + 1);
     726             :             const GDALDataType eOutputBandType =
     727          79 :                 GetOutputBandType(poSrcBand->GetRasterDataType());
     728             :             auto poBand =
     729          79 :                 new VRTProcessedRasterBand(this, i + 1, eOutputBandType);
     730          79 :             poBand->CopyCommonInfoFrom(poSrcBand);
     731          79 :             SetBand(i + 1, poBand);
     732             :         }
     733             :     }
     734          23 :     else if (m_outputBandCountProvenance != ValueProvenance::FROM_VRTRASTERBAND)
     735             :     {
     736           8 :         const GDALDataType eOutputBandType = GetOutputBandType(eInDT);
     737             : 
     738           8 :         bool bClearAndSetBands = true;
     739           8 :         if (nBands == nOutputBandCount)
     740             :         {
     741           1 :             bClearAndSetBands = false;
     742           3 :             for (int i = 0; i < nBands; ++i)
     743             :             {
     744           2 :                 bClearAndSetBands =
     745           2 :                     bClearAndSetBands ||
     746           4 :                     !dynamic_cast<VRTProcessedRasterBand *>(papoBands[i]) ||
     747           2 :                     papoBands[i]->GetRasterDataType() != eOutputBandType;
     748             :             }
     749             :         }
     750           8 :         if (bClearAndSetBands)
     751             :         {
     752           7 :             ClearBands();
     753          32 :             for (int i = 0; i < nOutputBandCount; ++i)
     754             :             {
     755          25 :                 SetBand(i + 1, std::make_unique<VRTProcessedRasterBand>(
     756          50 :                                    this, i + 1, eOutputBandType));
     757             :             }
     758             :         }
     759             :     }
     760             : 
     761          56 :     if (nBands > 1)
     762          41 :         SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     763             : 
     764          56 :     m_oXMLTree.reset(CPLCloneXMLTree(psTree));
     765             : 
     766          56 :     return CE_None;
     767             : }
     768             : 
     769             : /************************************************************************/
     770             : /*                            ParseStep()                               */
     771             : /************************************************************************/
     772             : 
     773             : /** Parse the current Step node and create a corresponding entry in m_aoSteps.
     774             :  *
     775             :  * @param psStep Step node
     776             :  * @param bIsFinalStep Whether this is the final step.
     777             :  * @param[in,out] eCurrentDT Input data type for this step.
     778             :  *                           Updated to output data type at end of method.
     779             :  * @param[in,out] nCurrentBandCount Input band count for this step.
     780             :  *                                  Updated to output band cout at end of
     781             :  *                                  method.
     782             :  * @param adfInNoData Input nodata values
     783             :  * @param[in,out] adfOutNoData Output nodata values, to be filled by this
     784             :  *                             method. When bIsFinalStep, this is also an
     785             :  *                             input parameter.
     786             :  * @return true on success.
     787             :  */
     788          89 : bool VRTProcessedDataset::ParseStep(const CPLXMLNode *psStep, bool bIsFinalStep,
     789             :                                     GDALDataType &eCurrentDT,
     790             :                                     int &nCurrentBandCount,
     791             :                                     std::vector<double> &adfInNoData,
     792             :                                     std::vector<double> &adfOutNoData)
     793             : {
     794          89 :     const char *pszStepName = CPLGetXMLValue(
     795          89 :         psStep, "name", CPLSPrintf("nr %d", 1 + int(m_aoSteps.size())));
     796          89 :     const char *pszAlgorithm = CPLGetXMLValue(psStep, "Algorithm", nullptr);
     797          89 :     if (!pszAlgorithm)
     798             :     {
     799           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     800             :                  "Step '%s' lacks a Algorithm element", pszStepName);
     801           0 :         return false;
     802             :     }
     803             : 
     804          89 :     const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
     805          89 :     const auto oIterFunc = oMapFunctions.find(pszAlgorithm);
     806          89 :     if (oIterFunc == oMapFunctions.end())
     807             :     {
     808           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     809             :                  "Step '%s' uses unregistered algorithm '%s'", pszStepName,
     810             :                  pszAlgorithm);
     811           0 :         return false;
     812             :     }
     813             : 
     814          89 :     const auto &oFunc = oIterFunc->second;
     815             : 
     816          89 :     if (!oFunc.aeSupportedInputDT.empty())
     817             :     {
     818           0 :         if (std::find(oFunc.aeSupportedInputDT.begin(),
     819             :                       oFunc.aeSupportedInputDT.end(),
     820           0 :                       eCurrentDT) == oFunc.aeSupportedInputDT.end())
     821             :         {
     822           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     823             :                      "Step '%s' (using algorithm '%s') does not "
     824             :                      "support input data type = '%s'",
     825             :                      pszStepName, pszAlgorithm,
     826             :                      GDALGetDataTypeName(eCurrentDT));
     827           0 :             return false;
     828             :         }
     829             :     }
     830             : 
     831          89 :     if (!oFunc.anSupportedInputBandCount.empty())
     832             :     {
     833           0 :         if (std::find(oFunc.anSupportedInputBandCount.begin(),
     834             :                       oFunc.anSupportedInputBandCount.end(),
     835           0 :                       nCurrentBandCount) ==
     836           0 :             oFunc.anSupportedInputBandCount.end())
     837             :         {
     838           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     839             :                      "Step '%s' (using algorithm '%s') does not "
     840             :                      "support input band count = %d",
     841             :                      pszStepName, pszAlgorithm, nCurrentBandCount);
     842           0 :             return false;
     843             :         }
     844             :     }
     845             : 
     846         178 :     Step oStep;
     847          89 :     oStep.osAlgorithm = pszAlgorithm;
     848         178 :     oStep.eInDT = oFunc.eRequestedInputDT != GDT_Unknown
     849          89 :                       ? oFunc.eRequestedInputDT
     850             :                       : eCurrentDT;
     851          89 :     oStep.nInBands = nCurrentBandCount;
     852             : 
     853             :     // Unless modified by pfnInit...
     854          89 :     oStep.eOutDT = oStep.eInDT;
     855             : 
     856          89 :     oStep.adfInNoData = adfInNoData;
     857          89 :     oStep.adfOutNoData = bIsFinalStep ? adfOutNoData : adfInNoData;
     858             : 
     859             :     // Deal with constant arguments
     860          89 :     for (const auto &nameValuePair : oFunc.oMapConstantArguments)
     861             :     {
     862             :         oStep.aosArguments.AddNameValue(nameValuePair.first.c_str(),
     863           0 :                                         nameValuePair.second.c_str());
     864             :     }
     865             : 
     866             :     // Deal with built-in arguments
     867          89 :     if (oFunc.oSetBuiltinArguments.find("nodata") !=
     868         178 :         oFunc.oSetBuiltinArguments.end())
     869             :     {
     870           0 :         int bHasVal = false;
     871           0 :         const auto poSrcFirstBand = m_poSrcDS->GetRasterBand(1);
     872           0 :         const double dfVal = poSrcFirstBand->GetNoDataValue(&bHasVal);
     873           0 :         if (bHasVal)
     874             :         {
     875             :             oStep.aosArguments.AddNameValue("nodata",
     876           0 :                                             CPLSPrintf("%.17g", dfVal));
     877             :         }
     878             :     }
     879             : 
     880          89 :     if (oFunc.oSetBuiltinArguments.find("offset_{band}") !=
     881         178 :         oFunc.oSetBuiltinArguments.end())
     882             :     {
     883           0 :         for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
     884             :         {
     885           0 :             int bHasVal = false;
     886           0 :             const double dfVal = GetRasterBand(i)->GetOffset(&bHasVal);
     887             :             oStep.aosArguments.AddNameValue(
     888             :                 CPLSPrintf("offset_%d", i),
     889           0 :                 CPLSPrintf("%.17g", bHasVal ? dfVal : 0.0));
     890             :         }
     891             :     }
     892             : 
     893          89 :     if (oFunc.oSetBuiltinArguments.find("scale_{band}") !=
     894         178 :         oFunc.oSetBuiltinArguments.end())
     895             :     {
     896           0 :         for (int i = 1; i <= m_poSrcDS->GetRasterCount(); ++i)
     897             :         {
     898           0 :             int bHasVal = false;
     899           0 :             const double dfVal = GetRasterBand(i)->GetScale(&bHasVal);
     900             :             oStep.aosArguments.AddNameValue(
     901             :                 CPLSPrintf("scale_%d", i),
     902           0 :                 CPLSPrintf("%.17g", bHasVal ? dfVal : 1.0));
     903             :         }
     904             :     }
     905             : 
     906             :     // Parse arguments specified in VRT
     907         178 :     std::set<std::string> oFoundArguments;
     908             : 
     909         469 :     for (const CPLXMLNode *psStepChild = psStep->psChild; psStepChild;
     910         380 :          psStepChild = psStepChild->psNext)
     911             :     {
     912         380 :         if (psStepChild->eType == CXT_Element &&
     913         356 :             strcmp(psStepChild->pszValue, "Argument") == 0)
     914             :         {
     915             :             const char *pszParamName =
     916         267 :                 CPLGetXMLValue(psStepChild, "name", nullptr);
     917         267 :             if (!pszParamName)
     918             :             {
     919           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     920             :                          "Step '%s' has a Argument without a name attribute",
     921             :                          pszStepName);
     922           0 :                 return false;
     923             :             }
     924         267 :             const char *pszValue = CPLGetXMLValue(psStepChild, nullptr, "");
     925             :             auto oOtherArgIter =
     926         267 :                 oFunc.oOtherArguments.find(CPLString(pszParamName).tolower());
     927         534 :             if (!oFunc.oOtherArguments.empty() &&
     928         534 :                 oOtherArgIter == oFunc.oOtherArguments.end())
     929             :             {
     930             :                 // If we got a parameter name like 'coefficients_1',
     931             :                 // try to fetch the generic 'coefficients_{band}'
     932         296 :                 std::string osParamName(pszParamName);
     933         148 :                 const auto nPos = osParamName.rfind('_');
     934         148 :                 if (nPos != std::string::npos)
     935             :                 {
     936         148 :                     osParamName.resize(nPos + 1);
     937         148 :                     osParamName += "{band}";
     938             :                     oOtherArgIter = oFunc.oOtherArguments.find(
     939         148 :                         CPLString(osParamName).tolower());
     940             :                 }
     941             :             }
     942         267 :             if (oOtherArgIter != oFunc.oOtherArguments.end())
     943             :             {
     944         267 :                 oFoundArguments.insert(oOtherArgIter->first);
     945             : 
     946         267 :                 const std::string &osType = oOtherArgIter->second.osType;
     947         267 :                 if (osType == "boolean")
     948             :                 {
     949           0 :                     if (!EQUAL(pszValue, "true") && !EQUAL(pszValue, "false"))
     950             :                     {
     951           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
     952             :                                  "Step '%s' has a Argument '%s' whose "
     953             :                                  "value '%s' is not a boolean",
     954             :                                  pszStepName, pszParamName, pszValue);
     955           0 :                         return false;
     956             :                     }
     957             :                 }
     958         267 :                 else if (osType == "integer")
     959             :                 {
     960          44 :                     if (CPLGetValueType(pszValue) != CPL_VALUE_INTEGER)
     961             :                     {
     962           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
     963             :                                  "Step '%s' has a Argument '%s' whose "
     964             :                                  "value '%s' is not a integer",
     965             :                                  pszStepName, pszParamName, pszValue);
     966           0 :                         return false;
     967             :                     }
     968             :                 }
     969         223 :                 else if (osType == "double")
     970             :                 {
     971          49 :                     const auto eType = CPLGetValueType(pszValue);
     972          49 :                     if (eType != CPL_VALUE_INTEGER && eType != CPL_VALUE_REAL)
     973             :                     {
     974           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
     975             :                                  "Step '%s' has a Argument '%s' whose "
     976             :                                  "value '%s' is not a double",
     977             :                                  pszStepName, pszParamName, pszValue);
     978           0 :                         return false;
     979             :                     }
     980             :                 }
     981         174 :                 else if (osType == "double_list")
     982             :                 {
     983             :                     const CPLStringList aosTokens(
     984          88 :                         CSLTokenizeString2(pszValue, ",", 0));
     985         405 :                     for (int i = 0; i < aosTokens.size(); ++i)
     986             :                     {
     987         317 :                         const auto eType = CPLGetValueType(aosTokens[i]);
     988         317 :                         if (eType != CPL_VALUE_INTEGER &&
     989             :                             eType != CPL_VALUE_REAL)
     990             :                         {
     991           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
     992             :                                      "Step '%s' has a Argument '%s' "
     993             :                                      "whose value '%s' is not a "
     994             :                                      "comma-separated list of doubles",
     995             :                                      pszStepName, pszParamName, pszValue);
     996           0 :                             return false;
     997             :                         }
     998             :                     }
     999             :                 }
    1000          86 :                 else if (osType != "string")
    1001             :                 {
    1002           0 :                     CPLDebug("VRT", "Unhandled argument type '%s'",
    1003             :                              osType.c_str());
    1004           0 :                     CPLAssert(0);
    1005             :                 }
    1006             :             }
    1007           0 :             else if (oFunc.bMetadataSpecified &&
    1008           0 :                      oFunc.oSetBuiltinArguments.find(
    1009           0 :                          CPLString(pszParamName).tolower()) ==
    1010           0 :                          oFunc.oSetBuiltinArguments.end() &&
    1011           0 :                      oFunc.oMapConstantArguments.find(
    1012           0 :                          CPLString(pszParamName).tolower()) ==
    1013           0 :                          oFunc.oMapConstantArguments.end())
    1014             :             {
    1015           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    1016             :                          "Step '%s' has a Argument '%s' which is not "
    1017             :                          "supported",
    1018             :                          pszStepName, pszParamName);
    1019             :             }
    1020             : 
    1021         267 :             oStep.aosArguments.AddNameValue(pszParamName, pszValue);
    1022             :         }
    1023             :     }
    1024             : 
    1025             :     // Check that required arguments have been specified
    1026         667 :     for (const auto &oIter : oFunc.oOtherArguments)
    1027             :     {
    1028         741 :         if (oIter.second.bRequired &&
    1029         741 :             oFoundArguments.find(oIter.first) == oFoundArguments.end())
    1030             :         {
    1031           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    1032             :                      "Step '%s' lacks required Argument '%s'", pszStepName,
    1033             :                      oIter.first.c_str());
    1034           3 :             return false;
    1035             :         }
    1036             :     }
    1037             : 
    1038          86 :     if (oFunc.pfnInit)
    1039             :     {
    1040          86 :         double *padfOutNoData = nullptr;
    1041          86 :         if (bIsFinalStep && !adfOutNoData.empty())
    1042             :         {
    1043          76 :             oStep.nOutBands = static_cast<int>(adfOutNoData.size());
    1044          76 :             padfOutNoData = static_cast<double *>(
    1045          76 :                 CPLMalloc(adfOutNoData.size() * sizeof(double)));
    1046          76 :             memcpy(padfOutNoData, adfOutNoData.data(),
    1047          76 :                    adfOutNoData.size() * sizeof(double));
    1048             :         }
    1049             :         else
    1050             :         {
    1051          10 :             oStep.nOutBands = 0;
    1052             :         }
    1053             : 
    1054         172 :         if (oFunc.pfnInit(pszAlgorithm, oFunc.pUserData,
    1055          86 :                           oStep.aosArguments.List(), oStep.nInBands,
    1056             :                           oStep.eInDT, adfInNoData.data(), &(oStep.nOutBands),
    1057             :                           &(oStep.eOutDT), &padfOutNoData, m_osVRTPath.c_str(),
    1058          86 :                           &(oStep.pWorkingData)) != CE_None)
    1059             :         {
    1060          28 :             CPLError(CE_Failure, CPLE_AppDefined,
    1061             :                      "Step '%s' (using algorithm '%s') init() function "
    1062             :                      "failed",
    1063             :                      pszStepName, pszAlgorithm);
    1064          28 :             CPLFree(padfOutNoData);
    1065          28 :             return false;
    1066             :         }
    1067             : 
    1068             :         // Input nodata values may have been modified by pfnInit()
    1069          58 :         oStep.adfInNoData = adfInNoData;
    1070             : 
    1071          58 :         if (padfOutNoData)
    1072             :         {
    1073         108 :             adfOutNoData = std::vector<double>(padfOutNoData,
    1074          54 :                                                padfOutNoData + oStep.nOutBands);
    1075             :         }
    1076             :         else
    1077             :         {
    1078          12 :             adfOutNoData = std::vector<double>(
    1079           8 :                 oStep.nOutBands, std::numeric_limits<double>::quiet_NaN());
    1080             :         }
    1081          58 :         CPLFree(padfOutNoData);
    1082             : 
    1083          58 :         oStep.adfOutNoData = adfOutNoData;
    1084             :     }
    1085             :     else
    1086             :     {
    1087           0 :         oStep.nOutBands = oStep.nInBands;
    1088           0 :         adfOutNoData = oStep.adfOutNoData;
    1089             :     }
    1090             : 
    1091          58 :     eCurrentDT = oStep.eOutDT;
    1092          58 :     nCurrentBandCount = oStep.nOutBands;
    1093             : 
    1094          58 :     m_aoSteps.emplace_back(std::move(oStep));
    1095             : 
    1096          58 :     return true;
    1097             : }
    1098             : 
    1099             : /************************************************************************/
    1100             : /*                           SerializeToXML()                           */
    1101             : /************************************************************************/
    1102             : 
    1103           1 : CPLXMLNode *VRTProcessedDataset::SerializeToXML(const char *pszVRTPathIn)
    1104             : 
    1105             : {
    1106           1 :     CPLXMLNode *psTree = CPLCloneXMLTree(m_oXMLTree.get());
    1107           1 :     if (psTree == nullptr)
    1108           0 :         return psTree;
    1109             : 
    1110             :     /* -------------------------------------------------------------------- */
    1111             :     /*      Remove VRTRasterBand nodes from the original tree and find the  */
    1112             :     /*      last child.                                                     */
    1113             :     /* -------------------------------------------------------------------- */
    1114           1 :     CPLXMLNode *psLastChild = psTree->psChild;
    1115           1 :     CPLXMLNode *psPrevChild = nullptr;
    1116           5 :     while (psLastChild)
    1117             :     {
    1118           5 :         CPLXMLNode *psNextChild = psLastChild->psNext;
    1119           5 :         if (psLastChild->eType == CXT_Element &&
    1120           3 :             strcmp(psLastChild->pszValue, "VRTRasterBand") == 0)
    1121             :         {
    1122           1 :             if (psPrevChild)
    1123           1 :                 psPrevChild->psNext = psNextChild;
    1124             :             else
    1125           0 :                 psTree->psChild = psNextChild;
    1126           1 :             psLastChild->psNext = nullptr;
    1127           1 :             CPLDestroyXMLNode(psLastChild);
    1128           1 :             psLastChild = psPrevChild ? psPrevChild : psTree->psChild;
    1129             :         }
    1130           4 :         else if (!psNextChild)
    1131             :         {
    1132           1 :             break;
    1133             :         }
    1134             :         else
    1135             :         {
    1136           3 :             psPrevChild = psLastChild;
    1137           3 :             psLastChild = psNextChild;
    1138             :         }
    1139             :     }
    1140           1 :     CPLAssert(psLastChild);  // we have at least Input
    1141             : 
    1142             :     /* -------------------------------------------------------------------- */
    1143             :     /*      Serialize bands.                                                */
    1144             :     /* -------------------------------------------------------------------- */
    1145           1 :     bool bHasWarnedAboutRAMUsage = false;
    1146           1 :     size_t nAccRAMUsage = 0;
    1147           2 :     for (int iBand = 0; iBand < nBands; iBand++)
    1148             :     {
    1149             :         CPLXMLNode *psBandTree =
    1150           1 :             static_cast<VRTRasterBand *>(papoBands[iBand])
    1151           2 :                 ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
    1152           1 :                                  nAccRAMUsage);
    1153             : 
    1154           1 :         if (psBandTree != nullptr)
    1155             :         {
    1156           1 :             psLastChild->psNext = psBandTree;
    1157           1 :             psLastChild = psBandTree;
    1158             :         }
    1159             :     }
    1160             : 
    1161           1 :     return psTree;
    1162             : }
    1163             : 
    1164             : /************************************************************************/
    1165             : /*                           SerializeToXML()                           */
    1166             : /************************************************************************/
    1167             : 
    1168             : CPLXMLNode *
    1169           1 : VRTProcessedRasterBand::SerializeToXML(const char *pszVRTPathIn,
    1170             :                                        bool &bHasWarnedAboutRAMUsage,
    1171             :                                        size_t &nAccRAMUsage)
    1172             : 
    1173             : {
    1174           1 :     CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
    1175             :         pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
    1176             : 
    1177             :     /* -------------------------------------------------------------------- */
    1178             :     /*      Set subclass.                                                   */
    1179             :     /* -------------------------------------------------------------------- */
    1180           1 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    1181             :                      CXT_Text, "VRTProcessedRasterBand");
    1182             : 
    1183           1 :     return psTree;
    1184             : }
    1185             : 
    1186             : /************************************************************************/
    1187             : /*                            GetBlockSize()                            */
    1188             : /************************************************************************/
    1189             : 
    1190             : /** Return block size */
    1191         140 : void VRTProcessedDataset::GetBlockSize(int *pnBlockXSize,
    1192             :                                        int *pnBlockYSize) const
    1193             : 
    1194             : {
    1195         140 :     *pnBlockXSize = m_nBlockXSize;
    1196         140 :     *pnBlockYSize = m_nBlockYSize;
    1197         140 : }
    1198             : 
    1199             : /************************************************************************/
    1200             : /*                            ProcessRegion()                           */
    1201             : /************************************************************************/
    1202             : 
    1203             : /** Compute pixel values for the specified region.
    1204             :  *
    1205             :  * The output is stored in m_abyInput in a pixel-interleaved way.
    1206             :  */
    1207          70 : bool VRTProcessedDataset::ProcessRegion(int nXOff, int nYOff, int nBufXSize,
    1208             :                                         int nBufYSize,
    1209             :                                         GDALProgressFunc pfnProgress,
    1210             :                                         void *pProgressData)
    1211             : {
    1212             : 
    1213          70 :     CPLAssert(!m_aoSteps.empty());
    1214             : 
    1215          70 :     const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
    1216             : 
    1217          70 :     const int nFirstBandCount = m_aoSteps.front().nInBands;
    1218          70 :     CPLAssert(nFirstBandCount == m_poSrcDS->GetRasterCount());
    1219          70 :     const GDALDataType eFirstDT = m_aoSteps.front().eInDT;
    1220          70 :     const int nFirstDTSize = GDALGetDataTypeSizeBytes(eFirstDT);
    1221          70 :     auto &abyInput = m_abyInput;
    1222          70 :     auto &abyOutput = m_abyOutput;
    1223             : 
    1224             :     const char *pszInterleave =
    1225          70 :         m_poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
    1226          70 :     if (nFirstBandCount > 1 && (!pszInterleave || EQUAL(pszInterleave, "BAND")))
    1227             :     {
    1228             :         // If there are several bands and the source dataset organization
    1229             :         // is apparently band interleaved, then first acquire data in
    1230             :         // a BSQ organization in the abyInput array use in the native
    1231             :         // data type.
    1232             :         // And then transpose it and convert it to the expected data type
    1233             :         // of the first step.
    1234           1 :         const auto eSrcDT = m_poSrcDS->GetRasterBand(1)->GetRasterDataType();
    1235             :         try
    1236             :         {
    1237           2 :             abyInput.resize(nPixelCount * nFirstBandCount *
    1238           1 :                             GDALGetDataTypeSizeBytes(eSrcDT));
    1239             :         }
    1240           0 :         catch (const std::bad_alloc &)
    1241             :         {
    1242           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1243             :                      "Out of memory allocating working buffer");
    1244           0 :             return false;
    1245             :         }
    1246             : 
    1247             :         try
    1248             :         {
    1249           1 :             abyOutput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
    1250             :         }
    1251           0 :         catch (const std::bad_alloc &)
    1252             :         {
    1253           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1254             :                      "Out of memory allocating working buffer");
    1255           0 :             return false;
    1256             :         }
    1257             : 
    1258             :         GDALRasterIOExtraArg sArg;
    1259           1 :         INIT_RASTERIO_EXTRA_ARG(sArg);
    1260           1 :         sArg.pfnProgress = GDALScaledProgress;
    1261           1 :         sArg.pProgressData =
    1262           1 :             GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
    1263           1 :         if (sArg.pProgressData == nullptr)
    1264           1 :             sArg.pfnProgress = nullptr;
    1265             : 
    1266           1 :         CPLDebugOnly("VRT", "ProcessRegion(): start RasterIO()");
    1267             :         const bool bOK =
    1268           2 :             m_poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nBufXSize, nBufYSize,
    1269           1 :                                 abyInput.data(), nBufXSize, nBufYSize, eSrcDT,
    1270             :                                 nFirstBandCount, nullptr, 0, 0, 0,
    1271           1 :                                 &sArg) == CE_None;
    1272           1 :         CPLDebugOnly("VRT", "ProcessRegion(): end RasterIO()");
    1273           1 :         GDALDestroyScaledProgress(sArg.pProgressData);
    1274           1 :         if (!bOK)
    1275           0 :             return false;
    1276             : 
    1277           1 :         CPLDebugOnly("VRT", "ProcessRegion(): start GDALTranspose2D()");
    1278           1 :         GDALTranspose2D(abyInput.data(), eSrcDT, abyOutput.data(), eFirstDT,
    1279           1 :                         static_cast<size_t>(nBufXSize) * nBufYSize,
    1280             :                         nFirstBandCount);
    1281           1 :         CPLDebugOnly("VRT", "ProcessRegion(): end GDALTranspose2D()");
    1282             : 
    1283             :         // Swap arrays
    1284           1 :         std::swap(abyInput, abyOutput);
    1285             :     }
    1286             :     else
    1287             :     {
    1288             :         try
    1289             :         {
    1290          69 :             abyInput.resize(nPixelCount * nFirstBandCount * nFirstDTSize);
    1291             :         }
    1292           0 :         catch (const std::bad_alloc &)
    1293             :         {
    1294           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1295             :                      "Out of memory allocating working buffer");
    1296           0 :             return false;
    1297             :         }
    1298             : 
    1299             :         GDALRasterIOExtraArg sArg;
    1300          69 :         INIT_RASTERIO_EXTRA_ARG(sArg);
    1301          69 :         sArg.pfnProgress = GDALScaledProgress;
    1302          69 :         sArg.pProgressData =
    1303          69 :             GDALCreateScaledProgress(0, 0.5, pfnProgress, pProgressData);
    1304          69 :         if (sArg.pProgressData == nullptr)
    1305          69 :             sArg.pfnProgress = nullptr;
    1306             : 
    1307             :         const bool bOK =
    1308         138 :             m_poSrcDS->RasterIO(
    1309          69 :                 GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, abyInput.data(),
    1310             :                 nBufXSize, nBufYSize, eFirstDT, nFirstBandCount, nullptr,
    1311          69 :                 static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount,
    1312          69 :                 static_cast<GSpacing>(nFirstDTSize) * nFirstBandCount *
    1313          69 :                     nBufXSize,
    1314          69 :                 nFirstDTSize, &sArg) == CE_None;
    1315             : 
    1316          69 :         GDALDestroyScaledProgress(sArg.pProgressData);
    1317          69 :         if (!bOK)
    1318           3 :             return false;
    1319             :     }
    1320             : 
    1321          67 :     const double dfSrcXOff = nXOff;
    1322          67 :     const double dfSrcYOff = nYOff;
    1323          67 :     const double dfSrcXSize = nBufXSize;
    1324          67 :     const double dfSrcYSize = nBufYSize;
    1325             : 
    1326          67 :     GDALGeoTransform srcGT;
    1327          67 :     if (m_poSrcDS->GetGeoTransform(srcGT) != CE_None)
    1328             :     {
    1329          38 :         srcGT = GDALGeoTransform();
    1330             :     }
    1331             : 
    1332          67 :     GDALDataType eLastDT = eFirstDT;
    1333          67 :     const auto &oMapFunctions = GetGlobalMapProcessedDatasetFunc();
    1334             : 
    1335          67 :     int iStep = 0;
    1336         132 :     for (const auto &oStep : m_aoSteps)
    1337             :     {
    1338          69 :         const auto oIterFunc = oMapFunctions.find(oStep.osAlgorithm);
    1339          69 :         CPLAssert(oIterFunc != oMapFunctions.end());
    1340             : 
    1341             :         // Data type adaptation
    1342          69 :         if (eLastDT != oStep.eInDT)
    1343             :         {
    1344             :             try
    1345             :             {
    1346           0 :                 abyOutput.resize(nPixelCount * oStep.nInBands *
    1347           0 :                                  GDALGetDataTypeSizeBytes(oStep.eInDT));
    1348             :             }
    1349           0 :             catch (const std::bad_alloc &)
    1350             :             {
    1351           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    1352             :                          "Out of memory allocating working buffer");
    1353           0 :                 return false;
    1354             :             }
    1355             : 
    1356           0 :             GDALCopyWords64(abyInput.data(), eLastDT,
    1357           0 :                             GDALGetDataTypeSizeBytes(eLastDT), abyOutput.data(),
    1358           0 :                             oStep.eInDT, GDALGetDataTypeSizeBytes(oStep.eInDT),
    1359           0 :                             nPixelCount * oStep.nInBands);
    1360             : 
    1361           0 :             std::swap(abyInput, abyOutput);
    1362             :         }
    1363             : 
    1364             :         try
    1365             :         {
    1366         138 :             abyOutput.resize(nPixelCount * oStep.nOutBands *
    1367          69 :                              GDALGetDataTypeSizeBytes(oStep.eOutDT));
    1368             :         }
    1369           0 :         catch (const std::bad_alloc &)
    1370             :         {
    1371           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1372             :                      "Out of memory allocating working buffer");
    1373           0 :             return false;
    1374             :         }
    1375             : 
    1376          69 :         const auto &oFunc = oIterFunc->second;
    1377         345 :         if (oFunc.pfnProcess(
    1378          69 :                 oStep.osAlgorithm.c_str(), oFunc.pUserData, oStep.pWorkingData,
    1379             :                 oStep.aosArguments.List(), nBufXSize, nBufYSize,
    1380          69 :                 abyInput.data(), abyInput.size(), oStep.eInDT, oStep.nInBands,
    1381          69 :                 oStep.adfInNoData.data(), abyOutput.data(), abyOutput.size(),
    1382          69 :                 oStep.eOutDT, oStep.nOutBands, oStep.adfOutNoData.data(),
    1383          69 :                 dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, srcGT.data(),
    1384             :                 m_osVRTPath.c_str(),
    1385          69 :                 /*papszExtra=*/nullptr) != CE_None)
    1386             :         {
    1387           4 :             return false;
    1388             :         }
    1389             : 
    1390          65 :         std::swap(abyInput, abyOutput);
    1391          65 :         eLastDT = oStep.eOutDT;
    1392             : 
    1393          65 :         ++iStep;
    1394          65 :         if (pfnProgress &&
    1395           0 :             !pfnProgress(0.5 + 0.5 * iStep / static_cast<int>(m_aoSteps.size()),
    1396             :                          "", pProgressData))
    1397           0 :             return false;
    1398             :     }
    1399             : 
    1400          63 :     return true;
    1401             : }
    1402             : 
    1403             : /************************************************************************/
    1404             : /*                        VRTProcessedRasterBand()                      */
    1405             : /************************************************************************/
    1406             : 
    1407             : /** Constructor */
    1408         140 : VRTProcessedRasterBand::VRTProcessedRasterBand(VRTProcessedDataset *poDSIn,
    1409             :                                                int nBandIn,
    1410         140 :                                                GDALDataType eDataTypeIn)
    1411             : {
    1412         140 :     Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
    1413             : 
    1414         140 :     poDS = poDSIn;
    1415         140 :     nBand = nBandIn;
    1416         140 :     eAccess = GA_Update;
    1417         140 :     eDataType = eDataTypeIn;
    1418             : 
    1419         140 :     poDSIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1420         140 : }
    1421             : 
    1422             : /************************************************************************/
    1423             : /*                           GetOverviewCount()                         */
    1424             : /************************************************************************/
    1425             : 
    1426           6 : int VRTProcessedRasterBand::GetOverviewCount()
    1427             : {
    1428           6 :     auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
    1429           6 :     return static_cast<int>(poVRTDS->m_apoOverviewDatasets.size());
    1430             : }
    1431             : 
    1432             : /************************************************************************/
    1433             : /*                              GetOverview()                           */
    1434             : /************************************************************************/
    1435             : 
    1436          11 : GDALRasterBand *VRTProcessedRasterBand::GetOverview(int iOvr)
    1437             : {
    1438          11 :     auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
    1439          22 :     if (iOvr < 0 ||
    1440          11 :         iOvr >= static_cast<int>(poVRTDS->m_apoOverviewDatasets.size()))
    1441           0 :         return nullptr;
    1442          11 :     return poVRTDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
    1443             : }
    1444             : 
    1445             : /************************************************************************/
    1446             : /*                             IReadBlock()                             */
    1447             : /************************************************************************/
    1448             : 
    1449          45 : CPLErr VRTProcessedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1450             :                                           void *pImage)
    1451             : 
    1452             : {
    1453          45 :     auto poVRTDS = cpl::down_cast<VRTProcessedDataset *>(poDS);
    1454             : 
    1455          45 :     int nBufXSize = 0;
    1456          45 :     int nBufYSize = 0;
    1457          45 :     GetActualBlockSize(nBlockXOff, nBlockYOff, &nBufXSize, &nBufYSize);
    1458             : 
    1459          45 :     const int nXPixelOff = nBlockXOff * nBlockXSize;
    1460          45 :     const int nYPixelOff = nBlockYOff * nBlockYSize;
    1461          45 :     if (!poVRTDS->ProcessRegion(nXPixelOff, nYPixelOff, nBufXSize, nBufYSize,
    1462             :                                 nullptr, nullptr))
    1463             :     {
    1464           3 :         return CE_Failure;
    1465             :     }
    1466             : 
    1467          42 :     const int nOutBands = poVRTDS->m_aoSteps.back().nOutBands;
    1468          42 :     CPLAssert(nOutBands == poVRTDS->GetRasterCount());
    1469          42 :     const auto eLastDT = poVRTDS->m_aoSteps.back().eOutDT;
    1470          42 :     const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
    1471          42 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
    1472             : 
    1473             :     // Dispatch final output buffer to cached blocks of output bands
    1474         120 :     for (int iDstBand = 0; iDstBand < nOutBands; ++iDstBand)
    1475             :     {
    1476          78 :         GDALRasterBlock *poBlock = nullptr;
    1477             :         GByte *pDst;
    1478          78 :         if (iDstBand + 1 == nBand)
    1479             :         {
    1480          42 :             pDst = static_cast<GByte *>(pImage);
    1481             :         }
    1482             :         else
    1483             :         {
    1484          36 :             auto poOtherBand = poVRTDS->papoBands[iDstBand];
    1485          36 :             poBlock = poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1486          36 :             if (poBlock)
    1487             :             {
    1488           0 :                 poBlock->DropLock();
    1489           0 :                 continue;
    1490             :             }
    1491          72 :             poBlock = poOtherBand->GetLockedBlockRef(
    1492          36 :                 nBlockXOff, nBlockYOff, /* bJustInitialized = */ true);
    1493          36 :             if (!poBlock)
    1494           0 :                 continue;
    1495          36 :             pDst = static_cast<GByte *>(poBlock->GetDataRef());
    1496             :         }
    1497         853 :         for (int iY = 0; iY < nBufYSize; ++iY)
    1498             :         {
    1499        1550 :             GDALCopyWords64(poVRTDS->m_abyInput.data() +
    1500         775 :                                 (iDstBand + static_cast<size_t>(iY) *
    1501         775 :                                                 nBufXSize * nOutBands) *
    1502         775 :                                     nLastDTSize,
    1503             :                             eLastDT, nLastDTSize * nOutBands,
    1504         775 :                             pDst +
    1505         775 :                                 static_cast<size_t>(iY) * nBlockXSize * nDTSize,
    1506             :                             eDataType, nDTSize, nBufXSize);
    1507             :         }
    1508          78 :         if (poBlock)
    1509          36 :             poBlock->DropLock();
    1510             :     }
    1511             : 
    1512          42 :     return CE_None;
    1513             : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                VRTProcessedDataset::IRasterIO()                      */
    1517             : /************************************************************************/
    1518             : 
    1519          56 : CPLErr VRTProcessedDataset::IRasterIO(
    1520             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1521             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1522             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1523             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
    1524             : {
    1525             :     // Try to pass the request to the most appropriate overview dataset.
    1526          56 :     if (nBufXSize < nXSize && nBufYSize < nYSize)
    1527             :     {
    1528           2 :         int bTried = FALSE;
    1529           2 :         const CPLErr eErr = TryOverviewRasterIO(
    1530             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1531             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    1532             :             nBandSpace, psExtraArg, &bTried);
    1533           2 :         if (bTried)
    1534           2 :             return eErr;
    1535             :     }
    1536             : 
    1537             :     // Optimize reading of all bands at nominal resolution for BIP-like or
    1538             :     // BSQ-like buffer spacing.
    1539          54 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    1540          53 :         nBandCount == nBands)
    1541             :     {
    1542          52 :         const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    1543          61 :         const bool bIsBIPLike = nBandSpace == nBufTypeSize &&
    1544           9 :                                 nPixelSpace == nBandSpace * nBands &&
    1545          66 :                                 nLineSpace >= nPixelSpace * nBufXSize &&
    1546           5 :                                 IsAllBands(nBandCount, panBandMap);
    1547          99 :         const bool bIsBSQLike = nPixelSpace == nBufTypeSize &&
    1548          47 :                                 nLineSpace >= nPixelSpace * nBufXSize &&
    1549         146 :                                 nBandSpace >= nLineSpace * nBufYSize &&
    1550          47 :                                 IsAllBands(nBandCount, panBandMap);
    1551          52 :         if (bIsBIPLike || bIsBSQLike)
    1552             :         {
    1553          50 :             GByte *pabyData = static_cast<GByte *>(pData);
    1554             :             // If acquiring the region of interest in a single time is going
    1555             :             // to consume too much RAM, split in halves.
    1556          50 :             if (m_nAllowedRAMUsage > 0 &&
    1557          50 :                 static_cast<GIntBig>(nBufXSize) * nBufYSize >
    1558          50 :                     m_nAllowedRAMUsage / m_nWorkingBytesPerPixel)
    1559             :             {
    1560          25 :                 if ((nBufXSize == nRasterXSize || nBufYSize >= nBufXSize) &&
    1561             :                     nBufYSize >= 2)
    1562             :                 {
    1563             :                     GDALRasterIOExtraArg sArg;
    1564          12 :                     INIT_RASTERIO_EXTRA_ARG(sArg);
    1565          12 :                     const int nHalfHeight = nBufYSize / 2;
    1566             : 
    1567          12 :                     sArg.pfnProgress = GDALScaledProgress;
    1568          12 :                     sArg.pProgressData = GDALCreateScaledProgress(
    1569             :                         0, 0.5, psExtraArg->pfnProgress,
    1570             :                         psExtraArg->pProgressData);
    1571          12 :                     if (sArg.pProgressData == nullptr)
    1572          12 :                         sArg.pfnProgress = nullptr;
    1573             :                     bool bOK =
    1574          12 :                         IRasterIO(eRWFlag, nXOff, nYOff, nBufXSize, nHalfHeight,
    1575             :                                   pabyData, nBufXSize, nHalfHeight, eBufType,
    1576             :                                   nBandCount, panBandMap, nPixelSpace,
    1577          12 :                                   nLineSpace, nBandSpace, &sArg) == CE_None;
    1578          12 :                     GDALDestroyScaledProgress(sArg.pProgressData);
    1579             : 
    1580          12 :                     if (bOK)
    1581             :                     {
    1582           2 :                         sArg.pfnProgress = GDALScaledProgress;
    1583           2 :                         sArg.pProgressData = GDALCreateScaledProgress(
    1584             :                             0.5, 1, psExtraArg->pfnProgress,
    1585             :                             psExtraArg->pProgressData);
    1586           2 :                         if (sArg.pProgressData == nullptr)
    1587           2 :                             sArg.pfnProgress = nullptr;
    1588           4 :                         bOK = IRasterIO(eRWFlag, nXOff, nYOff + nHalfHeight,
    1589             :                                         nBufXSize, nBufYSize - nHalfHeight,
    1590           2 :                                         pabyData + nHalfHeight * nLineSpace,
    1591             :                                         nBufXSize, nBufYSize - nHalfHeight,
    1592             :                                         eBufType, nBandCount, panBandMap,
    1593             :                                         nPixelSpace, nLineSpace, nBandSpace,
    1594             :                                         &sArg) == CE_None;
    1595           2 :                         GDALDestroyScaledProgress(sArg.pProgressData);
    1596             :                     }
    1597          12 :                     return bOK ? CE_None : CE_Failure;
    1598             :                 }
    1599          13 :                 else if (nBufXSize >= 2)
    1600             :                 {
    1601             :                     GDALRasterIOExtraArg sArg;
    1602          13 :                     INIT_RASTERIO_EXTRA_ARG(sArg);
    1603          13 :                     const int nHalfWidth = nBufXSize / 2;
    1604             : 
    1605          13 :                     sArg.pfnProgress = GDALScaledProgress;
    1606          13 :                     sArg.pProgressData = GDALCreateScaledProgress(
    1607             :                         0, 0.5, psExtraArg->pfnProgress,
    1608             :                         psExtraArg->pProgressData);
    1609          13 :                     if (sArg.pProgressData == nullptr)
    1610          13 :                         sArg.pfnProgress = nullptr;
    1611             :                     bool bOK =
    1612          13 :                         IRasterIO(eRWFlag, nXOff, nYOff, nHalfWidth, nBufYSize,
    1613             :                                   pabyData, nHalfWidth, nBufYSize, eBufType,
    1614             :                                   nBandCount, panBandMap, nPixelSpace,
    1615          13 :                                   nLineSpace, nBandSpace, &sArg) == CE_None;
    1616          13 :                     GDALDestroyScaledProgress(sArg.pProgressData);
    1617             : 
    1618          13 :                     if (bOK)
    1619             :                     {
    1620           3 :                         sArg.pfnProgress = GDALScaledProgress;
    1621           3 :                         sArg.pProgressData = GDALCreateScaledProgress(
    1622             :                             0.5, 1, psExtraArg->pfnProgress,
    1623             :                             psExtraArg->pProgressData);
    1624           3 :                         if (sArg.pProgressData == nullptr)
    1625           3 :                             sArg.pfnProgress = nullptr;
    1626           6 :                         bOK = IRasterIO(eRWFlag, nXOff + nHalfWidth, nYOff,
    1627             :                                         nBufXSize - nHalfWidth, nBufYSize,
    1628           3 :                                         pabyData + nHalfWidth * nPixelSpace,
    1629             :                                         nBufXSize - nHalfWidth, nBufYSize,
    1630             :                                         eBufType, nBandCount, panBandMap,
    1631             :                                         nPixelSpace, nLineSpace, nBandSpace,
    1632             :                                         &sArg) == CE_None;
    1633           3 :                         GDALDestroyScaledProgress(sArg.pProgressData);
    1634             :                     }
    1635          13 :                     return bOK ? CE_None : CE_Failure;
    1636             :                 }
    1637             :             }
    1638             : 
    1639          25 :             if (!ProcessRegion(nXOff, nYOff, nBufXSize, nBufYSize,
    1640             :                                psExtraArg->pfnProgress,
    1641             :                                psExtraArg->pProgressData))
    1642             :             {
    1643           4 :                 return CE_Failure;
    1644             :             }
    1645          21 :             const auto eLastDT = m_aoSteps.back().eOutDT;
    1646          21 :             const int nLastDTSize = GDALGetDataTypeSizeBytes(eLastDT);
    1647          21 :             if (bIsBIPLike)
    1648             :             {
    1649          10 :                 for (int iY = 0; iY < nBufYSize; ++iY)
    1650             :                 {
    1651          21 :                     GDALCopyWords64(
    1652          14 :                         m_abyInput.data() + static_cast<size_t>(iY) * nBands *
    1653           7 :                                                 nBufXSize * nLastDTSize,
    1654           7 :                         eLastDT, nLastDTSize, pabyData + iY * nLineSpace,
    1655             :                         eBufType, GDALGetDataTypeSizeBytes(eBufType),
    1656           7 :                         static_cast<size_t>(nBufXSize) * nBands);
    1657             :                 }
    1658             :             }
    1659             :             else
    1660             :             {
    1661          18 :                 CPLAssert(bIsBSQLike);
    1662          92 :                 for (int iBand = 0; iBand < nBands; ++iBand)
    1663             :                 {
    1664         168 :                     for (int iY = 0; iY < nBufYSize; ++iY)
    1665             :                     {
    1666         188 :                         GDALCopyWords64(
    1667         188 :                             m_abyInput.data() +
    1668          94 :                                 (static_cast<size_t>(iY) * nBands * nBufXSize +
    1669          94 :                                  iBand) *
    1670          94 :                                     nLastDTSize,
    1671          94 :                             eLastDT, nLastDTSize * nBands,
    1672          94 :                             pabyData + iBand * nBandSpace + iY * nLineSpace,
    1673             :                             eBufType, nBufTypeSize, nBufXSize);
    1674             :                     }
    1675             :                 }
    1676             :             }
    1677          21 :             return CE_None;
    1678             :         }
    1679             :     }
    1680             : 
    1681           4 :     return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1682             :                                  nBufXSize, nBufYSize, eBufType, nBandCount,
    1683             :                                  panBandMap, nPixelSpace, nLineSpace,
    1684           4 :                                  nBandSpace, psExtraArg);
    1685             : }
    1686             : 
    1687             : /*! @endcond */
    1688             : 
    1689             : /************************************************************************/
    1690             : /*                GDALVRTRegisterProcessedDatasetFunc()                 */
    1691             : /************************************************************************/
    1692             : 
    1693             : /** Register a function to be used by VRTProcessedDataset.
    1694             : 
    1695             :  An example of content for pszXMLMetadata is:
    1696             :  \verbatim
    1697             :   <ProcessedDatasetFunctionArgumentsList>
    1698             :      <Argument name='src_nodata' type='double' description='Override input nodata value'/>
    1699             :      <Argument name='dst_nodata' type='double' description='Override output nodata value'/>
    1700             :      <Argument name='replacement_nodata' description='value to substitute to a valid computed value that would be nodata' type='double'/>
    1701             :      <Argument name='dst_intended_datatype' type='string' description='Intented datatype of output (which might be different than the working data type)'/>
    1702             :      <Argument name='coefficients_{band}' description='Comma-separated coefficients for combining bands. First one is constant term' type='double_list' required='true'/>
    1703             :   </ProcessedDatasetFunctionArgumentsList>
    1704             :  \endverbatim
    1705             : 
    1706             :  @param pszFuncName Function name. Must be unique and not null.
    1707             :  @param pUserData User data. May be nullptr. Must remain valid during the
    1708             :                   lifetime of GDAL.
    1709             :  @param pszXMLMetadata XML metadata describing the function arguments. May be
    1710             :                        nullptr if there are no arguments.
    1711             :  @param eRequestedInputDT If the pfnProcess callback only supports a single
    1712             :                           data type, it should be specified in this parameter.
    1713             :                           Otherwise set it to GDT_Unknown.
    1714             :  @param paeSupportedInputDT List of supported input data types. May be nullptr
    1715             :                             if all are supported or if eRequestedInputDT is
    1716             :                             set to a non GDT_Unknown value.
    1717             :  @param nSupportedInputDTSize Size of paeSupportedInputDT
    1718             :  @param panSupportedInputBandCount List of supported band count. May be nullptr
    1719             :                                    if any source band count is supported.
    1720             :  @param nSupportedInputBandCountSize Size of panSupportedInputBandCount
    1721             :  @param pfnInit Initialization function called when a VRTProcessedDataset
    1722             :                 step uses the register function. This initialization function
    1723             :                 will return the output data type, output band count and
    1724             :                 potentially initialize a working structure, typically parsing
    1725             :                 arguments. May be nullptr.
    1726             :                 If not specified, it will be assumed that the input and output
    1727             :                 data types are the same, and that the input number of bands
    1728             :                 and output number of bands are the same.
    1729             :  @param pfnFree Free function that will free the working structure allocated
    1730             :                 by pfnInit. May be nullptr.
    1731             :  @param pfnProcess Processing function called to compute pixel values. Must
    1732             :                    not be nullptr.
    1733             :  @param papszOptions Unused currently. Must be nullptr.
    1734             :  @return CE_None in case of success, error otherwise.
    1735             :  @since 3.9
    1736             :  */
    1737        7265 : CPLErr GDALVRTRegisterProcessedDatasetFunc(
    1738             :     const char *pszFuncName, void *pUserData, const char *pszXMLMetadata,
    1739             :     GDALDataType eRequestedInputDT, const GDALDataType *paeSupportedInputDT,
    1740             :     size_t nSupportedInputDTSize, const int *panSupportedInputBandCount,
    1741             :     size_t nSupportedInputBandCountSize,
    1742             :     GDALVRTProcessedDatasetFuncInit pfnInit,
    1743             :     GDALVRTProcessedDatasetFuncFree pfnFree,
    1744             :     GDALVRTProcessedDatasetFuncProcess pfnProcess,
    1745             :     CPL_UNUSED CSLConstList papszOptions)
    1746             : {
    1747        7265 :     if (pszFuncName == nullptr || pszFuncName[0] == '\0')
    1748             :     {
    1749           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1750             :                  "pszFuncName should be non-empty");
    1751           0 :         return CE_Failure;
    1752             :     }
    1753             : 
    1754        7265 :     auto &oMap = GetGlobalMapProcessedDatasetFunc();
    1755        7265 :     if (oMap.find(pszFuncName) != oMap.end())
    1756             :     {
    1757           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s already registered",
    1758             :                  pszFuncName);
    1759           0 :         return CE_Failure;
    1760             :     }
    1761             : 
    1762        7265 :     if (!pfnProcess)
    1763             :     {
    1764           0 :         CPLError(CE_Failure, CPLE_AppDefined, "pfnProcess should not be null");
    1765           0 :         return CE_Failure;
    1766             :     }
    1767             : 
    1768       14530 :     VRTProcessedDatasetFunc oFunc;
    1769        7265 :     oFunc.osFuncName = pszFuncName;
    1770        7265 :     oFunc.pUserData = pUserData;
    1771        7265 :     if (pszXMLMetadata)
    1772             :     {
    1773        7265 :         oFunc.bMetadataSpecified = true;
    1774        7265 :         auto psTree = CPLXMLTreeCloser(CPLParseXMLString(pszXMLMetadata));
    1775        7265 :         if (!psTree)
    1776             :         {
    1777           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1778             :                      "Cannot parse pszXMLMetadata=%s for %s", pszXMLMetadata,
    1779             :                      pszFuncName);
    1780           0 :             return CE_Failure;
    1781             :         }
    1782        7265 :         const CPLXMLNode *psRoot = CPLGetXMLNode(
    1783             :             psTree.get(), "=ProcessedDatasetFunctionArgumentsList");
    1784        7265 :         if (!psRoot)
    1785             :         {
    1786           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1787             :                      "No root ProcessedDatasetFunctionArgumentsList element in "
    1788             :                      "pszXMLMetadata=%s for %s",
    1789             :                      pszXMLMetadata, pszFuncName);
    1790           0 :             return CE_Failure;
    1791             :         }
    1792       55214 :         for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
    1793       47949 :              psIter = psIter->psNext)
    1794             :         {
    1795       47949 :             if (psIter->eType == CXT_Element &&
    1796       47949 :                 strcmp(psIter->pszValue, "Argument") == 0)
    1797             :             {
    1798       47949 :                 const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    1799       47949 :                 if (!pszName)
    1800             :                 {
    1801           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1802             :                              "Missing Argument.name attribute in "
    1803             :                              "pszXMLMetadata=%s for %s",
    1804             :                              pszXMLMetadata, pszFuncName);
    1805           0 :                     return CE_Failure;
    1806             :                 }
    1807       47949 :                 const char *pszType = CPLGetXMLValue(psIter, "type", nullptr);
    1808       47949 :                 if (!pszType)
    1809             :                 {
    1810           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1811             :                              "Missing Argument.type attribute in "
    1812             :                              "pszXMLMetadata=%s for %s",
    1813             :                              pszXMLMetadata, pszFuncName);
    1814           0 :                     return CE_Failure;
    1815             :                 }
    1816       47949 :                 if (strcmp(pszType, "constant") == 0)
    1817             :                 {
    1818             :                     const char *pszValue =
    1819           0 :                         CPLGetXMLValue(psIter, "value", nullptr);
    1820           0 :                     if (!pszValue)
    1821             :                     {
    1822           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1823             :                                  "Missing Argument.value attribute in "
    1824             :                                  "pszXMLMetadata=%s for %s",
    1825             :                                  pszXMLMetadata, pszFuncName);
    1826           0 :                         return CE_Failure;
    1827             :                     }
    1828           0 :                     oFunc.oMapConstantArguments[CPLString(pszName).tolower()] =
    1829           0 :                         pszValue;
    1830             :                 }
    1831       47949 :                 else if (strcmp(pszType, "builtin") == 0)
    1832             :                 {
    1833           0 :                     if (EQUAL(pszName, "nodata") ||
    1834           0 :                         EQUAL(pszName, "offset_{band}") ||
    1835           0 :                         EQUAL(pszName, "scale_{band}"))
    1836             :                     {
    1837           0 :                         oFunc.oSetBuiltinArguments.insert(
    1838           0 :                             CPLString(pszName).tolower());
    1839             :                     }
    1840             :                     else
    1841             :                     {
    1842           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
    1843             :                                  "Unsupported builtin parameter name %s in "
    1844             :                                  "pszXMLMetadata=%s for %s. Only nodata, "
    1845             :                                  "offset_{band} and scale_{band} are supported",
    1846             :                                  pszName, pszXMLMetadata, pszFuncName);
    1847           0 :                         return CE_Failure;
    1848             :                     }
    1849             :                 }
    1850       47949 :                 else if (strcmp(pszType, "boolean") == 0 ||
    1851       45043 :                          strcmp(pszType, "string") == 0 ||
    1852       34872 :                          strcmp(pszType, "integer") == 0 ||
    1853       26154 :                          strcmp(pszType, "double") == 0 ||
    1854        1453 :                          strcmp(pszType, "double_list") == 0)
    1855             :                 {
    1856       47949 :                     VRTProcessedDatasetFunc::OtherArgument otherArgument;
    1857       47949 :                     otherArgument.bRequired = CPLTestBool(
    1858             :                         CPLGetXMLValue(psIter, "required", "false"));
    1859       47949 :                     otherArgument.osType = pszType;
    1860       95898 :                     oFunc.oOtherArguments[CPLString(pszName).tolower()] =
    1861      143847 :                         std::move(otherArgument);
    1862             :                 }
    1863             :                 else
    1864             :                 {
    1865           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1866             :                              "Unsupported type for parameter %s in "
    1867             :                              "pszXMLMetadata=%s for %s. Only boolean, string, "
    1868             :                              "integer, double and double_list are supported",
    1869             :                              pszName, pszXMLMetadata, pszFuncName);
    1870           0 :                     return CE_Failure;
    1871             :                 }
    1872             :             }
    1873             :         }
    1874             :     }
    1875        7265 :     oFunc.eRequestedInputDT = eRequestedInputDT;
    1876        7265 :     if (nSupportedInputDTSize)
    1877             :     {
    1878             :         oFunc.aeSupportedInputDT.insert(
    1879           0 :             oFunc.aeSupportedInputDT.end(), paeSupportedInputDT,
    1880           0 :             paeSupportedInputDT + nSupportedInputDTSize);
    1881             :     }
    1882        7265 :     if (nSupportedInputBandCountSize)
    1883             :     {
    1884             :         oFunc.anSupportedInputBandCount.insert(
    1885           0 :             oFunc.anSupportedInputBandCount.end(), panSupportedInputBandCount,
    1886           0 :             panSupportedInputBandCount + nSupportedInputBandCountSize);
    1887             :     }
    1888        7265 :     oFunc.pfnInit = pfnInit;
    1889        7265 :     oFunc.pfnFree = pfnFree;
    1890        7265 :     oFunc.pfnProcess = pfnProcess;
    1891             : 
    1892        7265 :     oMap[pszFuncName] = std::move(oFunc);
    1893             : 
    1894        7265 :     return CE_None;
    1895             : }

Generated by: LCOV version 1.14