LCOV - code coverage report
Current view: top level - frmts/vrt - vrtderivedrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 689 747 92.2 %
Date: 2026-03-26 12:50:33 Functions: 33 35 94.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of a sourced raster band that derives its raster
       5             :  *           by applying an algorithm (GDALDerivedPixelFunc) to the sources.
       6             :  * Author:   Pete Nagy
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2005 Vexcel Corp.
      10             :  * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  *****************************************************************************/
      14             : 
      15             : #include "cpl_minixml.h"
      16             : #include "cpl_string.h"
      17             : #include "gdal_priv.h"
      18             : #include "vrtdataset.h"
      19             : #include "cpl_multiproc.h"
      20             : #include "gdalpython.h"
      21             : #include "gdalantirecursion.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <array>
      25             : #include <map>
      26             : #include <vector>
      27             : #include <utility>
      28             : 
      29             : /*! @cond Doxygen_Suppress */
      30             : 
      31             : using namespace GDALPy;
      32             : 
      33             : // #define GDAL_VRT_DISABLE_PYTHON
      34             : 
      35             : #ifndef GDAL_VRT_ENABLE_PYTHON_DEFAULT
      36             : // Can be YES, NO or TRUSTED_MODULES
      37             : #define GDAL_VRT_ENABLE_PYTHON_DEFAULT "TRUSTED_MODULES"
      38             : #endif
      39             : 
      40             : /* Flags for getting buffers */
      41             : #define PyBUF_WRITABLE 0x0001
      42             : #define PyBUF_FORMAT 0x0004
      43             : #define PyBUF_ND 0x0008
      44             : #define PyBUF_STRIDES (0x0010 | PyBUF_ND)
      45             : #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES)
      46             : #define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT)
      47             : 
      48             : /************************************************************************/
      49             : /*                        GDALCreateNumpyArray()                        */
      50             : /************************************************************************/
      51             : 
      52         557 : static PyObject *GDALCreateNumpyArray(PyObject *pCreateArray, void *pBuffer,
      53             :                                       GDALDataType eType, int nHeight,
      54             :                                       int nWidth)
      55             : {
      56             :     PyObject *poPyBuffer;
      57             :     const size_t nSize =
      58         557 :         static_cast<size_t>(nHeight) * nWidth * GDALGetDataTypeSizeBytes(eType);
      59             :     Py_buffer pybuffer;
      60         557 :     if (PyBuffer_FillInfo(&pybuffer, nullptr, static_cast<char *>(pBuffer),
      61         557 :                           nSize, 0, PyBUF_FULL) != 0)
      62             :     {
      63           0 :         return nullptr;
      64             :     }
      65         557 :     poPyBuffer = PyMemoryView_FromBuffer(&pybuffer);
      66         557 :     PyObject *pArgsCreateArray = PyTuple_New(4);
      67         557 :     PyTuple_SetItem(pArgsCreateArray, 0, poPyBuffer);
      68         557 :     const char *pszDataType = nullptr;
      69         557 :     switch (eType)
      70             :     {
      71         436 :         case GDT_UInt8:
      72         436 :             pszDataType = "uint8";
      73         436 :             break;
      74           3 :         case GDT_Int8:
      75           3 :             pszDataType = "int8";
      76           3 :             break;
      77           2 :         case GDT_UInt16:
      78           2 :             pszDataType = "uint16";
      79           2 :             break;
      80          88 :         case GDT_Int16:
      81          88 :             pszDataType = "int16";
      82          88 :             break;
      83           2 :         case GDT_UInt32:
      84           2 :             pszDataType = "uint32";
      85           2 :             break;
      86           4 :         case GDT_Int32:
      87           4 :             pszDataType = "int32";
      88           4 :             break;
      89           4 :         case GDT_Int64:
      90           4 :             pszDataType = "int64";
      91           4 :             break;
      92           2 :         case GDT_UInt64:
      93           2 :             pszDataType = "uint64";
      94           2 :             break;
      95           2 :         case GDT_Float16:
      96           2 :             pszDataType = "float16";
      97           2 :             break;
      98           4 :         case GDT_Float32:
      99           4 :             pszDataType = "float32";
     100           4 :             break;
     101           4 :         case GDT_Float64:
     102           4 :             pszDataType = "float64";
     103           4 :             break;
     104           0 :         case GDT_CInt16:
     105             :         case GDT_CInt32:
     106           0 :             CPLAssert(FALSE);
     107             :             break;
     108           0 :         case GDT_CFloat16:
     109           0 :             CPLAssert(FALSE);
     110             :             break;
     111           3 :         case GDT_CFloat32:
     112           3 :             pszDataType = "complex64";
     113           3 :             break;
     114           3 :         case GDT_CFloat64:
     115           3 :             pszDataType = "complex128";
     116           3 :             break;
     117           0 :         case GDT_Unknown:
     118             :         case GDT_TypeCount:
     119           0 :             CPLAssert(FALSE);
     120             :             break;
     121             :     }
     122         557 :     PyTuple_SetItem(
     123             :         pArgsCreateArray, 1,
     124             :         PyBytes_FromStringAndSize(pszDataType, strlen(pszDataType)));
     125         557 :     PyTuple_SetItem(pArgsCreateArray, 2, PyLong_FromLong(nHeight));
     126         557 :     PyTuple_SetItem(pArgsCreateArray, 3, PyLong_FromLong(nWidth));
     127             :     PyObject *poNumpyArray =
     128         557 :         PyObject_Call(pCreateArray, pArgsCreateArray, nullptr);
     129         557 :     Py_DecRef(pArgsCreateArray);
     130         557 :     if (PyErr_Occurred())
     131           0 :         PyErr_Print();
     132         557 :     return poNumpyArray;
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /* ==================================================================== */
     137             : /*                     VRTDerivedRasterBandPrivateData                  */
     138             : /* ==================================================================== */
     139             : /************************************************************************/
     140             : 
     141             : class VRTDerivedRasterBandPrivateData
     142             : {
     143             :     VRTDerivedRasterBandPrivateData(const VRTDerivedRasterBandPrivateData &) =
     144             :         delete;
     145             :     VRTDerivedRasterBandPrivateData &
     146             :     operator=(const VRTDerivedRasterBandPrivateData &) = delete;
     147             : 
     148             :   public:
     149             :     CPLString m_osCode{};
     150             :     CPLString m_osLanguage = "C";
     151             :     int m_nBufferRadius = 0;
     152             :     PyObject *m_poGDALCreateNumpyArray = nullptr;
     153             :     PyObject *m_poUserFunction = nullptr;
     154             :     bool m_bPythonInitializationDone = false;
     155             :     bool m_bPythonInitializationSuccess = false;
     156             :     bool m_bExclusiveLock = false;
     157             :     bool m_bFirstTime = true;
     158             :     std::vector<std::pair<CPLString, CPLString>> m_oFunctionArgs{};
     159             :     bool m_bSkipNonContributingSourcesSpecified = false;
     160             :     bool m_bSkipNonContributingSources = false;
     161             :     GIntBig m_nAllowedRAMUsage = 0;
     162             : 
     163        2260 :     VRTDerivedRasterBandPrivateData()
     164        2260 :         : m_nAllowedRAMUsage(CPLGetUsablePhysicalRAM() / 10 * 4)
     165             :     {
     166             :         // Use only up to 40% of RAM to acquire source bands and generate the
     167             :         // output buffer.
     168             :         // Only for tests now
     169        2260 :         const char *pszMAX_RAM = "VRT_DERIVED_DATASET_ALLOWED_RAM_USAGE";
     170        2260 :         if (const char *pszVal = CPLGetConfigOption(pszMAX_RAM, nullptr))
     171             :         {
     172           1 :             CPL_IGNORE_RET_VAL(
     173           1 :                 CPLParseMemorySize(pszVal, &m_nAllowedRAMUsage, nullptr));
     174             :         }
     175        2260 :     }
     176             : 
     177             :     ~VRTDerivedRasterBandPrivateData();
     178             : };
     179             : 
     180        2260 : VRTDerivedRasterBandPrivateData::~VRTDerivedRasterBandPrivateData()
     181             : {
     182        2260 :     if (m_poGDALCreateNumpyArray)
     183          51 :         Py_DecRef(m_poGDALCreateNumpyArray);
     184        2260 :     if (m_poUserFunction)
     185          52 :         Py_DecRef(m_poUserFunction);
     186        2260 : }
     187             : 
     188             : /************************************************************************/
     189             : /* ==================================================================== */
     190             : /*                          VRTDerivedRasterBand                        */
     191             : /* ==================================================================== */
     192             : /************************************************************************/
     193             : 
     194             : /************************************************************************/
     195             : /*                        VRTDerivedRasterBand()                        */
     196             : /************************************************************************/
     197             : 
     198        1497 : VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDSIn, int nBandIn)
     199             :     : VRTSourcedRasterBand(poDSIn, nBandIn), m_poPrivate(nullptr),
     200        1497 :       eSourceTransferType(GDT_Unknown)
     201             : {
     202        1497 :     m_poPrivate = new VRTDerivedRasterBandPrivateData;
     203        1497 : }
     204             : 
     205             : /************************************************************************/
     206             : /*                        VRTDerivedRasterBand()                        */
     207             : /************************************************************************/
     208             : 
     209         763 : VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDSIn, int nBandIn,
     210             :                                            GDALDataType eType, int nXSize,
     211             :                                            int nYSize, int nBlockXSizeIn,
     212         763 :                                            int nBlockYSizeIn)
     213             :     : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize,
     214             :                            nBlockXSizeIn, nBlockYSizeIn),
     215         763 :       m_poPrivate(nullptr), eSourceTransferType(GDT_Unknown)
     216             : {
     217         763 :     m_poPrivate = new VRTDerivedRasterBandPrivateData;
     218         763 : }
     219             : 
     220             : /************************************************************************/
     221             : /*                       ~VRTDerivedRasterBand()                        */
     222             : /************************************************************************/
     223             : 
     224        4520 : VRTDerivedRasterBand::~VRTDerivedRasterBand()
     225             : 
     226             : {
     227        2260 :     delete m_poPrivate;
     228        4520 : }
     229             : 
     230             : /************************************************************************/
     231             : /*                     CopyForCloneWithoutSources()                     */
     232             : /************************************************************************/
     233             : 
     234           1 : void VRTDerivedRasterBand::CopyForCloneWithoutSources(
     235             :     const VRTDerivedRasterBand *poSrcBand)
     236             : {
     237           1 :     VRTSourcedRasterBand::CopyForCloneWithoutSources(poSrcBand);
     238           1 :     osFuncName = poSrcBand->osFuncName;
     239           1 :     eSourceTransferType = poSrcBand->eSourceTransferType;
     240           1 :     m_poPrivate->m_osCode = poSrcBand->m_poPrivate->m_osCode;
     241           1 :     m_poPrivate->m_osLanguage = poSrcBand->m_poPrivate->m_osLanguage;
     242           1 :     m_poPrivate->m_nBufferRadius = poSrcBand->m_poPrivate->m_nBufferRadius;
     243           1 :     m_poPrivate->m_oFunctionArgs = poSrcBand->m_poPrivate->m_oFunctionArgs;
     244           1 :     m_poPrivate->m_bSkipNonContributingSources =
     245           1 :         poSrcBand->m_poPrivate->m_bSkipNonContributingSources;
     246           1 : }
     247             : 
     248             : /************************************************************************/
     249             : /*                        CloneWithoutSources()                         */
     250             : /************************************************************************/
     251             : 
     252             : std::unique_ptr<VRTSourcedRasterBand>
     253           1 : VRTDerivedRasterBand::CloneWithoutSources(GDALDataset *poNewDS, int nNewXSize,
     254             :                                           int nNewYSize) const
     255             : {
     256             :     auto poClone = std::make_unique<VRTDerivedRasterBand>(
     257           1 :         poNewDS, GetBand(), GetRasterDataType(), nNewXSize, nNewYSize,
     258           3 :         nBlockXSize, nBlockYSize);
     259           1 :     poClone->CopyForCloneWithoutSources(this);
     260           2 :     return poClone;
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                              Cleanup()                               */
     265             : /************************************************************************/
     266             : 
     267        1131 : void VRTDerivedRasterBand::Cleanup()
     268             : {
     269        1131 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                     GetGlobalMapPixelFunction()                      */
     273             : /************************************************************************/
     274             : 
     275             : static std::map<std::string,
     276             :                 std::pair<VRTDerivedRasterBand::PixelFunc, std::string>> &
     277       66804 : GetGlobalMapPixelFunction()
     278             : {
     279             :     static std::map<std::string,
     280             :                     std::pair<VRTDerivedRasterBand::PixelFunc, std::string>>
     281       66804 :         gosMapPixelFunction;
     282       66804 :     return gosMapPixelFunction;
     283             : }
     284             : 
     285             : /************************************************************************/
     286             : /*                          AddPixelFunction()                          */
     287             : /************************************************************************/
     288             : 
     289             : /*! @endcond */
     290             : 
     291             : /**
     292             :  * This adds a pixel function to the global list of available pixel
     293             :  * functions for derived bands.  Pixel functions must be registered
     294             :  * in this way before a derived band tries to access data.
     295             :  *
     296             :  * Derived bands are stored with only the name of the pixel function
     297             :  * that it will apply, and if a pixel function matching the name is not
     298             :  * found the IRasterIO() call will do nothing.
     299             :  *
     300             :  * @param pszName Name used to access pixel function
     301             :  * @param pfnNewFunction Pixel function associated with name.  An
     302             :  *  existing pixel function registered with the same name will be
     303             :  *  replaced with the new one.
     304             :  *
     305             :  * @return CE_None, invalid (NULL) parameters are currently ignored.
     306             :  */
     307       15952 : CPLErr CPL_STDCALL GDALAddDerivedBandPixelFunc(
     308             :     const char *pszName, GDALDerivedPixelFunc pfnNewFunction)
     309             : {
     310       15952 :     if (pszName == nullptr || pszName[0] == '\0' || pfnNewFunction == nullptr)
     311             :     {
     312           0 :         return CE_None;
     313             :     }
     314             : 
     315       31904 :     GetGlobalMapPixelFunction()[pszName] = {
     316          54 :         [pfnNewFunction](void **papoSources, int nSources, void *pData,
     317             :                          int nBufXSize, int nBufYSize, GDALDataType eSrcType,
     318             :                          GDALDataType eBufType, int nPixelSpace, int nLineSpace,
     319          54 :                          CSLConstList papszFunctionArgs)
     320             :         {
     321             :             (void)papszFunctionArgs;
     322          54 :             return pfnNewFunction(papoSources, nSources, pData, nBufXSize,
     323             :                                   nBufYSize, eSrcType, eBufType, nPixelSpace,
     324          54 :                                   nLineSpace);
     325             :         },
     326       47856 :         ""};
     327             : 
     328       15952 :     return CE_None;
     329             : }
     330             : 
     331             : /**
     332             :  * This adds a pixel function to the global list of available pixel
     333             :  * functions for derived bands.  Pixel functions must be registered
     334             :  * in this way before a derived band tries to access data.
     335             :  *
     336             :  * Derived bands are stored with only the name of the pixel function
     337             :  * that it will apply, and if a pixel function matching the name is not
     338             :  * found the IRasterIO() call will do nothing.
     339             :  *
     340             :  * @param pszName Name used to access pixel function
     341             :  * @param pfnNewFunction Pixel function associated with name.  An
     342             :  *  existing pixel function registered with the same name will be
     343             :  *  replaced with the new one.
     344             :  * @param pszMetadata Pixel function metadata (not currently implemented)
     345             :  *
     346             :  * @return CE_None, invalid (NULL) parameters are currently ignored.
     347             :  * @since GDAL 3.4
     348             :  */
     349       47852 : CPLErr CPL_STDCALL GDALAddDerivedBandPixelFuncWithArgs(
     350             :     const char *pszName, GDALDerivedPixelFuncWithArgs pfnNewFunction,
     351             :     const char *pszMetadata)
     352             : {
     353       47852 :     if (!pszName || pszName[0] == '\0' || !pfnNewFunction)
     354             :     {
     355           0 :         return CE_None;
     356             :     }
     357             : 
     358       95704 :     GetGlobalMapPixelFunction()[pszName] = {pfnNewFunction,
     359      143556 :                                             pszMetadata ? pszMetadata : ""};
     360             : 
     361       47852 :     return CE_None;
     362             : }
     363             : 
     364             : /*! @cond Doxygen_Suppress */
     365             : 
     366             : /**
     367             :  * This adds a pixel function to the global list of available pixel
     368             :  * functions for derived bands.
     369             :  *
     370             :  * This is the same as the C function GDALAddDerivedBandPixelFunc()
     371             :  *
     372             :  * @param pszFuncNameIn Name used to access pixel function
     373             :  * @param pfnNewFunction Pixel function associated with name.  An
     374             :  *  existing pixel function registered with the same name will be
     375             :  *  replaced with the new one.
     376             :  *
     377             :  * @return CE_None, invalid (NULL) parameters are currently ignored.
     378             :  */
     379             : CPLErr
     380           0 : VRTDerivedRasterBand::AddPixelFunction(const char *pszFuncNameIn,
     381             :                                        GDALDerivedPixelFunc pfnNewFunction)
     382             : {
     383           0 :     return GDALAddDerivedBandPixelFunc(pszFuncNameIn, pfnNewFunction);
     384             : }
     385             : 
     386           0 : CPLErr VRTDerivedRasterBand::AddPixelFunction(
     387             :     const char *pszFuncNameIn, GDALDerivedPixelFuncWithArgs pfnNewFunction,
     388             :     const char *pszMetadata)
     389             : {
     390           0 :     return GDALAddDerivedBandPixelFuncWithArgs(pszFuncNameIn, pfnNewFunction,
     391           0 :                                                pszMetadata);
     392             : }
     393             : 
     394             : /************************************************************************/
     395             : /*                          GetPixelFunction()                          */
     396             : /************************************************************************/
     397             : 
     398             : /**
     399             :  * Get a pixel function previously registered using the global
     400             :  * AddPixelFunction.
     401             :  *
     402             :  * @param pszFuncNameIn The name associated with the pixel function.
     403             :  *
     404             :  * @return A pointer to a std::pair whose first element is the pixel
     405             :  *         function pointer and second element is the pixel function
     406             :  *         metadata string. If no pixel function has been registered
     407             :  *         for pszFuncNameIn, nullptr will be returned.
     408             :  */
     409             : /* static */
     410             : const std::pair<VRTDerivedRasterBand::PixelFunc, std::string> *
     411        2862 : VRTDerivedRasterBand::GetPixelFunction(const char *pszFuncNameIn)
     412             : {
     413        2862 :     if (pszFuncNameIn == nullptr || pszFuncNameIn[0] == '\0')
     414             :     {
     415           0 :         return nullptr;
     416             :     }
     417             : 
     418        2862 :     const auto &oMapPixelFunction = GetGlobalMapPixelFunction();
     419        2862 :     const auto oIter = oMapPixelFunction.find(pszFuncNameIn);
     420             : 
     421        2862 :     if (oIter == oMapPixelFunction.end())
     422           3 :         return nullptr;
     423             : 
     424        2859 :     return &(oIter->second);
     425             : }
     426             : 
     427             : /************************************************************************/
     428             : /*                       GetPixelFunctionNames()                        */
     429             : /************************************************************************/
     430             : 
     431             : /**
     432             :  * Return the list of available pixel function names.
     433             :  */
     434             : /* static */
     435         138 : std::vector<std::string> VRTDerivedRasterBand::GetPixelFunctionNames()
     436             : {
     437         138 :     std::vector<std::string> res;
     438        5658 :     for (const auto &iter : GetGlobalMapPixelFunction())
     439             :     {
     440        5520 :         res.push_back(iter.first);
     441             :     }
     442         138 :     return res;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                        SetPixelFunctionName()                        */
     447             : /************************************************************************/
     448             : 
     449             : /**
     450             :  * Set the pixel function name to be applied to this derived band.  The
     451             :  * name should match a pixel function registered using AddPixelFunction.
     452             :  *
     453             :  * @param pszFuncNameIn Name of pixel function to be applied to this derived
     454             :  * band.
     455             :  */
     456        2260 : void VRTDerivedRasterBand::SetPixelFunctionName(const char *pszFuncNameIn)
     457             : {
     458        2260 :     osFuncName = (pszFuncNameIn == nullptr) ? "" : pszFuncNameIn;
     459        2260 : }
     460             : 
     461             : /************************************************************************/
     462             : /*                      AddPixelFunctionArgument()                      */
     463             : /************************************************************************/
     464             : 
     465             : /**
     466             :  *  Set a pixel function argument to a specified value.
     467             :  * @param pszArg the argument name
     468             :  * @param pszValue the argument value
     469             :  *
     470             :  * @since 3.12
     471             :  */
     472        2674 : void VRTDerivedRasterBand::AddPixelFunctionArgument(const char *pszArg,
     473             :                                                     const char *pszValue)
     474             : {
     475        2674 :     m_poPrivate->m_oFunctionArgs.emplace_back(pszArg, pszValue);
     476        2674 : }
     477             : 
     478             : /************************************************************************/
     479             : /*                      SetPixelFunctionLanguage()                      */
     480             : /************************************************************************/
     481             : 
     482             : /**
     483             :  * Set the language of the pixel function.
     484             :  *
     485             :  * @param pszLanguage Language of the pixel function (only "C" and "Python"
     486             :  * are supported currently)
     487             :  * @since GDAL 2.3
     488             :  */
     489           1 : void VRTDerivedRasterBand::SetPixelFunctionLanguage(const char *pszLanguage)
     490             : {
     491           1 :     m_poPrivate->m_osLanguage = pszLanguage;
     492           1 : }
     493             : 
     494             : /************************************************************************/
     495             : /*                   SetSkipNonContributingSources()                    */
     496             : /************************************************************************/
     497             : 
     498             : /** Whether sources that do not intersect the VRTRasterBand RasterIO() requested
     499             :  * region should be omitted. By default, data for all sources, including ones
     500             :  * that do not intersect it, are passed to the pixel function. By setting this
     501             :  * parameter to true, only sources that intersect the requested region will be
     502             :  * passed.
     503             :  *
     504             :  * @param bSkip whether to skip non-contributing sources
     505             :  *
     506             :  * @since 3.12
     507             :  */
     508           5 : void VRTDerivedRasterBand::SetSkipNonContributingSources(bool bSkip)
     509             : {
     510           5 :     m_poPrivate->m_bSkipNonContributingSources = bSkip;
     511           5 :     m_poPrivate->m_bSkipNonContributingSourcesSpecified = true;
     512           5 : }
     513             : 
     514             : /************************************************************************/
     515             : /*                       SetSourceTransferType()                        */
     516             : /************************************************************************/
     517             : 
     518             : /**
     519             :  * Set the transfer type to be used to obtain pixel information from
     520             :  * all of the sources.  If unset, the transfer type used will be the
     521             :  * same as the derived band data type.  This makes it possible, for
     522             :  * example, to pass CFloat32 source pixels to the pixel function, even
     523             :  * if the pixel function generates a raster for a derived band that
     524             :  * is of type Byte.
     525             :  *
     526             :  * @param eDataTypeIn Data type to use to obtain pixel information from
     527             :  * the sources to be passed to the derived band pixel function.
     528             :  */
     529          21 : void VRTDerivedRasterBand::SetSourceTransferType(GDALDataType eDataTypeIn)
     530             : {
     531          21 :     eSourceTransferType = eDataTypeIn;
     532          21 : }
     533             : 
     534             : /************************************************************************/
     535             : /*                          InitializePython()                          */
     536             : /************************************************************************/
     537             : 
     538         463 : bool VRTDerivedRasterBand::InitializePython()
     539             : {
     540         463 :     if (m_poPrivate->m_bPythonInitializationDone)
     541         397 :         return m_poPrivate->m_bPythonInitializationSuccess;
     542             : 
     543          66 :     m_poPrivate->m_bPythonInitializationDone = true;
     544          66 :     m_poPrivate->m_bPythonInitializationSuccess = false;
     545             : 
     546          66 :     const size_t nIdxDot = osFuncName.rfind(".");
     547         132 :     CPLString osPythonModule;
     548         132 :     CPLString osPythonFunction;
     549          66 :     if (nIdxDot != std::string::npos)
     550             :     {
     551          29 :         osPythonModule = osFuncName.substr(0, nIdxDot);
     552          29 :         osPythonFunction = osFuncName.substr(nIdxDot + 1);
     553             :     }
     554             :     else
     555             :     {
     556          37 :         osPythonFunction = osFuncName;
     557             :     }
     558             : 
     559             : #ifndef GDAL_VRT_DISABLE_PYTHON
     560             :     const char *pszPythonEnabled =
     561          66 :         CPLGetConfigOption("GDAL_VRT_ENABLE_PYTHON", nullptr);
     562             : #else
     563             :     const char *pszPythonEnabled = "NO";
     564             : #endif
     565             :     const CPLString osPythonEnabled(
     566         132 :         pszPythonEnabled ? pszPythonEnabled : GDAL_VRT_ENABLE_PYTHON_DEFAULT);
     567             : 
     568          66 :     if (EQUAL(osPythonEnabled, "TRUSTED_MODULES"))
     569             :     {
     570          12 :         bool bIsTrustedModule = false;
     571             :         const CPLString osVRTTrustedModules(
     572          12 :             CPLGetConfigOption("GDAL_VRT_PYTHON_TRUSTED_MODULES", ""));
     573          12 :         if (!osPythonModule.empty())
     574             :         {
     575             :             char **papszTrustedModules =
     576          10 :                 CSLTokenizeString2(osVRTTrustedModules, ",", 0);
     577          23 :             for (char **papszIter = papszTrustedModules;
     578          23 :                  !bIsTrustedModule && papszIter && *papszIter; ++papszIter)
     579             :             {
     580          13 :                 const char *pszIterModule = *papszIter;
     581          13 :                 size_t nIterModuleLen = strlen(pszIterModule);
     582          13 :                 if (nIterModuleLen > 2 &&
     583          12 :                     strncmp(pszIterModule + nIterModuleLen - 2, ".*", 2) == 0)
     584             :                 {
     585           2 :                     bIsTrustedModule =
     586           2 :                         (strncmp(osPythonModule, pszIterModule,
     587           3 :                                  nIterModuleLen - 2) == 0) &&
     588           1 :                         (osPythonModule.size() == nIterModuleLen - 2 ||
     589           0 :                          (osPythonModule.size() >= nIterModuleLen &&
     590           0 :                           osPythonModule[nIterModuleLen - 1] == '.'));
     591             :                 }
     592          11 :                 else if (nIterModuleLen >= 1 &&
     593          11 :                          pszIterModule[nIterModuleLen - 1] == '*')
     594             :                 {
     595           4 :                     bIsTrustedModule = (strncmp(osPythonModule, pszIterModule,
     596             :                                                 nIterModuleLen - 1) == 0);
     597             :                 }
     598             :                 else
     599             :                 {
     600           7 :                     bIsTrustedModule =
     601           7 :                         (strcmp(osPythonModule, pszIterModule) == 0);
     602             :                 }
     603             :             }
     604          10 :             CSLDestroy(papszTrustedModules);
     605             :         }
     606             : 
     607          12 :         if (!bIsTrustedModule)
     608             :         {
     609           7 :             if (osPythonModule.empty())
     610             :             {
     611           2 :                 CPLError(
     612             :                     CE_Failure, CPLE_AppDefined,
     613             :                     "Python code needs to be executed, but it uses inline code "
     614             :                     "in the VRT whereas the current policy is to trust only "
     615             :                     "code from external trusted modules (defined in the "
     616             :                     "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option). "
     617             :                     "If you trust the code in %s, you can set the "
     618             :                     "GDAL_VRT_ENABLE_PYTHON configuration option to YES.",
     619           2 :                     GetDataset() ? GetDataset()->GetDescription()
     620             :                                  : "(unknown VRT)");
     621             :             }
     622           5 :             else if (osVRTTrustedModules.empty())
     623             :             {
     624           2 :                 CPLError(
     625             :                     CE_Failure, CPLE_AppDefined,
     626             :                     "Python code needs to be executed, but it uses code "
     627             :                     "from module '%s', whereas the current policy is to "
     628             :                     "trust only code from modules defined in the "
     629             :                     "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option, "
     630             :                     "which is currently unset. "
     631             :                     "If you trust the code in '%s', you can add module '%s' "
     632             :                     "to GDAL_VRT_PYTHON_TRUSTED_MODULES (or set the "
     633             :                     "GDAL_VRT_ENABLE_PYTHON configuration option to YES).",
     634             :                     osPythonModule.c_str(),
     635           1 :                     GetDataset() ? GetDataset()->GetDescription()
     636             :                                  : "(unknown VRT)",
     637             :                     osPythonModule.c_str());
     638             :             }
     639             :             else
     640             :             {
     641           8 :                 CPLError(
     642             :                     CE_Failure, CPLE_AppDefined,
     643             :                     "Python code needs to be executed, but it uses code "
     644             :                     "from module '%s', whereas the current policy is to "
     645             :                     "trust only code from modules '%s' (defined in the "
     646             :                     "GDAL_VRT_PYTHON_TRUSTED_MODULES configuration option). "
     647             :                     "If you trust the code in '%s', you can add module '%s' "
     648             :                     "to GDAL_VRT_PYTHON_TRUSTED_MODULES (or set the "
     649             :                     "GDAL_VRT_ENABLE_PYTHON configuration option to YES).",
     650             :                     osPythonModule.c_str(), osVRTTrustedModules.c_str(),
     651           4 :                     GetDataset() ? GetDataset()->GetDescription()
     652             :                                  : "(unknown VRT)",
     653             :                     osPythonModule.c_str());
     654             :             }
     655           7 :             return false;
     656             :         }
     657             :     }
     658             : 
     659             : #ifdef disabled_because_this_is_probably_broken_by_design
     660             :     // See https://lwn.net/Articles/574215/
     661             :     // and http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
     662             :     else if (EQUAL(osPythonEnabled, "IF_SAFE"))
     663             :     {
     664             :         bool bSafe = true;
     665             :         // If the function comes from another module, then we don't know
     666             :         if (!osPythonModule.empty())
     667             :         {
     668             :             CPLDebug("VRT", "Python function is from another module");
     669             :             bSafe = false;
     670             :         }
     671             : 
     672             :         CPLString osCode(m_poPrivate->m_osCode);
     673             : 
     674             :         // Reject all imports except a few trusted modules
     675             :         const char *const apszTrustedImports[] = {
     676             :             "import math",
     677             :             "from math import",
     678             :             "import numpy",  // caution: numpy has lots of I/O functions !
     679             :             "from numpy import",
     680             :             // TODO: not sure if importing arbitrary stuff from numba is OK
     681             :             // so let's just restrict to jit.
     682             :             "from numba import jit",
     683             : 
     684             :             // Not imports but still whitelisted, whereas other __ is banned
     685             :             "__init__",
     686             :             "__call__",
     687             :         };
     688             :         for (size_t i = 0; i < CPL_ARRAYSIZE(apszTrustedImports); ++i)
     689             :         {
     690             :             osCode.replaceAll(CPLString(apszTrustedImports[i]), "");
     691             :         }
     692             : 
     693             :         // Some dangerous built-in functions or numpy functions
     694             :         const char *const apszUntrusted[] = {
     695             :             "import",  // and __import__
     696             :             "eval",       "compile", "open",
     697             :             "load",        // reload, numpy.load
     698             :             "file",        // and exec_file, numpy.fromfile, numpy.tofile
     699             :             "input",       // and raw_input
     700             :             "save",        // numpy.save
     701             :             "memmap",      // numpy.memmap
     702             :             "DataSource",  // numpy.DataSource
     703             :             "genfromtxt",  // numpy.genfromtxt
     704             :             "getattr",
     705             :             "ctypeslib",  // numpy.ctypeslib
     706             :             "testing",    // numpy.testing
     707             :             "dump",       // numpy.ndarray.dump
     708             :             "fromregex",  // numpy.fromregex
     709             :             "__"};
     710             :         for (size_t i = 0; i < CPL_ARRAYSIZE(apszUntrusted); ++i)
     711             :         {
     712             :             if (osCode.find(apszUntrusted[i]) != std::string::npos)
     713             :             {
     714             :                 CPLDebug("VRT", "Found '%s' word in Python code",
     715             :                          apszUntrusted[i]);
     716             :                 bSafe = false;
     717             :             }
     718             :         }
     719             : 
     720             :         if (!bSafe)
     721             :         {
     722             :             CPLError(CE_Failure, CPLE_AppDefined,
     723             :                      "Python code needs to be executed, but we cannot verify "
     724             :                      "if it is safe, so this is disabled by default. "
     725             :                      "If you trust the code in %s, you can set the "
     726             :                      "GDAL_VRT_ENABLE_PYTHON configuration option to YES.",
     727             :                      GetDataset() ? GetDataset()->GetDescription()
     728             :                                   : "(unknown VRT)");
     729             :             return false;
     730             :         }
     731             :     }
     732             : #endif  // disabled_because_this_is_probably_broken_by_design
     733             : 
     734          55 :     else if (!EQUAL(osPythonEnabled, "YES") && !EQUAL(osPythonEnabled, "ON") &&
     735           1 :              !EQUAL(osPythonEnabled, "TRUE"))
     736             :     {
     737           1 :         if (pszPythonEnabled == nullptr)
     738             :         {
     739             :             // Note: this is dead code with our current default policy
     740             :             // GDAL_VRT_ENABLE_PYTHON == "TRUSTED_MODULES"
     741           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     742             :                      "Python code needs to be executed, but this is "
     743             :                      "disabled by default. If you trust the code in %s, "
     744             :                      "you can set the GDAL_VRT_ENABLE_PYTHON configuration "
     745             :                      "option to YES.",
     746           0 :                      GetDataset() ? GetDataset()->GetDescription()
     747             :                                   : "(unknown VRT)");
     748             :         }
     749             :         else
     750             :         {
     751           1 :             CPLError(
     752             :                 CE_Failure, CPLE_AppDefined,
     753             :                 "Python code in %s needs to be executed, but this has been "
     754             :                 "explicitly disabled.",
     755           1 :                 GetDataset() ? GetDataset()->GetDescription()
     756             :                              : "(unknown VRT)");
     757             :         }
     758           1 :         return false;
     759             :     }
     760             : 
     761          58 :     if (!GDALPythonInitialize())
     762           2 :         return false;
     763             : 
     764             :     // Whether we should just use our own global mutex, in addition to Python
     765             :     // GIL locking.
     766         112 :     m_poPrivate->m_bExclusiveLock =
     767          56 :         CPLTestBool(CPLGetConfigOption("GDAL_VRT_PYTHON_EXCLUSIVE_LOCK", "NO"));
     768             : 
     769             :     // numba jit'ification doesn't seem to be thread-safe, so force use of
     770             :     // lock now and at first execution of function. Later executions seem to
     771             :     // be thread-safe. This problem doesn't seem to appear for code in
     772             :     // regular files
     773             :     const bool bUseExclusiveLock =
     774         112 :         m_poPrivate->m_bExclusiveLock ||
     775          56 :         m_poPrivate->m_osCode.find("@jit") != std::string::npos;
     776         112 :     GIL_Holder oHolder(bUseExclusiveLock);
     777             : 
     778             :     // As we don't want to depend on numpy C API/ABI, we use a trick to build
     779             :     // a numpy array object. We define a Python function to which we pass a
     780             :     // Python buffer object.
     781             : 
     782             :     // We need to build a unique module name, otherwise this will crash in
     783             :     // multithreaded use cases.
     784         112 :     CPLString osModuleName(CPLSPrintf("gdal_vrt_module_%p", this));
     785         112 :     PyObject *poCompiledString = Py_CompileString(
     786             :         ("import numpy\n"
     787             :          "def GDALCreateNumpyArray(buffer, dtype, height, width):\n"
     788             :          "    return numpy.frombuffer(buffer, str(dtype.decode('ascii')))."
     789             :          "reshape([height, width])\n"
     790          56 :          "\n" +
     791          56 :          m_poPrivate->m_osCode)
     792             :             .c_str(),
     793             :         osModuleName, Py_file_input);
     794          56 :     if (poCompiledString == nullptr || PyErr_Occurred())
     795             :     {
     796           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Couldn't compile code:\n%s",
     797           2 :                  GetPyExceptionString().c_str());
     798           1 :         return false;
     799             :     }
     800             :     PyObject *poModule =
     801          55 :         PyImport_ExecCodeModule(osModuleName, poCompiledString);
     802          55 :     Py_DecRef(poCompiledString);
     803             : 
     804          55 :     if (poModule == nullptr || PyErr_Occurred())
     805             :     {
     806           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     807           2 :                  GetPyExceptionString().c_str());
     808           1 :         return false;
     809             :     }
     810             : 
     811             :     // Fetch user computation function
     812          54 :     if (!osPythonModule.empty())
     813             :     {
     814          24 :         PyObject *poUserModule = PyImport_ImportModule(osPythonModule);
     815          24 :         if (poUserModule == nullptr || PyErr_Occurred())
     816             :         {
     817           1 :             CPLString osException = GetPyExceptionString();
     818           1 :             if (!osException.empty() && osException.back() == '\n')
     819             :             {
     820           1 :                 osException.pop_back();
     821             :             }
     822           1 :             if (osException.find("ModuleNotFoundError") == 0)
     823             :             {
     824           1 :                 osException += ". You may need to define PYTHONPATH";
     825             :             }
     826           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", osException.c_str());
     827           1 :             Py_DecRef(poModule);
     828           1 :             return false;
     829             :         }
     830          46 :         m_poPrivate->m_poUserFunction =
     831          23 :             PyObject_GetAttrString(poUserModule, osPythonFunction);
     832          23 :         Py_DecRef(poUserModule);
     833             :     }
     834             :     else
     835             :     {
     836          60 :         m_poPrivate->m_poUserFunction =
     837          30 :             PyObject_GetAttrString(poModule, osPythonFunction);
     838             :     }
     839          53 :     if (m_poPrivate->m_poUserFunction == nullptr || PyErr_Occurred())
     840             :     {
     841           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     842           2 :                  GetPyExceptionString().c_str());
     843           1 :         Py_DecRef(poModule);
     844           1 :         return false;
     845             :     }
     846          52 :     if (!PyCallable_Check(m_poPrivate->m_poUserFunction))
     847             :     {
     848           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Object '%s' is not callable",
     849             :                  osPythonFunction.c_str());
     850           1 :         Py_DecRef(poModule);
     851           1 :         return false;
     852             :     }
     853             : 
     854             :     // Fetch our GDALCreateNumpyArray python function
     855         102 :     m_poPrivate->m_poGDALCreateNumpyArray =
     856          51 :         PyObject_GetAttrString(poModule, "GDALCreateNumpyArray");
     857          51 :     if (m_poPrivate->m_poGDALCreateNumpyArray == nullptr || PyErr_Occurred())
     858             :     {
     859             :         // Shouldn't happen normally...
     860           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     861           0 :                  GetPyExceptionString().c_str());
     862           0 :         Py_DecRef(poModule);
     863           0 :         return false;
     864             :     }
     865          51 :     Py_DecRef(poModule);
     866             : 
     867          51 :     m_poPrivate->m_bPythonInitializationSuccess = true;
     868          51 :     return true;
     869             : }
     870             : 
     871        2782 : CPLErr VRTDerivedRasterBand::GetPixelFunctionArguments(
     872             :     const CPLString &osMetadata,
     873             :     const std::vector<int> &anMapBufferIdxToSourceIdx, int nXOff, int nYOff,
     874             :     std::vector<std::pair<CPLString, CPLString>> &oAdditionalArgs)
     875             : {
     876             : 
     877        5564 :     auto poArgs = CPLXMLTreeCloser(CPLParseXMLString(osMetadata));
     878        5564 :     if (poArgs != nullptr && poArgs->eType == CXT_Element &&
     879        2782 :         !strcmp(poArgs->pszValue, "PixelFunctionArgumentsList"))
     880             :     {
     881       12737 :         for (CPLXMLNode *psIter = poArgs->psChild; psIter != nullptr;
     882        9955 :              psIter = psIter->psNext)
     883             :         {
     884        9957 :             if (psIter->eType == CXT_Element &&
     885        9957 :                 !strcmp(psIter->pszValue, "Argument"))
     886             :             {
     887        9957 :                 CPLString osName, osType, osValue;
     888        9957 :                 auto pszName = CPLGetXMLValue(psIter, "name", nullptr);
     889        9957 :                 if (pszName != nullptr)
     890        5659 :                     osName = pszName;
     891        9957 :                 auto pszType = CPLGetXMLValue(psIter, "type", nullptr);
     892        9957 :                 if (pszType != nullptr)
     893        9957 :                     osType = pszType;
     894        9957 :                 auto pszValue = CPLGetXMLValue(psIter, "value", nullptr);
     895        9957 :                 if (pszValue != nullptr)
     896        4679 :                     osValue = pszValue;
     897        9957 :                 if (osType == "constant" && osValue != "" && osName != "")
     898           1 :                     oAdditionalArgs.push_back(
     899           2 :                         std::pair<CPLString, CPLString>(osName, osValue));
     900        9957 :                 if (osType == "builtin")
     901             :                 {
     902        4298 :                     const CPLString &osArgName = osValue;
     903        4298 :                     CPLString osVal;
     904        4298 :                     double dfVal = 0;
     905             : 
     906        4298 :                     int success(FALSE);
     907        4298 :                     if (osArgName == "NoData")
     908        2772 :                         dfVal = this->GetNoDataValue(&success);
     909        1526 :                     else if (osArgName == "scale")
     910           3 :                         dfVal = this->GetScale(&success);
     911        1523 :                     else if (osArgName == "offset")
     912           2 :                         dfVal = this->GetOffset(&success);
     913        1521 :                     else if (osArgName == "xoff")
     914             :                     {
     915         380 :                         dfVal = static_cast<double>(nXOff);
     916         380 :                         success = true;
     917             :                     }
     918        1141 :                     else if (osArgName == "yoff")
     919             :                     {
     920         380 :                         dfVal = static_cast<double>(nYOff);
     921         380 :                         success = true;
     922             :                     }
     923         761 :                     else if (osArgName == "crs")
     924             :                     {
     925             :                         const auto *crs =
     926           5 :                             GetDataset()->GetSpatialRefRasterOnly();
     927           5 :                         if (crs)
     928             :                         {
     929             :                             osVal =
     930           4 :                                 std::to_string(reinterpret_cast<size_t>(crs));
     931           4 :                             success = true;
     932             :                         }
     933             :                         else
     934             :                         {
     935           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
     936             :                                      "VRTDataset has no <SRS>");
     937             :                         }
     938             :                     }
     939         756 :                     else if (osArgName == "geotransform")
     940             :                     {
     941         380 :                         GDALGeoTransform gt;
     942         380 :                         if (GetDataset()->GetGeoTransform(gt) != CE_None)
     943             :                         {
     944             :                             // Do not fail here because the argument is most
     945             :                             // likely not needed by the pixel function. If it
     946             :                             // is needed, the pixel function can emit the error.
     947         185 :                             continue;
     948             :                         }
     949         195 :                         osVal = gt.ToString();
     950         195 :                         success = true;
     951             :                     }
     952         376 :                     else if (osArgName == "source_names")
     953             :                     {
     954         992 :                         for (size_t iBuffer = 0;
     955         992 :                              iBuffer < anMapBufferIdxToSourceIdx.size();
     956             :                              iBuffer++)
     957             :                         {
     958             :                             const int iSource =
     959         616 :                                 anMapBufferIdxToSourceIdx[iBuffer];
     960             :                             const VRTSource *poSource =
     961         616 :                                 m_papoSources[iSource].get();
     962             : 
     963         616 :                             if (iBuffer > 0)
     964             :                             {
     965         247 :                                 osVal += "|";
     966             :                             }
     967             : 
     968         616 :                             const auto &osSourceName = poSource->GetName();
     969         616 :                             if (osSourceName.empty())
     970             :                             {
     971          42 :                                 osVal += "B" + std::to_string(iBuffer + 1);
     972             :                             }
     973             :                             else
     974             :                             {
     975         574 :                                 osVal += osSourceName;
     976             :                             }
     977             :                         }
     978             : 
     979         376 :                         success = true;
     980             :                     }
     981             :                     else
     982             :                     {
     983           0 :                         CPLError(
     984             :                             CE_Failure, CPLE_NotSupported,
     985             :                             "PixelFunction builtin argument %s not supported",
     986             :                             osArgName.c_str());
     987           0 :                         return CE_Failure;
     988             :                     }
     989        4113 :                     if (!success)
     990             :                     {
     991        2651 :                         if (CPLTestBool(
     992             :                                 CPLGetXMLValue(psIter, "optional", "false")))
     993        2649 :                             continue;
     994             : 
     995           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
     996             :                                  "Raster has no %s", osValue.c_str());
     997           2 :                         return CE_Failure;
     998             :                     }
     999             : 
    1000        1462 :                     if (osVal.empty())
    1001             :                     {
    1002         894 :                         osVal = CPLSPrintf("%.17g", dfVal);
    1003             :                     }
    1004             : 
    1005        1462 :                     oAdditionalArgs.push_back(
    1006        2924 :                         std::pair<CPLString, CPLString>(osArgName, osVal));
    1007        1462 :                     CPLDebug("VRT",
    1008             :                              "Added builtin pixel function argument %s = %s",
    1009             :                              osArgName.c_str(), osVal.c_str());
    1010             :                 }
    1011             :             }
    1012             :         }
    1013             :     }
    1014             : 
    1015        2780 :     return CE_None;
    1016             : }
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                   CreateMapBufferIdxToSourceIdx()                    */
    1020             : /************************************************************************/
    1021             : 
    1022        3310 : static bool CreateMapBufferIdxToSourceIdx(
    1023             :     const std::vector<std::unique_ptr<VRTSource>> &apoSources,
    1024             :     bool bSkipNonContributingSources, int nXOff, int nYOff, int nXSize,
    1025             :     int nYSize, int nBufXSize, int nBufYSize, GDALRasterIOExtraArg *psExtraArg,
    1026             :     bool &bCreateMapBufferIdxToSourceIdxHasRun,
    1027             :     std::vector<int> &anMapBufferIdxToSourceIdx,
    1028             :     bool &bSkipOutputBufferInitialization)
    1029             : {
    1030        3310 :     CPLAssert(!bCreateMapBufferIdxToSourceIdxHasRun);
    1031        3310 :     bCreateMapBufferIdxToSourceIdxHasRun = true;
    1032        3310 :     anMapBufferIdxToSourceIdx.reserve(apoSources.size());
    1033      107261 :     for (int iSource = 0; iSource < static_cast<int>(apoSources.size());
    1034             :          iSource++)
    1035             :     {
    1036      103965 :         if (bSkipNonContributingSources &&
    1037          14 :             apoSources[iSource]->IsSimpleSource())
    1038             :         {
    1039          14 :             bool bError = false;
    1040             :             double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize;
    1041             :             int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
    1042             :             int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
    1043             :             auto poSource =
    1044          14 :                 static_cast<VRTSimpleSource *>(apoSources[iSource].get());
    1045          14 :             if (!poSource->GetSrcDstWindow(
    1046             :                     nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
    1047             :                     psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
    1048             :                     &dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize,
    1049             :                     &nReqYSize, &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize,
    1050             :                     bError))
    1051             :             {
    1052           4 :                 if (bError)
    1053             :                 {
    1054           0 :                     return false;
    1055             :                 }
    1056             : 
    1057             :                 // Skip non contributing source
    1058           4 :                 bSkipOutputBufferInitialization = false;
    1059           4 :                 continue;
    1060             :             }
    1061             :         }
    1062             : 
    1063      103947 :         anMapBufferIdxToSourceIdx.push_back(iSource);
    1064             :     }
    1065        3310 :     return true;
    1066             : }
    1067             : 
    1068             : /************************************************************************/
    1069             : /*                             IRasterIO()                              */
    1070             : /************************************************************************/
    1071             : 
    1072             : /**
    1073             :  * Read/write a region of image data for this band.
    1074             :  *
    1075             :  * Each of the sources for this derived band will be read and passed to
    1076             :  * the derived band pixel function.  The pixel function is responsible
    1077             :  * for applying whatever algorithm is necessary to generate this band's
    1078             :  * pixels from the sources.
    1079             :  *
    1080             :  * The sources will be read using the transfer type specified for sources
    1081             :  * using SetSourceTransferType().  If no transfer type has been set for
    1082             :  * this derived band, the band's data type will be used as the transfer type.
    1083             :  *
    1084             :  * @see gdalrasterband
    1085             :  *
    1086             :  * @param eRWFlag Either GF_Read to read a region of data, or GT_Write to
    1087             :  * write a region of data.
    1088             :  *
    1089             :  * @param nXOff The pixel offset to the top left corner of the region
    1090             :  * of the band to be accessed.  This would be zero to start from the left side.
    1091             :  *
    1092             :  * @param nYOff The line offset to the top left corner of the region
    1093             :  * of the band to be accessed.  This would be zero to start from the top.
    1094             :  *
    1095             :  * @param nXSize The width of the region of the band to be accessed in pixels.
    1096             :  *
    1097             :  * @param nYSize The height of the region of the band to be accessed in lines.
    1098             :  *
    1099             :  * @param pData The buffer into which the data should be read, or from which
    1100             :  * it should be written.  This buffer must contain at least nBufXSize *
    1101             :  * nBufYSize words of type eBufType.  It is organized in left to right,
    1102             :  * top to bottom pixel order.  Spacing is controlled by the nPixelSpace,
    1103             :  * and nLineSpace parameters.
    1104             :  *
    1105             :  * @param nBufXSize The width of the buffer image into which the desired
    1106             :  * region is to be read, or from which it is to be written.
    1107             :  *
    1108             :  * @param nBufYSize The height of the buffer image into which the desired
    1109             :  * region is to be read, or from which it is to be written.
    1110             :  *
    1111             :  * @param eBufType The type of the pixel values in the pData data buffer.  The
    1112             :  * pixel values will automatically be translated to/from the GDALRasterBand
    1113             :  * data type as needed.
    1114             :  *
    1115             :  * @param nPixelSpace The byte offset from the start of one pixel value in
    1116             :  * pData to the start of the next pixel value within a scanline.  If defaulted
    1117             :  * (0) the size of the datatype eBufType is used.
    1118             :  *
    1119             :  * @param nLineSpace The byte offset from the start of one scanline in
    1120             :  * pData to the start of the next.  If defaulted the size of the datatype
    1121             :  * eBufType * nBufXSize is used.
    1122             :  *
    1123             :  * @return CE_Failure if the access fails, otherwise CE_None.
    1124             :  */
    1125        4311 : CPLErr VRTDerivedRasterBand::IRasterIO(
    1126             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1127             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1128             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
    1129             : {
    1130        4311 :     if (eRWFlag == GF_Write)
    1131             :     {
    1132           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1133             :                  "Writing through VRTSourcedRasterBand is not supported.");
    1134           1 :         return CE_Failure;
    1135             :     }
    1136             : 
    1137        8620 :     const std::string osFctId("VRTDerivedRasterBand::IRasterIO");
    1138        8620 :     GDALAntiRecursionGuard oGuard(osFctId);
    1139        4310 :     if (oGuard.GetCallDepth() >= 32)
    1140             :     {
    1141           0 :         CPLError(
    1142             :             CE_Failure, CPLE_AppDefined,
    1143             :             "VRTDerivedRasterBand::IRasterIO(): Recursion detected (case 1)");
    1144           0 :         return CE_Failure;
    1145             :     }
    1146             : 
    1147       12930 :     GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
    1148             :     // Allow multiple recursion depths on the same dataset in case the split strategy is applied
    1149        4310 :     if (oGuard2.GetCallDepth() > 15)
    1150             :     {
    1151           0 :         CPLError(
    1152             :             CE_Failure, CPLE_AppDefined,
    1153             :             "VRTDerivedRasterBand::IRasterIO(): Recursion detected (case 2)");
    1154           0 :         return CE_Failure;
    1155             :     }
    1156             : 
    1157             :     if constexpr (sizeof(GSpacing) > sizeof(int))
    1158             :     {
    1159        4310 :         if (nLineSpace > INT_MAX)
    1160             :         {
    1161           0 :             if (nBufYSize == 1)
    1162             :             {
    1163           0 :                 nLineSpace = 0;
    1164             :             }
    1165             :             else
    1166             :             {
    1167           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1168             :                          "VRTDerivedRasterBand::IRasterIO(): nLineSpace > "
    1169             :                          "INT_MAX not supported");
    1170           0 :                 return CE_Failure;
    1171             :             }
    1172             :         }
    1173             :     }
    1174             : 
    1175             :     /* -------------------------------------------------------------------- */
    1176             :     /*      Do we have overviews that would be appropriate to satisfy       */
    1177             :     /*      this request?                                                   */
    1178             :     /* -------------------------------------------------------------------- */
    1179        4310 :     auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
    1180        4310 :     if (l_poDS &&
    1181        8618 :         l_poDS->m_apoOverviews.empty() &&  // do not use virtual overviews
    1182        8620 :         (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
    1183             :     {
    1184           0 :         if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1185             :                              nBufXSize, nBufYSize, eBufType, nPixelSpace,
    1186           0 :                              nLineSpace, psExtraArg) == CE_None)
    1187           0 :             return CE_None;
    1188             :     }
    1189             : 
    1190        4310 :     const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    1191        4310 :     GDALDataType eSrcType = eSourceTransferType;
    1192        4310 :     if (eSrcType == GDT_Unknown || eSrcType >= GDT_TypeCount)
    1193             :     {
    1194             :         // Check the largest data type for all sources
    1195        3333 :         GDALDataType eAllSrcType = GDT_Unknown;
    1196      107475 :         for (auto &poSource : m_papoSources)
    1197             :         {
    1198      104143 :             if (poSource->IsSimpleSource())
    1199             :             {
    1200             :                 const auto poSS =
    1201      104142 :                     static_cast<VRTSimpleSource *>(poSource.get());
    1202      104142 :                 auto l_poBand = poSS->GetRasterBand();
    1203      104142 :                 if (l_poBand)
    1204             :                 {
    1205      104142 :                     eAllSrcType = GDALDataTypeUnion(
    1206             :                         eAllSrcType, l_poBand->GetRasterDataType());
    1207             :                 }
    1208             :                 else
    1209             :                 {
    1210           0 :                     eAllSrcType = GDT_Unknown;
    1211           0 :                     break;
    1212             :                 }
    1213             :             }
    1214             :             else
    1215             :             {
    1216           1 :                 eAllSrcType = GDT_Unknown;
    1217           1 :                 break;
    1218             :             }
    1219             :         }
    1220             : 
    1221        3333 :         if (eAllSrcType != GDT_Unknown)
    1222        2947 :             eSrcType = GDALDataTypeUnion(eAllSrcType, eDataType);
    1223             :         else
    1224         386 :             eSrcType = GDALDataTypeUnion(GDT_Float64, eDataType);
    1225             :     }
    1226        4310 :     const int nSrcTypeSize = GDALGetDataTypeSizeBytes(eSrcType);
    1227             : 
    1228        8620 :     std::vector<int> anMapBufferIdxToSourceIdx;
    1229        4310 :     bool bSkipOutputBufferInitialization = !m_papoSources.empty();
    1230        4310 :     bool bCreateMapBufferIdxToSourceIdxHasRun = false;
    1231             : 
    1232             :     // If acquiring the region of interest in a single time is going
    1233             :     // to consume too much RAM, split in halves, and that recursively
    1234             :     // until we get below m_nAllowedRAMUsage.
    1235        4310 :     if (m_poPrivate->m_nAllowedRAMUsage > 0 && !m_papoSources.empty() &&
    1236       12538 :         nSrcTypeSize > 0 && nBufXSize == nXSize && nBufYSize == nYSize &&
    1237        3918 :         static_cast<GIntBig>(nBufXSize) * nBufYSize >
    1238        7836 :             m_poPrivate->m_nAllowedRAMUsage /
    1239        3918 :                 (static_cast<int>(m_papoSources.size()) * nSrcTypeSize))
    1240             :     {
    1241         999 :         bool bSplit = true;
    1242         999 :         if (m_poPrivate->m_bSkipNonContributingSources)
    1243             :         {
    1244             :             // More accurate check by comparing against the number of
    1245             :             // actually contributing sources.
    1246           0 :             if (!CreateMapBufferIdxToSourceIdx(
    1247           0 :                     m_papoSources, m_poPrivate->m_bSkipNonContributingSources,
    1248             :                     nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
    1249             :                     psExtraArg, bCreateMapBufferIdxToSourceIdxHasRun,
    1250             :                     anMapBufferIdxToSourceIdx, bSkipOutputBufferInitialization))
    1251             :             {
    1252           0 :                 return CE_Failure;
    1253             :             }
    1254           0 :             bSplit =
    1255           0 :                 !anMapBufferIdxToSourceIdx.empty() &&
    1256           0 :                 static_cast<GIntBig>(nBufXSize) * nBufYSize >
    1257           0 :                     m_poPrivate->m_nAllowedRAMUsage /
    1258           0 :                         (static_cast<int>(anMapBufferIdxToSourceIdx.size()) *
    1259             :                          nSrcTypeSize);
    1260             :         }
    1261         999 :         if (bSplit)
    1262             :         {
    1263         999 :             CPLErr eErr = SplitRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1264             :                                         pData, nBufXSize, nBufYSize, eBufType,
    1265             :                                         nPixelSpace, nLineSpace, psExtraArg);
    1266         999 :             if (eErr != CE_Warning)
    1267         999 :                 return eErr;
    1268             :         }
    1269             :     }
    1270             : 
    1271             :     /* ---- Get pixel function for band ---- */
    1272        3311 :     const std::pair<PixelFunc, std::string> *poPixelFunc = nullptr;
    1273        6622 :     std::vector<std::pair<CPLString, CPLString>> oAdditionalArgs;
    1274             : 
    1275        3311 :     if (EQUAL(m_poPrivate->m_osLanguage, "C"))
    1276             :     {
    1277             :         poPixelFunc =
    1278        2838 :             VRTDerivedRasterBand::GetPixelFunction(osFuncName.c_str());
    1279        2838 :         if (poPixelFunc == nullptr)
    1280             :         {
    1281           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1282             :                      "VRTDerivedRasterBand::IRasterIO:"
    1283             :                      "Derived band pixel function '%s' not registered.",
    1284             :                      osFuncName.c_str());
    1285           1 :             return CE_Failure;
    1286             :         }
    1287             :     }
    1288             : 
    1289             :     /* TODO: It would be nice to use a MallocBlock function for each
    1290             :        individual buffer that would recycle blocks of memory from a
    1291             :        cache by reassigning blocks that are nearly the same size.
    1292             :        A corresponding FreeBlock might only truly free if the total size
    1293             :        of freed blocks gets to be too great of a percentage of the size
    1294             :        of the allocated blocks. */
    1295             : 
    1296             :     // Get buffers for each source.
    1297        3310 :     const int nBufferRadius = m_poPrivate->m_nBufferRadius;
    1298        3310 :     if (nBufferRadius > (INT_MAX - nBufXSize) / 2 ||
    1299        3310 :         nBufferRadius > (INT_MAX - nBufYSize) / 2)
    1300             :     {
    1301           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1302             :                  "Integer overflow: "
    1303             :                  "nBufferRadius > (INT_MAX - nBufXSize) / 2 || "
    1304             :                  "nBufferRadius > (INT_MAX - nBufYSize) / 2)");
    1305           0 :         return CE_Failure;
    1306             :     }
    1307        3310 :     const int nExtBufXSize = nBufXSize + 2 * nBufferRadius;
    1308        3310 :     const int nExtBufYSize = nBufYSize + 2 * nBufferRadius;
    1309             : 
    1310        3310 :     if (!bCreateMapBufferIdxToSourceIdxHasRun)
    1311             :     {
    1312        3310 :         if (!CreateMapBufferIdxToSourceIdx(
    1313        3310 :                 m_papoSources, m_poPrivate->m_bSkipNonContributingSources,
    1314             :                 nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, psExtraArg,
    1315             :                 bCreateMapBufferIdxToSourceIdxHasRun, anMapBufferIdxToSourceIdx,
    1316             :                 bSkipOutputBufferInitialization))
    1317             :         {
    1318           0 :             return CE_Failure;
    1319             :         }
    1320             :     }
    1321             :     std::vector<std::unique_ptr<void, VSIFreeReleaser>> apBuffers(
    1322        6620 :         anMapBufferIdxToSourceIdx.size());
    1323      107257 :     for (size_t iBuffer = 0; iBuffer < anMapBufferIdxToSourceIdx.size();
    1324             :          ++iBuffer)
    1325             :     {
    1326      103947 :         apBuffers[iBuffer].reset(
    1327             :             VSI_MALLOC3_VERBOSE(nSrcTypeSize, nExtBufXSize, nExtBufYSize));
    1328      103947 :         if (apBuffers[iBuffer] == nullptr)
    1329             :         {
    1330           0 :             return CE_Failure;
    1331             :         }
    1332             : 
    1333      103947 :         bool bBufferInit = true;
    1334      103947 :         const int iSource = anMapBufferIdxToSourceIdx[iBuffer];
    1335      103947 :         if (m_papoSources[iSource]->IsSimpleSource())
    1336             :         {
    1337             :             const auto poSS =
    1338      103946 :                 static_cast<VRTSimpleSource *>(m_papoSources[iSource].get());
    1339      103946 :             auto l_poBand = poSS->GetRasterBand();
    1340      103946 :             if (l_poBand != nullptr && poSS->m_dfSrcXOff == 0.0 &&
    1341        1052 :                 poSS->m_dfSrcYOff == 0.0 &&
    1342        2104 :                 poSS->m_dfSrcXOff + poSS->m_dfSrcXSize ==
    1343        1052 :                     l_poBand->GetXSize() &&
    1344        2068 :                 poSS->m_dfSrcYOff + poSS->m_dfSrcYSize ==
    1345        1034 :                     l_poBand->GetYSize() &&
    1346        1034 :                 poSS->m_dfDstXOff == 0.0 && poSS->m_dfDstYOff == 0.0 &&
    1347      208919 :                 poSS->m_dfDstXOff + poSS->m_dfDstXSize == nRasterXSize &&
    1348        1027 :                 poSS->m_dfDstYOff + poSS->m_dfDstYSize == nRasterYSize)
    1349             :             {
    1350        1027 :                 if (m_papoSources[iSource]->GetType() ==
    1351        1027 :                     VRTSimpleSource::GetTypeStatic())
    1352        1000 :                     bBufferInit = false;
    1353             :             }
    1354             :             else
    1355             :             {
    1356      102919 :                 bSkipOutputBufferInitialization = false;
    1357             :             }
    1358             :         }
    1359             :         else
    1360             :         {
    1361           1 :             bSkipOutputBufferInitialization = false;
    1362             :         }
    1363      103947 :         if (bBufferInit)
    1364             :         {
    1365             :             /* ------------------------------------------------------------ */
    1366             :             /* #4045: Initialize the newly allocated buffers before handing */
    1367             :             /* them off to the sources. These buffers are packed, so we     */
    1368             :             /* don't need any special line-by-line handling when a nonzero  */
    1369             :             /* nodata value is set.                                         */
    1370             :             /* ------------------------------------------------------------ */
    1371      102947 :             if (!m_bNoDataValueSet || m_dfNoDataValue == 0)
    1372             :             {
    1373      102674 :                 memset(apBuffers[iBuffer].get(), 0,
    1374      102674 :                        static_cast<size_t>(nSrcTypeSize) * nExtBufXSize *
    1375      102674 :                            nExtBufYSize);
    1376             :             }
    1377             :             else
    1378             :             {
    1379         273 :                 GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0,
    1380         273 :                                 static_cast<GByte *>(apBuffers[iBuffer].get()),
    1381             :                                 eSrcType, nSrcTypeSize,
    1382         273 :                                 static_cast<GPtrDiff_t>(nExtBufXSize) *
    1383         273 :                                     nExtBufYSize);
    1384             :             }
    1385             :         }
    1386             :     }
    1387             : 
    1388             :     /* -------------------------------------------------------------------- */
    1389             :     /*      Initialize the buffer to some background value. Use the         */
    1390             :     /*      nodata value if available.                                      */
    1391             :     /* -------------------------------------------------------------------- */
    1392        3310 :     if (!bSkipOutputBufferInitialization)
    1393             :     {
    1394        2605 :         InitializeOutputBuffer(pData, nBufXSize, nBufYSize, eBufType,
    1395             :                                nPixelSpace, nLineSpace);
    1396             :     }
    1397             : 
    1398             :     // No contributing sources and SkipNonContributingSources mode ?
    1399             :     // Do not call the pixel function and just return the 0/nodata initialized
    1400             :     // output buffer.
    1401        3699 :     if (anMapBufferIdxToSourceIdx.empty() &&
    1402         389 :         m_poPrivate->m_bSkipNonContributingSources)
    1403             :     {
    1404           1 :         return CE_None;
    1405             :     }
    1406             : 
    1407             :     GDALRasterIOExtraArg sExtraArg;
    1408        3309 :     GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    1409             : 
    1410        3309 :     int nXShiftInBuffer = 0;
    1411        3309 :     int nYShiftInBuffer = 0;
    1412        3309 :     int nExtBufXSizeReq = nExtBufXSize;
    1413        3309 :     int nExtBufYSizeReq = nExtBufYSize;
    1414             : 
    1415        3309 :     int nXOffExt = nXOff;
    1416        3309 :     int nYOffExt = nYOff;
    1417        3309 :     int nXSizeExt = nXSize;
    1418        3309 :     int nYSizeExt = nYSize;
    1419             : 
    1420        3309 :     if (nBufferRadius)
    1421             :     {
    1422          88 :         double dfXRatio = static_cast<double>(nXSize) / nBufXSize;
    1423          88 :         double dfYRatio = static_cast<double>(nYSize) / nBufYSize;
    1424             : 
    1425          88 :         if (!sExtraArg.bFloatingPointWindowValidity)
    1426             :         {
    1427          88 :             sExtraArg.dfXOff = nXOff;
    1428          88 :             sExtraArg.dfYOff = nYOff;
    1429          88 :             sExtraArg.dfXSize = nXSize;
    1430          88 :             sExtraArg.dfYSize = nYSize;
    1431             :         }
    1432             : 
    1433          88 :         sExtraArg.dfXOff -= dfXRatio * nBufferRadius;
    1434          88 :         sExtraArg.dfYOff -= dfYRatio * nBufferRadius;
    1435          88 :         sExtraArg.dfXSize += 2 * dfXRatio * nBufferRadius;
    1436          88 :         sExtraArg.dfYSize += 2 * dfYRatio * nBufferRadius;
    1437          88 :         if (sExtraArg.dfXOff < 0)
    1438             :         {
    1439          88 :             nXShiftInBuffer = -static_cast<int>(sExtraArg.dfXOff / dfXRatio);
    1440          88 :             nExtBufXSizeReq -= nXShiftInBuffer;
    1441          88 :             sExtraArg.dfXSize += sExtraArg.dfXOff;
    1442          88 :             sExtraArg.dfXOff = 0;
    1443             :         }
    1444          88 :         if (sExtraArg.dfYOff < 0)
    1445             :         {
    1446          88 :             nYShiftInBuffer = -static_cast<int>(sExtraArg.dfYOff / dfYRatio);
    1447          88 :             nExtBufYSizeReq -= nYShiftInBuffer;
    1448          88 :             sExtraArg.dfYSize += sExtraArg.dfYOff;
    1449          88 :             sExtraArg.dfYOff = 0;
    1450             :         }
    1451          88 :         if (sExtraArg.dfXOff + sExtraArg.dfXSize > nRasterXSize)
    1452             :         {
    1453          88 :             nExtBufXSizeReq -= static_cast<int>(
    1454          88 :                 (sExtraArg.dfXOff + sExtraArg.dfXSize - nRasterXSize) /
    1455             :                 dfXRatio);
    1456          88 :             sExtraArg.dfXSize = nRasterXSize - sExtraArg.dfXOff;
    1457             :         }
    1458          88 :         if (sExtraArg.dfYOff + sExtraArg.dfYSize > nRasterYSize)
    1459             :         {
    1460          88 :             nExtBufYSizeReq -= static_cast<int>(
    1461          88 :                 (sExtraArg.dfYOff + sExtraArg.dfYSize - nRasterYSize) /
    1462             :                 dfYRatio);
    1463          88 :             sExtraArg.dfYSize = nRasterYSize - sExtraArg.dfYOff;
    1464             :         }
    1465             : 
    1466          88 :         nXOffExt = static_cast<int>(sExtraArg.dfXOff);
    1467          88 :         nYOffExt = static_cast<int>(sExtraArg.dfYOff);
    1468         176 :         nXSizeExt = std::min(static_cast<int>(sExtraArg.dfXSize + 0.5),
    1469          88 :                              nRasterXSize - nXOffExt);
    1470         176 :         nYSizeExt = std::min(static_cast<int>(sExtraArg.dfYSize + 0.5),
    1471          88 :                              nRasterYSize - nYOffExt);
    1472             :     }
    1473             : 
    1474             :     // Load values for sources into packed buffers.
    1475        3309 :     CPLErr eErr = CE_None;
    1476        6618 :     VRTSource::WorkingState oWorkingState;
    1477      107256 :     for (size_t iBuffer = 0;
    1478      107256 :          iBuffer < anMapBufferIdxToSourceIdx.size() && eErr == CE_None;
    1479             :          iBuffer++)
    1480             :     {
    1481      103947 :         const int iSource = anMapBufferIdxToSourceIdx[iBuffer];
    1482      103947 :         GByte *pabyBuffer = static_cast<GByte *>(apBuffers[iBuffer].get());
    1483      103947 :         eErr = static_cast<VRTSource *>(m_papoSources[iSource].get())
    1484      207894 :                    ->RasterIO(
    1485             :                        eSrcType, nXOffExt, nYOffExt, nXSizeExt, nYSizeExt,
    1486      103947 :                        pabyBuffer + (static_cast<size_t>(nYShiftInBuffer) *
    1487      103947 :                                          nExtBufXSize +
    1488      103947 :                                      nXShiftInBuffer) *
    1489      103947 :                                         nSrcTypeSize,
    1490             :                        nExtBufXSizeReq, nExtBufYSizeReq, eSrcType, nSrcTypeSize,
    1491      103947 :                        static_cast<GSpacing>(nSrcTypeSize) * nExtBufXSize,
    1492      103947 :                        &sExtraArg, oWorkingState);
    1493             : 
    1494             :         // Extend first lines
    1495      104035 :         for (int iY = 0; iY < nYShiftInBuffer; iY++)
    1496             :         {
    1497          88 :             memcpy(pabyBuffer +
    1498          88 :                        static_cast<size_t>(iY) * nExtBufXSize * nSrcTypeSize,
    1499          88 :                    pabyBuffer + static_cast<size_t>(nYShiftInBuffer) *
    1500          88 :                                     nExtBufXSize * nSrcTypeSize,
    1501          88 :                    static_cast<size_t>(nExtBufXSize) * nSrcTypeSize);
    1502             :         }
    1503             :         // Extend last lines
    1504      104035 :         for (int iY = nYShiftInBuffer + nExtBufYSizeReq; iY < nExtBufYSize;
    1505             :              iY++)
    1506             :         {
    1507          88 :             memcpy(pabyBuffer +
    1508          88 :                        static_cast<size_t>(iY) * nExtBufXSize * nSrcTypeSize,
    1509          88 :                    pabyBuffer + static_cast<size_t>(nYShiftInBuffer +
    1510          88 :                                                     nExtBufYSizeReq - 1) *
    1511          88 :                                     nExtBufXSize * nSrcTypeSize,
    1512          88 :                    static_cast<size_t>(nExtBufXSize) * nSrcTypeSize);
    1513             :         }
    1514             :         // Extend first cols
    1515      103947 :         if (nXShiftInBuffer)
    1516             :         {
    1517       10912 :             for (int iY = 0; iY < nExtBufYSize; iY++)
    1518             :             {
    1519       21648 :                 for (int iX = 0; iX < nXShiftInBuffer; iX++)
    1520             :                 {
    1521       10824 :                     memcpy(pabyBuffer +
    1522       10824 :                                static_cast<size_t>(iY * nExtBufXSize + iX) *
    1523       10824 :                                    nSrcTypeSize,
    1524       10824 :                            pabyBuffer +
    1525       10824 :                                (static_cast<size_t>(iY) * nExtBufXSize +
    1526       10824 :                                 nXShiftInBuffer) *
    1527       10824 :                                    nSrcTypeSize,
    1528             :                            nSrcTypeSize);
    1529             :                 }
    1530             :             }
    1531             :         }
    1532             :         // Extent last cols
    1533      103947 :         if (nXShiftInBuffer + nExtBufXSizeReq < nExtBufXSize)
    1534             :         {
    1535       10912 :             for (int iY = 0; iY < nExtBufYSize; iY++)
    1536             :             {
    1537       10824 :                 for (int iX = nXShiftInBuffer + nExtBufXSizeReq;
    1538       21648 :                      iX < nExtBufXSize; iX++)
    1539             :                 {
    1540       10824 :                     memcpy(pabyBuffer +
    1541       10824 :                                (static_cast<size_t>(iY) * nExtBufXSize + iX) *
    1542       10824 :                                    nSrcTypeSize,
    1543       10824 :                            pabyBuffer +
    1544       10824 :                                (static_cast<size_t>(iY) * nExtBufXSize +
    1545       10824 :                                 nXShiftInBuffer + nExtBufXSizeReq - 1) *
    1546       10824 :                                    nSrcTypeSize,
    1547             :                            nSrcTypeSize);
    1548             :                 }
    1549             :             }
    1550             :         }
    1551             :     }
    1552             : 
    1553             :     // Collect any pixel function arguments into oAdditionalArgs
    1554        3309 :     if (poPixelFunc != nullptr && !poPixelFunc->second.empty())
    1555             :     {
    1556        5564 :         if (GetPixelFunctionArguments(poPixelFunc->second,
    1557             :                                       anMapBufferIdxToSourceIdx, nXOff, nYOff,
    1558        2782 :                                       oAdditionalArgs) != CE_None)
    1559             :         {
    1560           2 :             eErr = CE_Failure;
    1561             :         }
    1562             :     }
    1563             : 
    1564             :     // Apply pixel function.
    1565        3309 :     const int nBufferCount = static_cast<int>(anMapBufferIdxToSourceIdx.size());
    1566        3309 :     if (eErr == CE_None && EQUAL(m_poPrivate->m_osLanguage, "Python"))
    1567             :     {
    1568             :         // numpy doesn't have native cint16/cint32/cfloat16
    1569         472 :         if (eSrcType == GDT_CInt16 || eSrcType == GDT_CInt32 ||
    1570             :             eSrcType == GDT_CFloat16)
    1571             :         {
    1572           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1573             :                      "CInt16/CInt32/CFloat16 data type not supported for "
    1574             :                      "SourceTransferType");
    1575          24 :             return CE_Failure;
    1576             :         }
    1577         470 :         if (eDataType == GDT_CInt16 || eDataType == GDT_CInt32 ||
    1578         466 :             eDataType == GDT_CFloat16)
    1579             :         {
    1580           7 :             CPLError(
    1581             :                 CE_Failure, CPLE_AppDefined,
    1582             :                 "CInt16/CInt32/CFloat16 data type not supported for data type");
    1583           7 :             return CE_Failure;
    1584             :         }
    1585             : 
    1586         463 :         if (!InitializePython())
    1587          15 :             return CE_Failure;
    1588             : 
    1589           0 :         std::unique_ptr<GByte, VSIFreeReleaser> pabyTmpBuffer;
    1590             :         // Do we need a temporary buffer or can we use directly the output
    1591             :         // buffer ?
    1592         448 :         if (nBufferRadius != 0 || eDataType != eBufType ||
    1593          25 :             nPixelSpace != nBufTypeSize ||
    1594          25 :             nLineSpace != static_cast<GSpacing>(nBufTypeSize) * nBufXSize)
    1595             :         {
    1596         423 :             pabyTmpBuffer.reset(static_cast<GByte *>(VSI_CALLOC_VERBOSE(
    1597             :                 static_cast<size_t>(nExtBufXSize) * nExtBufYSize,
    1598             :                 GDALGetDataTypeSizeBytes(eDataType))));
    1599         423 :             if (!pabyTmpBuffer)
    1600           0 :                 return CE_Failure;
    1601             :         }
    1602             : 
    1603             :         {
    1604             :             const bool bUseExclusiveLock =
    1605         896 :                 m_poPrivate->m_bExclusiveLock ||
    1606         499 :                 (m_poPrivate->m_bFirstTime &&
    1607          51 :                  m_poPrivate->m_osCode.find("@jit") != std::string::npos);
    1608         448 :             m_poPrivate->m_bFirstTime = false;
    1609         448 :             GIL_Holder oHolder(bUseExclusiveLock);
    1610             : 
    1611             :             // Prepare target numpy array
    1612         448 :             PyObject *poPyDstArray = GDALCreateNumpyArray(
    1613         448 :                 m_poPrivate->m_poGDALCreateNumpyArray,
    1614         871 :                 pabyTmpBuffer ? pabyTmpBuffer.get() : pData, eDataType,
    1615             :                 nExtBufYSize, nExtBufXSize);
    1616         448 :             if (!poPyDstArray)
    1617             :             {
    1618           0 :                 return CE_Failure;
    1619             :             }
    1620             : 
    1621             :             // Wrap source buffers as input numpy arrays
    1622         448 :             PyObject *pyArgInputArray = PyTuple_New(nBufferCount);
    1623         557 :             for (int i = 0; i < nBufferCount; i++)
    1624             :             {
    1625         109 :                 GByte *pabyBuffer = static_cast<GByte *>(apBuffers[i].get());
    1626         218 :                 PyObject *poPySrcArray = GDALCreateNumpyArray(
    1627         109 :                     m_poPrivate->m_poGDALCreateNumpyArray, pabyBuffer, eSrcType,
    1628             :                     nExtBufYSize, nExtBufXSize);
    1629         109 :                 CPLAssert(poPySrcArray);
    1630         109 :                 PyTuple_SetItem(pyArgInputArray, i, poPySrcArray);
    1631             :             }
    1632             : 
    1633             :             // Create arguments
    1634         448 :             PyObject *pyArgs = PyTuple_New(10);
    1635         448 :             PyTuple_SetItem(pyArgs, 0, pyArgInputArray);
    1636         448 :             PyTuple_SetItem(pyArgs, 1, poPyDstArray);
    1637         448 :             PyTuple_SetItem(pyArgs, 2, PyLong_FromLong(nXOff));
    1638         448 :             PyTuple_SetItem(pyArgs, 3, PyLong_FromLong(nYOff));
    1639         448 :             PyTuple_SetItem(pyArgs, 4, PyLong_FromLong(nXSize));
    1640         448 :             PyTuple_SetItem(pyArgs, 5, PyLong_FromLong(nYSize));
    1641         448 :             PyTuple_SetItem(pyArgs, 6, PyLong_FromLong(nRasterXSize));
    1642         448 :             PyTuple_SetItem(pyArgs, 7, PyLong_FromLong(nRasterYSize));
    1643         448 :             PyTuple_SetItem(pyArgs, 8, PyLong_FromLong(nBufferRadius));
    1644             : 
    1645         448 :             GDALGeoTransform gt;
    1646         448 :             if (GetDataset())
    1647         448 :                 GetDataset()->GetGeoTransform(gt);
    1648         448 :             PyObject *pyGT = PyTuple_New(6);
    1649        3136 :             for (int i = 0; i < 6; i++)
    1650        2688 :                 PyTuple_SetItem(pyGT, i, PyFloat_FromDouble(gt[i]));
    1651         448 :             PyTuple_SetItem(pyArgs, 9, pyGT);
    1652             : 
    1653             :             // Prepare kwargs
    1654         448 :             PyObject *pyKwargs = PyDict_New();
    1655         616 :             for (size_t i = 0; i < m_poPrivate->m_oFunctionArgs.size(); ++i)
    1656             :             {
    1657             :                 const char *pszKey =
    1658         168 :                     m_poPrivate->m_oFunctionArgs[i].first.c_str();
    1659             :                 const char *pszValue =
    1660         168 :                     m_poPrivate->m_oFunctionArgs[i].second.c_str();
    1661         168 :                 PyDict_SetItemString(
    1662             :                     pyKwargs, pszKey,
    1663             :                     PyBytes_FromStringAndSize(pszValue, strlen(pszValue)));
    1664             :             }
    1665             : 
    1666             :             // Call user function
    1667             :             PyObject *pRetValue =
    1668         448 :                 PyObject_Call(m_poPrivate->m_poUserFunction, pyArgs, pyKwargs);
    1669             : 
    1670         448 :             Py_DecRef(pyArgs);
    1671         448 :             Py_DecRef(pyKwargs);
    1672             : 
    1673         448 :             if (ErrOccurredEmitCPLError())
    1674             :             {
    1675           2 :                 eErr = CE_Failure;
    1676             :             }
    1677         448 :             if (pRetValue)
    1678         446 :                 Py_DecRef(pRetValue);
    1679             :         }  // End of GIL section
    1680             : 
    1681         448 :         if (pabyTmpBuffer)
    1682             :         {
    1683             :             // Copy numpy destination array to user buffer
    1684       50867 :             for (int iY = 0; iY < nBufYSize; iY++)
    1685             :             {
    1686             :                 size_t nSrcOffset =
    1687       50444 :                     (static_cast<size_t>(iY + nBufferRadius) * nExtBufXSize +
    1688       50444 :                      nBufferRadius) *
    1689       50444 :                     GDALGetDataTypeSizeBytes(eDataType);
    1690       50444 :                 GDALCopyWords64(pabyTmpBuffer.get() + nSrcOffset, eDataType,
    1691             :                                 GDALGetDataTypeSizeBytes(eDataType),
    1692       50444 :                                 static_cast<GByte *>(pData) + iY * nLineSpace,
    1693             :                                 eBufType, static_cast<int>(nPixelSpace),
    1694             :                                 nBufXSize);
    1695             :             }
    1696             :         }
    1697             :     }
    1698        2837 :     else if (eErr == CE_None && poPixelFunc != nullptr)
    1699             :     {
    1700        2835 :         CPLStringList aosArgs;
    1701             : 
    1702             :         // Apply arguments specified using <PixelFunctionArguments>
    1703        4951 :         for (const auto &[pszKey, pszValue] : m_poPrivate->m_oFunctionArgs)
    1704             :         {
    1705        2116 :             aosArgs.SetNameValue(pszKey, pszValue);
    1706             :         }
    1707             : 
    1708             :         // Apply built-in arguments, potentially overwriting those in <PixelFunctionArguments>
    1709             :         // This is important because some pixel functions rely on built-in arguments being
    1710             :         // properly formatted, or even being a valid pointer. If a user can override these, we could have a crash.
    1711        4298 :         for (const auto &[pszKey, pszValue] : oAdditionalArgs)
    1712             :         {
    1713        1463 :             aosArgs.SetNameValue(pszKey, pszValue);
    1714             :         }
    1715             : 
    1716             :         static_assert(sizeof(apBuffers[0]) == sizeof(void *));
    1717        2835 :         eErr = (poPixelFunc->first)(
    1718             :             // We cast vector<unique_ptr<void>>.data() as void**. This is OK
    1719             :             // given above static_assert
    1720        2835 :             reinterpret_cast<void **>(apBuffers.data()), nBufferCount, pData,
    1721             :             nBufXSize, nBufYSize, eSrcType, eBufType,
    1722             :             static_cast<int>(nPixelSpace), static_cast<int>(nLineSpace),
    1723        2835 :             aosArgs.List());
    1724             :     }
    1725             : 
    1726        3285 :     return eErr;
    1727             : }
    1728             : 
    1729             : /************************************************************************/
    1730             : /*                       IGetDataCoverageStatus()                       */
    1731             : /************************************************************************/
    1732             : 
    1733          59 : int VRTDerivedRasterBand::IGetDataCoverageStatus(
    1734             :     int /* nXOff */, int /* nYOff */, int /* nXSize */, int /* nYSize */,
    1735             :     int /* nMaskFlagStop */, double *pdfDataPct)
    1736             : {
    1737          59 :     if (pdfDataPct != nullptr)
    1738           0 :         *pdfDataPct = -1.0;
    1739             :     return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
    1740          59 :            GDAL_DATA_COVERAGE_STATUS_DATA;
    1741             : }
    1742             : 
    1743             : /************************************************************************/
    1744             : /*                              XMLInit()                               */
    1745             : /************************************************************************/
    1746             : 
    1747        1497 : CPLErr VRTDerivedRasterBand::XMLInit(const CPLXMLNode *psTree,
    1748             :                                      const char *pszVRTPath,
    1749             :                                      VRTMapSharedResources &oMapSharedSources)
    1750             : 
    1751             : {
    1752             :     const CPLErr eErr =
    1753        1497 :         VRTSourcedRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
    1754        1497 :     if (eErr != CE_None)
    1755           0 :         return eErr;
    1756             : 
    1757             :     // Read derived pixel function type.
    1758        1497 :     SetPixelFunctionName(CPLGetXMLValue(psTree, "PixelFunctionType", nullptr));
    1759        1497 :     if (osFuncName.empty())
    1760             :     {
    1761           1 :         CPLError(CE_Failure, CPLE_AppDefined, "PixelFunctionType missing");
    1762           1 :         return CE_Failure;
    1763             :     }
    1764             : 
    1765        1496 :     m_poPrivate->m_osLanguage =
    1766        1496 :         CPLGetXMLValue(psTree, "PixelFunctionLanguage", "C");
    1767        1574 :     if (!EQUAL(m_poPrivate->m_osLanguage, "C") &&
    1768          78 :         !EQUAL(m_poPrivate->m_osLanguage, "Python"))
    1769             :     {
    1770           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1771             :                  "Unsupported PixelFunctionLanguage");
    1772           1 :         return CE_Failure;
    1773             :     }
    1774             : 
    1775        1495 :     m_poPrivate->m_osCode = CPLGetXMLValue(psTree, "PixelFunctionCode", "");
    1776        1537 :     if (!m_poPrivate->m_osCode.empty() &&
    1777          42 :         !EQUAL(m_poPrivate->m_osLanguage, "Python"))
    1778             :     {
    1779           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1780             :                  "PixelFunctionCode can only be used with Python");
    1781           1 :         return CE_Failure;
    1782             :     }
    1783             : 
    1784        1494 :     m_poPrivate->m_nBufferRadius =
    1785        1494 :         atoi(CPLGetXMLValue(psTree, "BufferRadius", "0"));
    1786        1494 :     if (m_poPrivate->m_nBufferRadius < 0 || m_poPrivate->m_nBufferRadius > 1024)
    1787             :     {
    1788           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for BufferRadius");
    1789           1 :         return CE_Failure;
    1790             :     }
    1791        1507 :     if (m_poPrivate->m_nBufferRadius != 0 &&
    1792          14 :         !EQUAL(m_poPrivate->m_osLanguage, "Python"))
    1793             :     {
    1794           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1795             :                  "BufferRadius can only be used with Python");
    1796           1 :         return CE_Failure;
    1797             :     }
    1798             : 
    1799             :     const CPLXMLNode *const psArgs =
    1800        1492 :         CPLGetXMLNode(psTree, "PixelFunctionArguments");
    1801        1492 :     if (psArgs != nullptr)
    1802             :     {
    1803        2678 :         for (const CPLXMLNode *psIter = psArgs->psChild; psIter;
    1804        1420 :              psIter = psIter->psNext)
    1805             :         {
    1806        1420 :             if (psIter->eType == CXT_Attribute)
    1807             :             {
    1808        1420 :                 AddPixelFunctionArgument(psIter->pszValue,
    1809        1420 :                                          psIter->psChild->pszValue);
    1810             :             }
    1811             :         }
    1812             :     }
    1813             : 
    1814             :     // Read optional source transfer data type.
    1815             :     const char *pszTypeName =
    1816        1492 :         CPLGetXMLValue(psTree, "SourceTransferType", nullptr);
    1817        1492 :     if (pszTypeName != nullptr)
    1818             :     {
    1819         891 :         eSourceTransferType = GDALGetDataTypeByName(pszTypeName);
    1820             :     }
    1821             : 
    1822             :     // Whether to skip non contributing sources
    1823             :     const char *pszSkipNonContributingSources =
    1824        1492 :         CPLGetXMLValue(psTree, "SkipNonContributingSources", nullptr);
    1825        1492 :     if (pszSkipNonContributingSources)
    1826             :     {
    1827           2 :         SetSkipNonContributingSources(
    1828           2 :             CPLTestBool(pszSkipNonContributingSources));
    1829             :     }
    1830             : 
    1831        1492 :     return CE_None;
    1832             : }
    1833             : 
    1834             : /************************************************************************/
    1835             : /*                           SerializeToXML()                           */
    1836             : /************************************************************************/
    1837             : 
    1838          67 : CPLXMLNode *VRTDerivedRasterBand::SerializeToXML(const char *pszVRTPath,
    1839             :                                                  bool &bHasWarnedAboutRAMUsage,
    1840             :                                                  size_t &nAccRAMUsage)
    1841             : {
    1842          67 :     CPLXMLNode *psTree = VRTSourcedRasterBand::SerializeToXML(
    1843             :         pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
    1844             : 
    1845             :     /* -------------------------------------------------------------------- */
    1846             :     /*      Set subclass.                                                   */
    1847             :     /* -------------------------------------------------------------------- */
    1848          67 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    1849             :                      CXT_Text, "VRTDerivedRasterBand");
    1850             : 
    1851             :     /* ---- Encode DerivedBand-specific fields ---- */
    1852          67 :     if (!EQUAL(m_poPrivate->m_osLanguage, "C"))
    1853             :     {
    1854           5 :         CPLSetXMLValue(psTree, "PixelFunctionLanguage",
    1855           5 :                        m_poPrivate->m_osLanguage);
    1856             :     }
    1857          67 :     if (!osFuncName.empty())
    1858          66 :         CPLSetXMLValue(psTree, "PixelFunctionType", osFuncName.c_str());
    1859          67 :     if (!m_poPrivate->m_oFunctionArgs.empty())
    1860             :     {
    1861             :         CPLXMLNode *psArgs =
    1862          60 :             CPLCreateXMLNode(psTree, CXT_Element, "PixelFunctionArguments");
    1863         157 :         for (size_t i = 0; i < m_poPrivate->m_oFunctionArgs.size(); ++i)
    1864             :         {
    1865          97 :             const char *pszKey = m_poPrivate->m_oFunctionArgs[i].first.c_str();
    1866             :             const char *pszValue =
    1867          97 :                 m_poPrivate->m_oFunctionArgs[i].second.c_str();
    1868          97 :             CPLCreateXMLNode(CPLCreateXMLNode(psArgs, CXT_Attribute, pszKey),
    1869             :                              CXT_Text, pszValue);
    1870             :         }
    1871             :     }
    1872          67 :     if (!m_poPrivate->m_osCode.empty())
    1873             :     {
    1874           4 :         if (m_poPrivate->m_osCode.find("<![CDATA[") == std::string::npos)
    1875             :         {
    1876           4 :             CPLCreateXMLNode(
    1877             :                 CPLCreateXMLNode(psTree, CXT_Element, "PixelFunctionCode"),
    1878             :                 CXT_Literal,
    1879           8 :                 ("<![CDATA[" + m_poPrivate->m_osCode + "]]>").c_str());
    1880             :         }
    1881             :         else
    1882             :         {
    1883           0 :             CPLSetXMLValue(psTree, "PixelFunctionCode", m_poPrivate->m_osCode);
    1884             :         }
    1885             :     }
    1886          67 :     if (m_poPrivate->m_nBufferRadius != 0)
    1887           1 :         CPLSetXMLValue(psTree, "BufferRadius",
    1888           1 :                        CPLSPrintf("%d", m_poPrivate->m_nBufferRadius));
    1889          67 :     if (this->eSourceTransferType != GDT_Unknown)
    1890           4 :         CPLSetXMLValue(psTree, "SourceTransferType",
    1891             :                        GDALGetDataTypeName(eSourceTransferType));
    1892             : 
    1893          67 :     if (m_poPrivate->m_bSkipNonContributingSourcesSpecified)
    1894             :     {
    1895           1 :         CPLSetXMLValue(psTree, "SkipNonContributingSources",
    1896           1 :                        m_poPrivate->m_bSkipNonContributingSources ? "true"
    1897             :                                                                   : "false");
    1898             :     }
    1899             : 
    1900          67 :     return psTree;
    1901             : }
    1902             : 
    1903             : /************************************************************************/
    1904             : /*                             GetMinimum()                             */
    1905             : /************************************************************************/
    1906             : 
    1907           5 : double VRTDerivedRasterBand::GetMinimum(int *pbSuccess)
    1908             : {
    1909           5 :     return GDALRasterBand::GetMinimum(pbSuccess);
    1910             : }
    1911             : 
    1912             : /************************************************************************/
    1913             : /*                             GetMaximum()                             */
    1914             : /************************************************************************/
    1915             : 
    1916           5 : double VRTDerivedRasterBand::GetMaximum(int *pbSuccess)
    1917             : {
    1918           5 :     return GDALRasterBand::GetMaximum(pbSuccess);
    1919             : }
    1920             : 
    1921             : /************************************************************************/
    1922             : /*                        ComputeRasterMinMax()                         */
    1923             : /************************************************************************/
    1924             : 
    1925          15 : CPLErr VRTDerivedRasterBand::ComputeRasterMinMax(int bApproxOK,
    1926             :                                                  double *adfMinMax)
    1927             : {
    1928          15 :     return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
    1929             : }
    1930             : 
    1931             : /************************************************************************/
    1932             : /*                         ComputeStatistics()                          */
    1933             : /************************************************************************/
    1934             : 
    1935           1 : CPLErr VRTDerivedRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
    1936             :                                                double *pdfMax, double *pdfMean,
    1937             :                                                double *pdfStdDev,
    1938             :                                                GDALProgressFunc pfnProgress,
    1939             :                                                void *pProgressData)
    1940             : 
    1941             : {
    1942           1 :     return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean,
    1943             :                                              pdfStdDev, pfnProgress,
    1944           1 :                                              pProgressData);
    1945             : }
    1946             : 
    1947             : /************************************************************************/
    1948             : /*                            GetHistogram()                            */
    1949             : /************************************************************************/
    1950             : 
    1951           1 : CPLErr VRTDerivedRasterBand::GetHistogram(double dfMin, double dfMax,
    1952             :                                           int nBuckets, GUIntBig *panHistogram,
    1953             :                                           int bIncludeOutOfRange, int bApproxOK,
    1954             :                                           GDALProgressFunc pfnProgress,
    1955             :                                           void *pProgressData)
    1956             : 
    1957             : {
    1958           1 :     return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
    1959             :                                        bIncludeOutOfRange, bApproxOK,
    1960           1 :                                        pfnProgress, pProgressData);
    1961             : }
    1962             : 
    1963             : /*! @endcond */

Generated by: LCOV version 1.14