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

Generated by: LCOV version 1.14