LCOV - code coverage report
Current view: top level - frmts/vrt - pixelfunctions.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1111 1195 93.0 %
Date: 2025-05-31 00:00:17 Functions: 129 130 99.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Implementation of a set of GDALDerivedPixelFunc(s) to be used
       5             :  *           with source raster band of virtual GDAL datasets.
       6             :  * Author:   Antonio Valentino <antonio.valentino@tiscali.it>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2008-2014,2022 Antonio Valentino <antonio.valentino@tiscali.it>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *****************************************************************************/
      13             : 
      14             : #include <cmath>
      15             : #include "gdal.h"
      16             : #include "vrtdataset.h"
      17             : #include "vrtexpression.h"
      18             : #include "vrtreclassifier.h"
      19             : #include "cpl_float.h"
      20             : 
      21             : #if defined(__x86_64) || defined(_M_X64) || defined(USE_NEON_OPTIMIZATIONS)
      22             : #define USE_SSE2
      23             : #include "gdalsse_priv.h"
      24             : #endif
      25             : 
      26             : #include "gdal_priv_templates.hpp"
      27             : 
      28             : #include <limits>
      29             : 
      30             : template <typename T>
      31      177300 : inline double GetSrcVal(const void *pSource, GDALDataType eSrcType, T ii)
      32             : {
      33      177300 :     switch (eSrcType)
      34             :     {
      35           0 :         case GDT_Unknown:
      36           0 :             return 0;
      37      125890 :         case GDT_Byte:
      38      125890 :             return static_cast<const GByte *>(pSource)[ii];
      39         476 :         case GDT_Int8:
      40         476 :             return static_cast<const GInt8 *>(pSource)[ii];
      41        1156 :         case GDT_UInt16:
      42        1156 :             return static_cast<const GUInt16 *>(pSource)[ii];
      43        1158 :         case GDT_Int16:
      44        1158 :             return static_cast<const GInt16 *>(pSource)[ii];
      45        8092 :         case GDT_UInt32:
      46        8092 :             return static_cast<const GUInt32 *>(pSource)[ii];
      47        2406 :         case GDT_Int32:
      48        2406 :             return static_cast<const GInt32 *>(pSource)[ii];
      49             :         // Precision loss currently for int64/uint64
      50         476 :         case GDT_UInt64:
      51             :             return static_cast<double>(
      52         476 :                 static_cast<const uint64_t *>(pSource)[ii]);
      53         476 :         case GDT_Int64:
      54             :             return static_cast<double>(
      55         476 :                 static_cast<const int64_t *>(pSource)[ii]);
      56           0 :         case GDT_Float16:
      57           0 :             return static_cast<const GFloat16 *>(pSource)[ii];
      58        4850 :         case GDT_Float32:
      59        4850 :             return static_cast<const float *>(pSource)[ii];
      60       22058 :         case GDT_Float64:
      61       22058 :             return static_cast<const double *>(pSource)[ii];
      62        1552 :         case GDT_CInt16:
      63        1552 :             return static_cast<const GInt16 *>(pSource)[2 * ii];
      64         952 :         case GDT_CInt32:
      65         952 :             return static_cast<const GInt32 *>(pSource)[2 * ii];
      66           0 :         case GDT_CFloat16:
      67           0 :             return static_cast<const GFloat16 *>(pSource)[2 * ii];
      68        5904 :         case GDT_CFloat32:
      69        5904 :             return static_cast<const float *>(pSource)[2 * ii];
      70        1854 :         case GDT_CFloat64:
      71        1854 :             return static_cast<const double *>(pSource)[2 * ii];
      72           0 :         case GDT_TypeCount:
      73           0 :             break;
      74             :     }
      75           0 :     return 0;
      76             : }
      77             : 
      78         208 : static bool IsNoData(double dfVal, double dfNoData)
      79             : {
      80         208 :     return dfVal == dfNoData || (std::isnan(dfVal) && std::isnan(dfNoData));
      81             : }
      82             : 
      83        1107 : static CPLErr FetchDoubleArg(CSLConstList papszArgs, const char *pszName,
      84             :                              double *pdfX, double *pdfDefault = nullptr)
      85             : {
      86        1107 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
      87             : 
      88        1107 :     if (pszVal == nullptr)
      89             :     {
      90          70 :         if (pdfDefault == nullptr)
      91             :         {
      92           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      93             :                      "Missing pixel function argument: %s", pszName);
      94           0 :             return CE_Failure;
      95             :         }
      96             :         else
      97             :         {
      98          70 :             *pdfX = *pdfDefault;
      99          70 :             return CE_None;
     100             :         }
     101             :     }
     102             : 
     103        1037 :     char *pszEnd = nullptr;
     104        1037 :     *pdfX = std::strtod(pszVal, &pszEnd);
     105        1037 :     if (pszEnd == pszVal)
     106             :     {
     107           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     108             :                  "Failed to parse pixel function argument: %s", pszName);
     109           0 :         return CE_Failure;
     110             :     }
     111             : 
     112        1037 :     return CE_None;
     113             : }
     114             : 
     115           7 : static CPLErr RealPixelFunc(void **papoSources, int nSources, void *pData,
     116             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     117             :                             GDALDataType eBufType, int nPixelSpace,
     118             :                             int nLineSpace)
     119             : {
     120             :     /* ---- Init ---- */
     121           7 :     if (nSources != 1)
     122           1 :         return CE_Failure;
     123             : 
     124           6 :     const int nPixelSpaceSrc = GDALGetDataTypeSizeBytes(eSrcType);
     125           6 :     const size_t nLineSpaceSrc = static_cast<size_t>(nPixelSpaceSrc) * nXSize;
     126             : 
     127             :     /* ---- Set pixels ---- */
     128          98 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     129             :     {
     130          92 :         GDALCopyWords(static_cast<GByte *>(papoSources[0]) +
     131          92 :                           nLineSpaceSrc * iLine,
     132             :                       eSrcType, nPixelSpaceSrc,
     133             :                       static_cast<GByte *>(pData) +
     134          92 :                           static_cast<GSpacing>(nLineSpace) * iLine,
     135             :                       eBufType, nPixelSpace, nXSize);
     136             :     }
     137             : 
     138             :     /* ---- Return success ---- */
     139           6 :     return CE_None;
     140             : }  // RealPixelFunc
     141             : 
     142           8 : static CPLErr ImagPixelFunc(void **papoSources, int nSources, void *pData,
     143             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     144             :                             GDALDataType eBufType, int nPixelSpace,
     145             :                             int nLineSpace)
     146             : {
     147             :     /* ---- Init ---- */
     148           8 :     if (nSources != 1)
     149           1 :         return CE_Failure;
     150             : 
     151           7 :     if (GDALDataTypeIsComplex(eSrcType))
     152             :     {
     153           6 :         const GDALDataType eSrcBaseType = GDALGetNonComplexDataType(eSrcType);
     154           6 :         const int nPixelSpaceSrc = GDALGetDataTypeSizeBytes(eSrcType);
     155           6 :         const size_t nLineSpaceSrc =
     156           6 :             static_cast<size_t>(nPixelSpaceSrc) * nXSize;
     157             : 
     158           6 :         const void *const pImag = static_cast<GByte *>(papoSources[0]) +
     159           6 :                                   GDALGetDataTypeSizeBytes(eSrcType) / 2;
     160             : 
     161             :         /* ---- Set pixels ---- */
     162          56 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     163             :         {
     164          50 :             GDALCopyWords(static_cast<const GByte *>(pImag) +
     165          50 :                               nLineSpaceSrc * iLine,
     166             :                           eSrcBaseType, nPixelSpaceSrc,
     167             :                           static_cast<GByte *>(pData) +
     168          50 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     169             :                           eBufType, nPixelSpace, nXSize);
     170             :         }
     171             :     }
     172             :     else
     173             :     {
     174           1 :         const double dfImag = 0;
     175             : 
     176             :         /* ---- Set pixels ---- */
     177          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     178             :         {
     179             :             // Always copy from the same location.
     180          20 :             GDALCopyWords(&dfImag, eSrcType, 0,
     181             :                           static_cast<GByte *>(pData) +
     182          20 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     183             :                           eBufType, nPixelSpace, nXSize);
     184             :         }
     185             :     }
     186             : 
     187             :     /* ---- Return success ---- */
     188           7 :     return CE_None;
     189             : }  // ImagPixelFunc
     190             : 
     191           6 : static CPLErr ComplexPixelFunc(void **papoSources, int nSources, void *pData,
     192             :                                int nXSize, int nYSize, GDALDataType eSrcType,
     193             :                                GDALDataType eBufType, int nPixelSpace,
     194             :                                int nLineSpace)
     195             : {
     196             :     /* ---- Init ---- */
     197           6 :     if (nSources != 2)
     198           1 :         return CE_Failure;
     199             : 
     200           5 :     const void *const pReal = papoSources[0];
     201           5 :     const void *const pImag = papoSources[1];
     202             : 
     203             :     /* ---- Set pixels ---- */
     204           5 :     size_t ii = 0;
     205         281 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     206             :     {
     207       17060 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     208             :         {
     209             :             // Source raster pixels may be obtained with GetSrcVal macro.
     210             :             const double adfPixVal[2] = {
     211       16784 :                 GetSrcVal(pReal, eSrcType, ii),  // re
     212       33568 :                 GetSrcVal(pImag, eSrcType, ii)   // im
     213       16784 :             };
     214             : 
     215       16784 :             GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     216             :                           static_cast<GByte *>(pData) +
     217       16784 :                               static_cast<GSpacing>(nLineSpace) * iLine +
     218       16784 :                               iCol * nPixelSpace,
     219             :                           eBufType, nPixelSpace, 1);
     220             :         }
     221             :     }
     222             : 
     223             :     /* ---- Return success ---- */
     224           5 :     return CE_None;
     225             : }  // ComplexPixelFunc
     226             : 
     227             : typedef enum
     228             : {
     229             :     GAT_amplitude,
     230             :     GAT_intensity,
     231             :     GAT_dB
     232             : } PolarAmplitudeType;
     233             : 
     234             : static const char pszPolarPixelFuncMetadata[] =
     235             :     "<PixelFunctionArgumentsList>"
     236             :     "   <Argument name='amplitude_type' description='Amplitude Type' "
     237             :     "type='string-select' default='AMPLITUDE'>"
     238             :     "       <Value>INTENSITY</Value>"
     239             :     "       <Value>dB</Value>"
     240             :     "       <Value>AMPLITUDE</Value>"
     241             :     "   </Argument>"
     242             :     "</PixelFunctionArgumentsList>";
     243             : 
     244           4 : static CPLErr PolarPixelFunc(void **papoSources, int nSources, void *pData,
     245             :                              int nXSize, int nYSize, GDALDataType eSrcType,
     246             :                              GDALDataType eBufType, int nPixelSpace,
     247             :                              int nLineSpace, CSLConstList papszArgs)
     248             : {
     249             :     /* ---- Init ---- */
     250           4 :     if (nSources != 2)
     251           0 :         return CE_Failure;
     252             : 
     253           4 :     const char pszName[] = "amplitude_type";
     254           4 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
     255           4 :     PolarAmplitudeType amplitudeType = GAT_amplitude;
     256           4 :     if (pszVal != nullptr)
     257             :     {
     258           3 :         if (strcmp(pszVal, "INTENSITY") == 0)
     259           1 :             amplitudeType = GAT_intensity;
     260           2 :         else if (strcmp(pszVal, "dB") == 0)
     261           1 :             amplitudeType = GAT_dB;
     262           1 :         else if (strcmp(pszVal, "AMPLITUDE") != 0)
     263             :         {
     264           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     265             :                      "Invalid value for pixel function argument '%s': %s",
     266             :                      pszName, pszVal);
     267           0 :             return CE_Failure;
     268             :         }
     269             :     }
     270             : 
     271           4 :     const void *const pAmp = papoSources[0];
     272           4 :     const void *const pPhase = papoSources[1];
     273             : 
     274             :     /* ---- Set pixels ---- */
     275           4 :     size_t ii = 0;
     276          84 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     277             :     {
     278        1680 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     279             :         {
     280             :             // Source raster pixels may be obtained with GetSrcVal macro.
     281        1600 :             double dfAmp = GetSrcVal(pAmp, eSrcType, ii);
     282        1600 :             switch (amplitudeType)
     283             :             {
     284         400 :                 case GAT_intensity:
     285             :                     // clip to zero
     286         400 :                     dfAmp = dfAmp <= 0 ? 0 : std::sqrt(dfAmp);
     287         400 :                     break;
     288         400 :                 case GAT_dB:
     289         400 :                     dfAmp = dfAmp <= 0
     290         400 :                                 ? -std::numeric_limits<double>::infinity()
     291         400 :                                 : pow(10, dfAmp / 20.);
     292         400 :                     break;
     293         800 :                 case GAT_amplitude:
     294         800 :                     break;
     295             :             }
     296        1600 :             const double dfPhase = GetSrcVal(pPhase, eSrcType, ii);
     297             :             const double adfPixVal[2] = {
     298        1600 :                 dfAmp * std::cos(dfPhase),  // re
     299        1600 :                 dfAmp * std::sin(dfPhase)   // im
     300        1600 :             };
     301             : 
     302        1600 :             GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     303             :                           static_cast<GByte *>(pData) +
     304        1600 :                               static_cast<GSpacing>(nLineSpace) * iLine +
     305        1600 :                               iCol * nPixelSpace,
     306             :                           eBufType, nPixelSpace, 1);
     307             :         }
     308             :     }
     309             : 
     310             :     /* ---- Return success ---- */
     311           4 :     return CE_None;
     312             : }  // PolarPixelFunc
     313             : 
     314           4 : static CPLErr ModulePixelFunc(void **papoSources, int nSources, void *pData,
     315             :                               int nXSize, int nYSize, GDALDataType eSrcType,
     316             :                               GDALDataType eBufType, int nPixelSpace,
     317             :                               int nLineSpace)
     318             : {
     319             :     /* ---- Init ---- */
     320           4 :     if (nSources != 1)
     321           1 :         return CE_Failure;
     322             : 
     323           3 :     if (GDALDataTypeIsComplex(eSrcType))
     324             :     {
     325           2 :         const void *pReal = papoSources[0];
     326           2 :         const void *pImag = static_cast<GByte *>(papoSources[0]) +
     327           2 :                             GDALGetDataTypeSizeBytes(eSrcType) / 2;
     328             : 
     329             :         /* ---- Set pixels ---- */
     330           2 :         size_t ii = 0;
     331          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     332             :         {
     333          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     334             :             {
     335             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     336          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     337          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
     338             : 
     339          60 :                 const double dfPixVal = sqrt(dfReal * dfReal + dfImag * dfImag);
     340             : 
     341          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     342             :                               static_cast<GByte *>(pData) +
     343          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     344          60 :                                   iCol * nPixelSpace,
     345             :                               eBufType, nPixelSpace, 1);
     346             :             }
     347             :         }
     348             :     }
     349             :     else
     350             :     {
     351             :         /* ---- Set pixels ---- */
     352           1 :         size_t ii = 0;
     353          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     354             :         {
     355         420 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     356             :             {
     357             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     358             :                 const double dfPixVal =
     359         400 :                     fabs(GetSrcVal(papoSources[0], eSrcType, ii));
     360             : 
     361         400 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     362             :                               static_cast<GByte *>(pData) +
     363         400 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     364         400 :                                   iCol * nPixelSpace,
     365             :                               eBufType, nPixelSpace, 1);
     366             :             }
     367             :         }
     368             :     }
     369             : 
     370             :     /* ---- Return success ---- */
     371           3 :     return CE_None;
     372             : }  // ModulePixelFunc
     373             : 
     374           5 : static CPLErr PhasePixelFunc(void **papoSources, int nSources, void *pData,
     375             :                              int nXSize, int nYSize, GDALDataType eSrcType,
     376             :                              GDALDataType eBufType, int nPixelSpace,
     377             :                              int nLineSpace)
     378             : {
     379             :     /* ---- Init ---- */
     380           5 :     if (nSources != 1)
     381           1 :         return CE_Failure;
     382             : 
     383           4 :     if (GDALDataTypeIsComplex(eSrcType))
     384             :     {
     385           2 :         const void *const pReal = papoSources[0];
     386           2 :         const void *const pImag = static_cast<GByte *>(papoSources[0]) +
     387           2 :                                   GDALGetDataTypeSizeBytes(eSrcType) / 2;
     388             : 
     389             :         /* ---- Set pixels ---- */
     390           2 :         size_t ii = 0;
     391          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     392             :         {
     393          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     394             :             {
     395             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     396          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     397          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
     398             : 
     399          60 :                 const double dfPixVal = atan2(dfImag, dfReal);
     400             : 
     401          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     402             :                               static_cast<GByte *>(pData) +
     403          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     404          60 :                                   iCol * nPixelSpace,
     405             :                               eBufType, nPixelSpace, 1);
     406             :             }
     407             :         }
     408             :     }
     409           2 :     else if (GDALDataTypeIsInteger(eSrcType) && !GDALDataTypeIsSigned(eSrcType))
     410             :     {
     411           1 :         constexpr double dfZero = 0;
     412           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     413             :         {
     414           6 :             GDALCopyWords(&dfZero, GDT_Float64, 0,
     415             :                           static_cast<GByte *>(pData) +
     416           6 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     417             :                           eBufType, nPixelSpace, nXSize);
     418             :         }
     419             :     }
     420             :     else
     421             :     {
     422             :         /* ---- Set pixels ---- */
     423           1 :         size_t ii = 0;
     424           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     425             :         {
     426          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     427             :             {
     428          30 :                 const void *const pReal = papoSources[0];
     429             : 
     430             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     431          30 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     432          30 :                 const double dfPixVal = (dfReal < 0) ? M_PI : 0.0;
     433             : 
     434          30 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     435             :                               static_cast<GByte *>(pData) +
     436          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     437          30 :                                   iCol * nPixelSpace,
     438             :                               eBufType, nPixelSpace, 1);
     439             :             }
     440             :         }
     441             :     }
     442             : 
     443             :     /* ---- Return success ---- */
     444           4 :     return CE_None;
     445             : }  // PhasePixelFunc
     446             : 
     447           4 : static CPLErr ConjPixelFunc(void **papoSources, int nSources, void *pData,
     448             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     449             :                             GDALDataType eBufType, int nPixelSpace,
     450             :                             int nLineSpace)
     451             : {
     452             :     /* ---- Init ---- */
     453           4 :     if (nSources != 1)
     454           1 :         return CE_Failure;
     455             : 
     456           3 :     if (GDALDataTypeIsComplex(eSrcType) && GDALDataTypeIsComplex(eBufType))
     457             :     {
     458           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
     459           2 :         const void *const pReal = papoSources[0];
     460           2 :         const void *const pImag =
     461           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
     462             : 
     463             :         /* ---- Set pixels ---- */
     464           2 :         size_t ii = 0;
     465          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     466             :         {
     467          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     468             :             {
     469             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     470             :                 const double adfPixVal[2] = {
     471          60 :                     +GetSrcVal(pReal, eSrcType, ii),  // re
     472         120 :                     -GetSrcVal(pImag, eSrcType, ii)   // im
     473          60 :                 };
     474             : 
     475          60 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     476             :                               static_cast<GByte *>(pData) +
     477          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     478          60 :                                   iCol * nPixelSpace,
     479             :                               eBufType, nPixelSpace, 1);
     480             :             }
     481             :         }
     482             :     }
     483             :     else
     484             :     {
     485             :         // No complex data type.
     486           1 :         return RealPixelFunc(papoSources, nSources, pData, nXSize, nYSize,
     487           1 :                              eSrcType, eBufType, nPixelSpace, nLineSpace);
     488             :     }
     489             : 
     490             :     /* ---- Return success ---- */
     491           2 :     return CE_None;
     492             : }  // ConjPixelFunc
     493             : 
     494             : #ifdef USE_SSE2
     495             : 
     496             : /************************************************************************/
     497             : /*                        OptimizedSumToFloat_SSE2()                    */
     498             : /************************************************************************/
     499             : 
     500             : template <typename Tsrc>
     501          87 : static void OptimizedSumToFloat_SSE2(double dfK, void *pOutBuffer,
     502             :                                      int nLineSpace, int nXSize, int nYSize,
     503             :                                      int nSources,
     504             :                                      const void *const *papoSources)
     505             : {
     506          87 :     const XMMReg4Float cst = XMMReg4Float::Set1(static_cast<float>(dfK));
     507             : 
     508         279 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     509             :     {
     510         192 :         float *CPL_RESTRICT const pDest = reinterpret_cast<float *>(
     511             :             static_cast<GByte *>(pOutBuffer) +
     512         192 :             static_cast<GSpacing>(nLineSpace) * iLine);
     513         192 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     514             : 
     515         192 :         constexpr int VALUES_PER_REG = 4;
     516         192 :         constexpr int UNROLLING = 4 * VALUES_PER_REG;
     517         192 :         int iCol = 0;
     518         384 :         for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     519             :         {
     520         192 :             XMMReg4Float d0(cst);
     521         192 :             XMMReg4Float d1(cst);
     522         192 :             XMMReg4Float d2(cst);
     523         192 :             XMMReg4Float d3(cst);
     524         556 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     525             :             {
     526             :                 XMMReg4Float t0, t1, t2, t3;
     527         364 :                 XMMReg4Float::Load16Val(
     528         364 :                     static_cast<const Tsrc * CPL_RESTRICT>(papoSources[iSrc]) +
     529         364 :                         iOffsetLine + iCol,
     530             :                     t0, t1, t2, t3);
     531         364 :                 d0 += t0;
     532         364 :                 d1 += t1;
     533         364 :                 d2 += t2;
     534         364 :                 d3 += t3;
     535             :             }
     536         192 :             d0.Store4Val(pDest + iCol + VALUES_PER_REG * 0);
     537         192 :             d1.Store4Val(pDest + iCol + VALUES_PER_REG * 1);
     538         192 :             d2.Store4Val(pDest + iCol + VALUES_PER_REG * 2);
     539         192 :             d3.Store4Val(pDest + iCol + VALUES_PER_REG * 3);
     540             :         }
     541             : 
     542         444 :         for (; iCol < nXSize; iCol++)
     543             :         {
     544         252 :             float d = static_cast<float>(dfK);
     545         676 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     546             :             {
     547         424 :                 d += static_cast<const Tsrc * CPL_RESTRICT>(
     548         424 :                     papoSources[iSrc])[iOffsetLine + iCol];
     549             :             }
     550         252 :             pDest[iCol] = d;
     551             :         }
     552             :     }
     553          87 : }
     554             : 
     555             : /************************************************************************/
     556             : /*                       OptimizedSumToDouble_SSE2()                    */
     557             : /************************************************************************/
     558             : 
     559             : template <typename Tsrc>
     560         141 : static void OptimizedSumToDouble_SSE2(double dfK, void *pOutBuffer,
     561             :                                       int nLineSpace, int nXSize, int nYSize,
     562             :                                       int nSources,
     563             :                                       const void *const *papoSources)
     564             : {
     565         141 :     const XMMReg4Double cst = XMMReg4Double::Set1(dfK);
     566             : 
     567        4041 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     568             :     {
     569        3900 :         double *CPL_RESTRICT const pDest = reinterpret_cast<double *>(
     570             :             static_cast<GByte *>(pOutBuffer) +
     571        3900 :             static_cast<GSpacing>(nLineSpace) * iLine);
     572        3900 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     573             : 
     574        3900 :         constexpr int VALUES_PER_REG = 4;
     575        3900 :         constexpr int UNROLLING = 2 * VALUES_PER_REG;
     576        3900 :         int iCol = 0;
     577     1624476 :         for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     578             :         {
     579     1620574 :             XMMReg4Double d0(cst);
     580     1620574 :             XMMReg4Double d1(cst);
     581     4861552 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     582             :             {
     583             :                 XMMReg4Double t0, t1;
     584     3240988 :                 XMMReg4Double::Load8Val(
     585     3240988 :                     static_cast<const Tsrc * CPL_RESTRICT>(papoSources[iSrc]) +
     586     3240988 :                         iOffsetLine + iCol,
     587             :                     t0, t1);
     588     3240988 :                 d0 += t0;
     589     3240988 :                 d1 += t1;
     590             :             }
     591     1620574 :             d0.Store4Val(pDest + iCol + VALUES_PER_REG * 0);
     592     1620574 :             d1.Store4Val(pDest + iCol + VALUES_PER_REG * 1);
     593             :         }
     594             : 
     595        4478 :         for (; iCol < nXSize; iCol++)
     596             :         {
     597         578 :             double d = dfK;
     598        1450 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     599             :             {
     600         872 :                 d += static_cast<const Tsrc * CPL_RESTRICT>(
     601         872 :                     papoSources[iSrc])[iOffsetLine + iCol];
     602             :             }
     603         578 :             pDest[iCol] = d;
     604             :         }
     605             :     }
     606         141 : }
     607             : 
     608             : #endif  // USE_SSE2
     609             : 
     610             : /************************************************************************/
     611             : /*                       OptimizedSumPackedOutput()                     */
     612             : /************************************************************************/
     613             : 
     614             : template <typename Tsrc, typename Tdest>
     615         264 : static void OptimizedSumPackedOutput(double dfK, void *pOutBuffer,
     616             :                                      int nLineSpace, int nXSize, int nYSize,
     617             :                                      int nSources,
     618             :                                      const void *const *papoSources)
     619             : {
     620             : #ifdef USE_SSE2
     621             :     if constexpr (std::is_same_v<Tdest, float> && !std::is_same_v<Tsrc, double>)
     622             :     {
     623          87 :         OptimizedSumToFloat_SSE2<Tsrc>(dfK, pOutBuffer, nLineSpace, nXSize,
     624             :                                        nYSize, nSources, papoSources);
     625             :     }
     626             :     else if constexpr (std::is_same_v<Tdest, double>)
     627             :     {
     628         141 :         OptimizedSumToDouble_SSE2<Tsrc>(dfK, pOutBuffer, nLineSpace, nXSize,
     629             :                                         nYSize, nSources, papoSources);
     630             :     }
     631             :     else
     632             : #endif  // USE_SSE2
     633             :     {
     634          36 :         const Tdest nCst = static_cast<Tdest>(dfK);
     635         144 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     636             :         {
     637         108 :             Tdest *CPL_RESTRICT const pDest = reinterpret_cast<Tdest *>(
     638             :                 static_cast<GByte *>(pOutBuffer) +
     639         108 :                 static_cast<GSpacing>(nLineSpace) * iLine);
     640         108 :             const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     641             : 
     642             : #define LOAD_SRCVAL(iSrc_, j_)                                                 \
     643             :     static_cast<Tdest>(static_cast<const Tsrc * CPL_RESTRICT>(                 \
     644             :         papoSources[(iSrc_)])[iOffsetLine + iCol + (j_)])
     645             : 
     646         108 :             constexpr int UNROLLING = 4;
     647         108 :             int iCol = 0;
     648         580 :             for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     649             :             {
     650         472 :                 Tdest d[4] = {nCst, nCst, nCst, nCst};
     651        1616 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
     652             :                 {
     653        1144 :                     d[0] += LOAD_SRCVAL(iSrc, 0);
     654        1144 :                     d[1] += LOAD_SRCVAL(iSrc, 1);
     655        1144 :                     d[2] += LOAD_SRCVAL(iSrc, 2);
     656        1144 :                     d[3] += LOAD_SRCVAL(iSrc, 3);
     657             :                 }
     658         472 :                 pDest[iCol + 0] = d[0];
     659         472 :                 pDest[iCol + 1] = d[1];
     660         472 :                 pDest[iCol + 2] = d[2];
     661         472 :                 pDest[iCol + 3] = d[3];
     662             :             }
     663         176 :             for (; iCol < nXSize; iCol++)
     664             :             {
     665          68 :                 Tdest d0 = nCst;
     666         204 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
     667             :                 {
     668         136 :                     d0 += LOAD_SRCVAL(iSrc, 0);
     669             :                 }
     670          68 :                 pDest[iCol] = d0;
     671             :             }
     672             : #undef LOAD_SRCVAL
     673             :         }
     674             :     }
     675         264 : }
     676             : 
     677             : /************************************************************************/
     678             : /*                       OptimizedSumPackedOutput()                     */
     679             : /************************************************************************/
     680             : 
     681             : template <typename Tdest>
     682         287 : static bool OptimizedSumPackedOutput(GDALDataType eSrcType, double dfK,
     683             :                                      void *pOutBuffer, int nLineSpace,
     684             :                                      int nXSize, int nYSize, int nSources,
     685             :                                      const void *const *papoSources)
     686             : {
     687         287 :     switch (eSrcType)
     688             :     {
     689          73 :         case GDT_Byte:
     690          73 :             OptimizedSumPackedOutput<uint8_t, Tdest>(dfK, pOutBuffer,
     691             :                                                      nLineSpace, nXSize, nYSize,
     692             :                                                      nSources, papoSources);
     693          73 :             return true;
     694             : 
     695          34 :         case GDT_UInt16:
     696          34 :             OptimizedSumPackedOutput<uint16_t, Tdest>(
     697             :                 dfK, pOutBuffer, nLineSpace, nXSize, nYSize, nSources,
     698             :                 papoSources);
     699          34 :             return true;
     700             : 
     701          34 :         case GDT_Int16:
     702          34 :             OptimizedSumPackedOutput<int16_t, Tdest>(dfK, pOutBuffer,
     703             :                                                      nLineSpace, nXSize, nYSize,
     704             :                                                      nSources, papoSources);
     705          34 :             return true;
     706             : 
     707          34 :         case GDT_Int32:
     708          34 :             OptimizedSumPackedOutput<int32_t, Tdest>(dfK, pOutBuffer,
     709             :                                                      nLineSpace, nXSize, nYSize,
     710             :                                                      nSources, papoSources);
     711          34 :             return true;
     712             : 
     713          36 :         case GDT_Float32:
     714          36 :             OptimizedSumPackedOutput<float, Tdest>(dfK, pOutBuffer, nLineSpace,
     715             :                                                    nXSize, nYSize, nSources,
     716             :                                                    papoSources);
     717          36 :             return true;
     718             : 
     719          36 :         case GDT_Float64:
     720          36 :             OptimizedSumPackedOutput<double, Tdest>(dfK, pOutBuffer, nLineSpace,
     721             :                                                     nXSize, nYSize, nSources,
     722             :                                                     papoSources);
     723          36 :             return true;
     724             : 
     725          40 :         default:
     726          40 :             break;
     727             :     }
     728          40 :     return false;
     729             : }
     730             : 
     731             : /************************************************************************/
     732             : /*                    OptimizedSumThroughLargerType()                   */
     733             : /************************************************************************/
     734             : 
     735             : namespace
     736             : {
     737             : template <typename Tsrc, typename Tdest, typename Enable = void>
     738             : struct TintermediateS
     739             : {
     740             :     using type = double;
     741             : };
     742             : 
     743             : template <typename Tsrc, typename Tdest>
     744             : struct TintermediateS<
     745             :     Tsrc, Tdest,
     746             :     std::enable_if_t<
     747             :         (std::is_same_v<Tsrc, uint8_t> || std::is_same_v<Tsrc, int16_t> ||
     748             :          std::is_same_v<Tsrc, uint16_t>)&&(std::is_same_v<Tdest, uint8_t> ||
     749             :                                            std::is_same_v<Tdest, int16_t> ||
     750             :                                            std::is_same_v<Tdest, uint16_t>),
     751             :         bool>>
     752             : {
     753             :     using type = int32_t;
     754             : };
     755             : 
     756             : }  // namespace
     757             : 
     758             : template <typename Tsrc, typename Tdest>
     759         397 : static bool OptimizedSumThroughLargerType(double dfK, void *pOutBuffer,
     760             :                                           int nPixelSpace, int nLineSpace,
     761             :                                           int nXSize, int nYSize, int nSources,
     762             :                                           const void *const *papoSources)
     763             : {
     764             :     using Tintermediate = typename TintermediateS<Tsrc, Tdest>::type;
     765         397 :     const Tintermediate k = static_cast<Tintermediate>(dfK);
     766             : 
     767         397 :     size_t ii = 0;
     768        1189 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     769             :     {
     770         792 :         GByte *CPL_RESTRICT pDest = static_cast<GByte *>(pOutBuffer) +
     771         792 :                                     static_cast<GSpacing>(nLineSpace) * iLine;
     772             : 
     773         792 :         constexpr int UNROLLING = 4;
     774         792 :         int iCol = 0;
     775        3952 :         for (; iCol < nXSize - (UNROLLING - 1);
     776             :              iCol += UNROLLING, ii += UNROLLING)
     777             :         {
     778        3160 :             Tintermediate aSum[4] = {k, k, k, k};
     779             : 
     780        9480 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     781             :             {
     782        6320 :                 aSum[0] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 0];
     783        6320 :                 aSum[1] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 1];
     784        6320 :                 aSum[2] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 2];
     785        6320 :                 aSum[3] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 3];
     786             :             }
     787             : 
     788        3160 :             GDALCopyWord(aSum[0], *reinterpret_cast<Tdest *>(pDest));
     789        3160 :             pDest += nPixelSpace;
     790        3160 :             GDALCopyWord(aSum[1], *reinterpret_cast<Tdest *>(pDest));
     791        3160 :             pDest += nPixelSpace;
     792        3160 :             GDALCopyWord(aSum[2], *reinterpret_cast<Tdest *>(pDest));
     793        3160 :             pDest += nPixelSpace;
     794        3160 :             GDALCopyWord(aSum[3], *reinterpret_cast<Tdest *>(pDest));
     795        3160 :             pDest += nPixelSpace;
     796             :         }
     797        1584 :         for (; iCol < nXSize; ++iCol, ++ii, pDest += nPixelSpace)
     798             :         {
     799         792 :             Tintermediate sum = k;
     800        2374 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     801             :             {
     802        1582 :                 sum += static_cast<const Tsrc *>(papoSources[iSrc])[ii];
     803             :             }
     804             : 
     805         792 :             auto pDst = reinterpret_cast<Tdest *>(pDest);
     806         792 :             GDALCopyWord(sum, *pDst);
     807             :         }
     808             :     }
     809         397 :     return true;
     810             : }
     811             : 
     812             : /************************************************************************/
     813             : /*                     OptimizedSumThroughLargerType()                  */
     814             : /************************************************************************/
     815             : 
     816             : template <typename Tsrc>
     817         500 : static bool OptimizedSumThroughLargerType(GDALDataType eBufType, double dfK,
     818             :                                           void *pOutBuffer, int nPixelSpace,
     819             :                                           int nLineSpace, int nXSize,
     820             :                                           int nYSize, int nSources,
     821             :                                           const void *const *papoSources)
     822             : {
     823         500 :     switch (eBufType)
     824             :     {
     825         105 :         case GDT_Byte:
     826         105 :             return OptimizedSumThroughLargerType<Tsrc, uint8_t>(
     827             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
     828         105 :                 nSources, papoSources);
     829             : 
     830         103 :         case GDT_UInt16:
     831         103 :             return OptimizedSumThroughLargerType<Tsrc, uint16_t>(
     832             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
     833         103 :                 nSources, papoSources);
     834             : 
     835         103 :         case GDT_Int16:
     836         103 :             return OptimizedSumThroughLargerType<Tsrc, int16_t>(
     837             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
     838         103 :                 nSources, papoSources);
     839             : 
     840          86 :         case GDT_Int32:
     841          86 :             return OptimizedSumThroughLargerType<Tsrc, int32_t>(
     842             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
     843          86 :                 nSources, papoSources);
     844             : 
     845             :         // Float32 and Float64 already covered by OptimizedSum() for packed case
     846         103 :         default:
     847         103 :             break;
     848             :     }
     849         103 :     return false;
     850             : }
     851             : 
     852             : /************************************************************************/
     853             : /*                    OptimizedSumThroughLargerType()                   */
     854             : /************************************************************************/
     855             : 
     856         640 : static bool OptimizedSumThroughLargerType(GDALDataType eSrcType,
     857             :                                           GDALDataType eBufType, double dfK,
     858             :                                           void *pOutBuffer, int nPixelSpace,
     859             :                                           int nLineSpace, int nXSize,
     860             :                                           int nYSize, int nSources,
     861             :                                           const void *const *papoSources)
     862             : {
     863         640 :     switch (eSrcType)
     864             :     {
     865          70 :         case GDT_Byte:
     866          70 :             return OptimizedSumThroughLargerType<uint8_t>(
     867             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     868          70 :                 nYSize, nSources, papoSources);
     869             : 
     870          85 :         case GDT_UInt16:
     871          85 :             return OptimizedSumThroughLargerType<uint16_t>(
     872             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     873          85 :                 nYSize, nSources, papoSources);
     874             : 
     875          85 :         case GDT_Int16:
     876          85 :             return OptimizedSumThroughLargerType<int16_t>(
     877             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     878          85 :                 nYSize, nSources, papoSources);
     879             : 
     880          85 :         case GDT_Int32:
     881          85 :             return OptimizedSumThroughLargerType<int32_t>(
     882             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     883          85 :                 nYSize, nSources, papoSources);
     884             : 
     885          90 :         case GDT_Float32:
     886          90 :             return OptimizedSumThroughLargerType<float>(
     887             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     888          90 :                 nYSize, nSources, papoSources);
     889             : 
     890          85 :         case GDT_Float64:
     891          85 :             return OptimizedSumThroughLargerType<double>(
     892             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
     893          85 :                 nYSize, nSources, papoSources);
     894             : 
     895         140 :         default:
     896         140 :             break;
     897             :     }
     898             : 
     899         140 :     return false;
     900             : }
     901             : 
     902             : /************************************************************************/
     903             : /*                            SumPixelFunc()                            */
     904             : /************************************************************************/
     905             : 
     906             : static const char pszSumPixelFuncMetadata[] =
     907             :     "<PixelFunctionArgumentsList>"
     908             :     "   <Argument name='k' description='Optional constant term' type='double' "
     909             :     "default='0.0' />"
     910             :     "   <Argument name='propagateNoData' description='Whether the output value "
     911             :     "should be NoData as as soon as one source is NoData' type='boolean' "
     912             :     "default='false' />"
     913             :     "   <Argument type='builtin' value='NoData' optional='true' />"
     914             :     "</PixelFunctionArgumentsList>";
     915             : 
     916         945 : static CPLErr SumPixelFunc(void **papoSources, int nSources, void *pData,
     917             :                            int nXSize, int nYSize, GDALDataType eSrcType,
     918             :                            GDALDataType eBufType, int nPixelSpace,
     919             :                            int nLineSpace, CSLConstList papszArgs)
     920             : {
     921             :     /* ---- Init ---- */
     922         945 :     if (nSources < 1)
     923             :     {
     924           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     925             :                  "sum requires at least one source");
     926           1 :         return CE_Failure;
     927             :     }
     928             : 
     929         944 :     double dfK = 0.0;
     930         944 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
     931           0 :         return CE_Failure;
     932             : 
     933         944 :     double dfNoData{0};
     934         944 :     bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
     935         944 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
     936           0 :         return CE_Failure;
     937             : 
     938         944 :     const bool bPropagateNoData = CPLTestBool(
     939             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
     940             : 
     941         944 :     if (dfNoData == 0 && !bPropagateNoData)
     942         940 :         bHasNoData = false;
     943             : 
     944             :     /* ---- Set pixels ---- */
     945         944 :     if (GDALDataTypeIsComplex(eSrcType))
     946             :     {
     947          36 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
     948             : 
     949             :         /* ---- Set pixels ---- */
     950          36 :         size_t ii = 0;
     951         112 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     952             :         {
     953        1296 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     954             :             {
     955        1220 :                 double adfSum[2] = {dfK, 0.0};
     956             : 
     957        3690 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
     958             :                 {
     959        2470 :                     const void *const pReal = papoSources[iSrc];
     960        2470 :                     const void *const pImag =
     961        2470 :                         static_cast<const GByte *>(pReal) + nOffset;
     962             : 
     963             :                     // Source raster pixels may be obtained with GetSrcVal
     964             :                     // macro.
     965        2470 :                     adfSum[0] += GetSrcVal(pReal, eSrcType, ii);
     966        2470 :                     adfSum[1] += GetSrcVal(pImag, eSrcType, ii);
     967             :                 }
     968             : 
     969        1220 :                 GDALCopyWords(adfSum, GDT_CFloat64, 0,
     970             :                               static_cast<GByte *>(pData) +
     971        1220 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     972        1220 :                                   iCol * nPixelSpace,
     973             :                               eBufType, nPixelSpace, 1);
     974             :             }
     975             :         }
     976             :     }
     977             :     else
     978             :     {
     979             :         /* ---- Set pixels ---- */
     980         908 :         bool bGeneralCase = true;
     981         908 :         if (dfNoData == 0 && !bPropagateNoData)
     982             :         {
     983         904 :             if (eBufType == GDT_Float32 && nPixelSpace == sizeof(float))
     984             :             {
     985         126 :                 bGeneralCase = !OptimizedSumPackedOutput<float>(
     986             :                     eSrcType, dfK, pData, nLineSpace, nXSize, nYSize, nSources,
     987             :                     papoSources);
     988             :             }
     989         778 :             else if (eBufType == GDT_Float64 && nPixelSpace == sizeof(double))
     990             :             {
     991         161 :                 bGeneralCase = !OptimizedSumPackedOutput<double>(
     992             :                     eSrcType, dfK, pData, nLineSpace, nXSize, nYSize, nSources,
     993             :                     papoSources);
     994             :             }
     995         617 :             else if (
     996         617 :                 dfK >= 0 && dfK <= INT_MAX && eBufType == GDT_Int32 &&
     997        1251 :                 nPixelSpace == sizeof(int32_t) && eSrcType == GDT_Byte &&
     998             :                 // Limitation to avoid overflow of int32 if all source values are at the max of their data type
     999          17 :                 nSources <=
    1000          17 :                     (INT_MAX - dfK) / std::numeric_limits<uint8_t>::max())
    1001             :             {
    1002          17 :                 bGeneralCase = false;
    1003          17 :                 OptimizedSumPackedOutput<uint8_t, int32_t>(
    1004             :                     dfK, pData, nLineSpace, nXSize, nYSize, nSources,
    1005             :                     papoSources);
    1006             :             }
    1007             : 
    1008        1544 :             if (bGeneralCase && dfK >= 0 && dfK <= INT_MAX &&
    1009         640 :                 nSources <=
    1010         640 :                     (INT_MAX - dfK) / std::numeric_limits<uint16_t>::max())
    1011             :             {
    1012         640 :                 bGeneralCase = !OptimizedSumThroughLargerType(
    1013             :                     eSrcType, eBufType, dfK, pData, nPixelSpace, nLineSpace,
    1014             :                     nXSize, nYSize, nSources, papoSources);
    1015             :             }
    1016             :         }
    1017             : 
    1018         908 :         if (bGeneralCase)
    1019             :         {
    1020         247 :             size_t ii = 0;
    1021         737 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    1022             :             {
    1023        8765 :                 for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1024             :                 {
    1025        8275 :                     double dfSum = dfK;
    1026       24824 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1027             :                     {
    1028             :                         const double dfVal =
    1029       16551 :                             GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1030             : 
    1031       16551 :                         if (bHasNoData && IsNoData(dfVal, dfNoData))
    1032             :                         {
    1033          12 :                             if (bPropagateNoData)
    1034             :                             {
    1035           2 :                                 dfSum = dfNoData;
    1036           2 :                                 break;
    1037             :                             }
    1038             :                         }
    1039             :                         else
    1040             :                         {
    1041       16539 :                             dfSum += dfVal;
    1042             :                         }
    1043             :                     }
    1044             : 
    1045        8275 :                     GDALCopyWords(&dfSum, GDT_Float64, 0,
    1046             :                                   static_cast<GByte *>(pData) +
    1047        8275 :                                       static_cast<GSpacing>(nLineSpace) *
    1048        8275 :                                           iLine +
    1049        8275 :                                       iCol * nPixelSpace,
    1050             :                                   eBufType, nPixelSpace, 1);
    1051             :                 }
    1052             :             }
    1053             :         }
    1054             :     }
    1055             : 
    1056             :     /* ---- Return success ---- */
    1057         944 :     return CE_None;
    1058             : } /* SumPixelFunc */
    1059             : 
    1060             : static const char pszDiffPixelFuncMetadata[] =
    1061             :     "<PixelFunctionArgumentsList>"
    1062             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1063             :     "</PixelFunctionArgumentsList>";
    1064             : 
    1065           5 : static CPLErr DiffPixelFunc(void **papoSources, int nSources, void *pData,
    1066             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1067             :                             GDALDataType eBufType, int nPixelSpace,
    1068             :                             int nLineSpace, CSLConstList papszArgs)
    1069             : {
    1070             :     /* ---- Init ---- */
    1071           5 :     if (nSources != 2)
    1072           1 :         return CE_Failure;
    1073             : 
    1074           4 :     double dfNoData{0};
    1075           4 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1076           4 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1077           0 :         return CE_Failure;
    1078             : 
    1079           4 :     if (GDALDataTypeIsComplex(eSrcType))
    1080             :     {
    1081           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1082           1 :         const void *const pReal0 = papoSources[0];
    1083           1 :         const void *const pImag0 =
    1084           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1085           1 :         const void *const pReal1 = papoSources[1];
    1086           1 :         const void *const pImag1 =
    1087           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1088             : 
    1089             :         /* ---- Set pixels ---- */
    1090           1 :         size_t ii = 0;
    1091           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1092             :         {
    1093          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1094             :             {
    1095             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1096          30 :                 double adfPixVal[2] = {GetSrcVal(pReal0, eSrcType, ii) -
    1097          30 :                                            GetSrcVal(pReal1, eSrcType, ii),
    1098          90 :                                        GetSrcVal(pImag0, eSrcType, ii) -
    1099          30 :                                            GetSrcVal(pImag1, eSrcType, ii)};
    1100             : 
    1101          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1102             :                               static_cast<GByte *>(pData) +
    1103          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1104          30 :                                   iCol * nPixelSpace,
    1105             :                               eBufType, nPixelSpace, 1);
    1106             :             }
    1107             :         }
    1108             :     }
    1109             :     else
    1110             :     {
    1111             :         /* ---- Set pixels ---- */
    1112           3 :         size_t ii = 0;
    1113          11 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1114             :         {
    1115          42 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1116             :             {
    1117          34 :                 const double dfA = GetSrcVal(papoSources[0], eSrcType, ii);
    1118          34 :                 const double dfB = GetSrcVal(papoSources[1], eSrcType, ii);
    1119             : 
    1120             :                 const double dfPixVal =
    1121          38 :                     bHasNoData &&
    1122           4 :                             (IsNoData(dfA, dfNoData) || IsNoData(dfB, dfNoData))
    1123          38 :                         ? dfNoData
    1124          34 :                         : dfA - dfB;
    1125             : 
    1126          34 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1127             :                               static_cast<GByte *>(pData) +
    1128          34 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1129          34 :                                   iCol * nPixelSpace,
    1130             :                               eBufType, nPixelSpace, 1);
    1131             :             }
    1132             :         }
    1133             :     }
    1134             : 
    1135             :     /* ---- Return success ---- */
    1136           4 :     return CE_None;
    1137             : }  // DiffPixelFunc
    1138             : 
    1139             : static const char pszMulPixelFuncMetadata[] =
    1140             :     "<PixelFunctionArgumentsList>"
    1141             :     "   <Argument name='k' description='Optional constant factor' "
    1142             :     "type='double' default='1.0' />"
    1143             :     "   <Argument name='propagateNoData' description='Whether the output value "
    1144             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    1145             :     "default='false' />"
    1146             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1147             :     "</PixelFunctionArgumentsList>";
    1148             : 
    1149          11 : static CPLErr MulPixelFunc(void **papoSources, int nSources, void *pData,
    1150             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1151             :                            GDALDataType eBufType, int nPixelSpace,
    1152             :                            int nLineSpace, CSLConstList papszArgs)
    1153             : {
    1154             :     /* ---- Init ---- */
    1155          11 :     if (nSources < 2 && CSLFetchNameValue(papszArgs, "k") == nullptr)
    1156             :     {
    1157           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1158             :                  "mul requires at least two sources or a specified constant k");
    1159           1 :         return CE_Failure;
    1160             :     }
    1161             : 
    1162          10 :     double dfK = 1.0;
    1163          10 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1164           0 :         return CE_Failure;
    1165             : 
    1166          10 :     double dfNoData{0};
    1167          10 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1168          10 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1169           0 :         return CE_Failure;
    1170             : 
    1171          10 :     const bool bPropagateNoData = CPLTestBool(
    1172             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    1173             : 
    1174             :     /* ---- Set pixels ---- */
    1175          10 :     if (GDALDataTypeIsComplex(eSrcType))
    1176             :     {
    1177           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1178             : 
    1179             :         /* ---- Set pixels ---- */
    1180           1 :         size_t ii = 0;
    1181           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1182             :         {
    1183          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1184             :             {
    1185          30 :                 double adfPixVal[2] = {dfK, 0.0};
    1186             : 
    1187          90 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1188             :                 {
    1189          60 :                     const void *const pReal = papoSources[iSrc];
    1190          60 :                     const void *const pImag =
    1191          60 :                         static_cast<const GByte *>(pReal) + nOffset;
    1192             : 
    1193          60 :                     const double dfOldR = adfPixVal[0];
    1194          60 :                     const double dfOldI = adfPixVal[1];
    1195             : 
    1196             :                     // Source raster pixels may be obtained with GetSrcVal
    1197             :                     // macro.
    1198          60 :                     const double dfNewR = GetSrcVal(pReal, eSrcType, ii);
    1199          60 :                     const double dfNewI = GetSrcVal(pImag, eSrcType, ii);
    1200             : 
    1201          60 :                     adfPixVal[0] = dfOldR * dfNewR - dfOldI * dfNewI;
    1202          60 :                     adfPixVal[1] = dfOldR * dfNewI + dfOldI * dfNewR;
    1203             :                 }
    1204             : 
    1205          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1206             :                               static_cast<GByte *>(pData) +
    1207          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1208          30 :                                   iCol * nPixelSpace,
    1209             :                               eBufType, nPixelSpace, 1);
    1210             :             }
    1211             :         }
    1212             :     }
    1213             :     else
    1214             :     {
    1215             :         /* ---- Set pixels ---- */
    1216           9 :         size_t ii = 0;
    1217          75 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1218             :         {
    1219        1278 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1220             :             {
    1221        1212 :                 double dfPixVal = dfK;  // Not complex.
    1222             : 
    1223        4033 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1224             :                 {
    1225             :                     const double dfVal =
    1226        2827 :                         GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1227             : 
    1228        2827 :                     if (bHasNoData && IsNoData(dfVal, dfNoData))
    1229             :                     {
    1230          18 :                         if (bPropagateNoData)
    1231             :                         {
    1232           6 :                             dfPixVal = dfNoData;
    1233           6 :                             break;
    1234             :                         }
    1235             :                     }
    1236             :                     else
    1237             :                     {
    1238        2809 :                         dfPixVal *= dfVal;
    1239             :                     }
    1240             :                 }
    1241             : 
    1242        1212 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1243             :                               static_cast<GByte *>(pData) +
    1244        1212 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1245        1212 :                                   iCol * nPixelSpace,
    1246             :                               eBufType, nPixelSpace, 1);
    1247             :             }
    1248             :         }
    1249             :     }
    1250             : 
    1251             :     /* ---- Return success ---- */
    1252          10 :     return CE_None;
    1253             : }  // MulPixelFunc
    1254             : 
    1255             : static const char pszDivPixelFuncMetadata[] =
    1256             :     "<PixelFunctionArgumentsList>"
    1257             :     "   "
    1258             :     "<Argument type='builtin' value='NoData' optional='true' />"
    1259             :     "</PixelFunctionArgumentsList>";
    1260             : 
    1261           7 : static CPLErr DivPixelFunc(void **papoSources, int nSources, void *pData,
    1262             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1263             :                            GDALDataType eBufType, int nPixelSpace,
    1264             :                            int nLineSpace, CSLConstList papszArgs)
    1265             : {
    1266             :     /* ---- Init ---- */
    1267           7 :     if (nSources != 2)
    1268           0 :         return CE_Failure;
    1269             : 
    1270           7 :     double dfNoData{0};
    1271           7 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1272           7 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1273           0 :         return CE_Failure;
    1274             : 
    1275             :     /* ---- Set pixels ---- */
    1276           7 :     if (GDALDataTypeIsComplex(eSrcType))
    1277             :     {
    1278           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1279           1 :         const void *const pReal0 = papoSources[0];
    1280           1 :         const void *const pImag0 =
    1281           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1282           1 :         const void *const pReal1 = papoSources[1];
    1283           1 :         const void *const pImag1 =
    1284           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1285             : 
    1286             :         /* ---- Set pixels ---- */
    1287           1 :         size_t ii = 0;
    1288           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1289             :         {
    1290          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1291             :             {
    1292             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1293          30 :                 const double dfReal0 = GetSrcVal(pReal0, eSrcType, ii);
    1294          30 :                 const double dfReal1 = GetSrcVal(pReal1, eSrcType, ii);
    1295          30 :                 const double dfImag0 = GetSrcVal(pImag0, eSrcType, ii);
    1296          30 :                 const double dfImag1 = GetSrcVal(pImag1, eSrcType, ii);
    1297          30 :                 const double dfAux = dfReal1 * dfReal1 + dfImag1 * dfImag1;
    1298             : 
    1299             :                 const double adfPixVal[2] = {
    1300             :                     dfAux == 0
    1301          30 :                         ? std::numeric_limits<double>::infinity()
    1302          30 :                         : dfReal0 * dfReal1 / dfAux + dfImag0 * dfImag1 / dfAux,
    1303           0 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1304          30 :                                : dfReal1 / dfAux * dfImag0 -
    1305          30 :                                      dfReal0 * dfImag1 / dfAux};
    1306             : 
    1307          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1308             :                               static_cast<GByte *>(pData) +
    1309          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1310          30 :                                   iCol * nPixelSpace,
    1311             :                               eBufType, nPixelSpace, 1);
    1312             :             }
    1313             :         }
    1314             :     }
    1315             :     else
    1316             :     {
    1317             :         /* ---- Set pixels ---- */
    1318           6 :         size_t ii = 0;
    1319          17 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1320             :         {
    1321          48 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1322             :             {
    1323          37 :                 const double dfNum = GetSrcVal(papoSources[0], eSrcType, ii);
    1324          37 :                 const double dfDenom = GetSrcVal(papoSources[1], eSrcType, ii);
    1325             : 
    1326          37 :                 double dfPixVal = dfNoData;
    1327          41 :                 if (!bHasNoData || (!IsNoData(dfNum, dfNoData) &&
    1328           4 :                                     !IsNoData(dfDenom, dfNoData)))
    1329             :                 {
    1330             :                     // coverity[divide_by_zero]
    1331          33 :                     dfPixVal = dfDenom == 0
    1332          33 :                                    ? std::numeric_limits<double>::infinity()
    1333             :                                    : dfNum / dfDenom;
    1334             :                 }
    1335             : 
    1336          37 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1337             :                               static_cast<GByte *>(pData) +
    1338          37 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1339          37 :                                   iCol * nPixelSpace,
    1340             :                               eBufType, nPixelSpace, 1);
    1341             :             }
    1342             :         }
    1343             :     }
    1344             : 
    1345             :     /* ---- Return success ---- */
    1346           7 :     return CE_None;
    1347             : }  // DivPixelFunc
    1348             : 
    1349           3 : static CPLErr CMulPixelFunc(void **papoSources, int nSources, void *pData,
    1350             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1351             :                             GDALDataType eBufType, int nPixelSpace,
    1352             :                             int nLineSpace)
    1353             : {
    1354             :     /* ---- Init ---- */
    1355           3 :     if (nSources != 2)
    1356           1 :         return CE_Failure;
    1357             : 
    1358             :     /* ---- Set pixels ---- */
    1359           2 :     if (GDALDataTypeIsComplex(eSrcType))
    1360             :     {
    1361           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1362           1 :         const void *const pReal0 = papoSources[0];
    1363           1 :         const void *const pImag0 =
    1364           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1365           1 :         const void *const pReal1 = papoSources[1];
    1366           1 :         const void *const pImag1 =
    1367           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1368             : 
    1369           1 :         size_t ii = 0;
    1370           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1371             :         {
    1372          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1373             :             {
    1374             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1375          30 :                 const double dfReal0 = GetSrcVal(pReal0, eSrcType, ii);
    1376          30 :                 const double dfReal1 = GetSrcVal(pReal1, eSrcType, ii);
    1377          30 :                 const double dfImag0 = GetSrcVal(pImag0, eSrcType, ii);
    1378          30 :                 const double dfImag1 = GetSrcVal(pImag1, eSrcType, ii);
    1379             :                 const double adfPixVal[2] = {
    1380          30 :                     dfReal0 * dfReal1 + dfImag0 * dfImag1,
    1381          30 :                     dfReal1 * dfImag0 - dfReal0 * dfImag1};
    1382             : 
    1383          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1384             :                               static_cast<GByte *>(pData) +
    1385          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1386          30 :                                   iCol * nPixelSpace,
    1387             :                               eBufType, nPixelSpace, 1);
    1388             :             }
    1389             :         }
    1390             :     }
    1391             :     else
    1392             :     {
    1393           1 :         size_t ii = 0;
    1394          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1395             :         {
    1396         420 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1397             :             {
    1398             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1399             :                 // Not complex.
    1400         400 :                 const double adfPixVal[2] = {
    1401         400 :                     GetSrcVal(papoSources[0], eSrcType, ii) *
    1402         400 :                         GetSrcVal(papoSources[1], eSrcType, ii),
    1403         400 :                     0.0};
    1404             : 
    1405         400 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1406             :                               static_cast<GByte *>(pData) +
    1407         400 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1408         400 :                                   iCol * nPixelSpace,
    1409             :                               eBufType, nPixelSpace, 1);
    1410             :             }
    1411             :         }
    1412             :     }
    1413             : 
    1414             :     /* ---- Return success ---- */
    1415           2 :     return CE_None;
    1416             : }  // CMulPixelFunc
    1417             : 
    1418             : static const char pszInvPixelFuncMetadata[] =
    1419             :     "<PixelFunctionArgumentsList>"
    1420             :     "   <Argument name='k' description='Optional constant factor' "
    1421             :     "type='double' default='1.0' />"
    1422             :     "   "
    1423             :     "<Argument type='builtin' value='NoData' optional='true' />"
    1424             :     "</PixelFunctionArgumentsList>";
    1425             : 
    1426           9 : static CPLErr InvPixelFunc(void **papoSources, int nSources, void *pData,
    1427             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1428             :                            GDALDataType eBufType, int nPixelSpace,
    1429             :                            int nLineSpace, CSLConstList papszArgs)
    1430             : {
    1431             :     /* ---- Init ---- */
    1432           9 :     if (nSources != 1)
    1433           1 :         return CE_Failure;
    1434             : 
    1435           8 :     double dfK = 1.0;
    1436           8 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1437           0 :         return CE_Failure;
    1438             : 
    1439           8 :     double dfNoData{0};
    1440           8 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1441           8 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1442           0 :         return CE_Failure;
    1443             : 
    1444             :     /* ---- Set pixels ---- */
    1445           8 :     if (GDALDataTypeIsComplex(eSrcType))
    1446             :     {
    1447           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1448           2 :         const void *const pReal = papoSources[0];
    1449           2 :         const void *const pImag =
    1450           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1451             : 
    1452           2 :         size_t ii = 0;
    1453           9 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1454             :         {
    1455          38 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1456             :             {
    1457             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1458          31 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1459          31 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1460          31 :                 const double dfAux = dfReal * dfReal + dfImag * dfImag;
    1461             :                 const double adfPixVal[2] = {
    1462          31 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1463          30 :                                : dfK * dfReal / dfAux,
    1464           1 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1465          31 :                                : -dfK * dfImag / dfAux};
    1466             : 
    1467          31 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1468             :                               static_cast<GByte *>(pData) +
    1469          31 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1470          31 :                                   iCol * nPixelSpace,
    1471             :                               eBufType, nPixelSpace, 1);
    1472             :             }
    1473             :         }
    1474             :     }
    1475             :     else
    1476             :     {
    1477             :         /* ---- Set pixels ---- */
    1478           6 :         size_t ii = 0;
    1479          50 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1480             :         {
    1481         851 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1482             :             {
    1483             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1484             :                 // Not complex.
    1485         807 :                 const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1486         807 :                 double dfPixVal = dfNoData;
    1487             : 
    1488         807 :                 if (!bHasNoData || !IsNoData(dfVal, dfNoData))
    1489             :                 {
    1490         802 :                     dfPixVal = dfVal == 0
    1491         802 :                                    ? std::numeric_limits<double>::infinity()
    1492         801 :                                    : dfK / dfVal;
    1493             :                 }
    1494             : 
    1495         807 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1496             :                               static_cast<GByte *>(pData) +
    1497         807 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1498         807 :                                   iCol * nPixelSpace,
    1499             :                               eBufType, nPixelSpace, 1);
    1500             :             }
    1501             :         }
    1502             :     }
    1503             : 
    1504             :     /* ---- Return success ---- */
    1505           8 :     return CE_None;
    1506             : }  // InvPixelFunc
    1507             : 
    1508           4 : static CPLErr IntensityPixelFunc(void **papoSources, int nSources, void *pData,
    1509             :                                  int nXSize, int nYSize, GDALDataType eSrcType,
    1510             :                                  GDALDataType eBufType, int nPixelSpace,
    1511             :                                  int nLineSpace)
    1512             : {
    1513             :     /* ---- Init ---- */
    1514           4 :     if (nSources != 1)
    1515           1 :         return CE_Failure;
    1516             : 
    1517           3 :     if (GDALDataTypeIsComplex(eSrcType))
    1518             :     {
    1519           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1520           2 :         const void *const pReal = papoSources[0];
    1521           2 :         const void *const pImag =
    1522           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1523             : 
    1524             :         /* ---- Set pixels ---- */
    1525           2 :         size_t ii = 0;
    1526          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1527             :         {
    1528          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1529             :             {
    1530             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1531          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1532          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1533             : 
    1534          60 :                 const double dfPixVal = dfReal * dfReal + dfImag * dfImag;
    1535             : 
    1536          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1537             :                               static_cast<GByte *>(pData) +
    1538          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1539          60 :                                   iCol * nPixelSpace,
    1540             :                               eBufType, nPixelSpace, 1);
    1541             :             }
    1542             :         }
    1543             :     }
    1544             :     else
    1545             :     {
    1546             :         /* ---- Set pixels ---- */
    1547           1 :         size_t ii = 0;
    1548          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1549             :         {
    1550         420 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1551             :             {
    1552             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1553         400 :                 double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1554         400 :                 dfPixVal *= dfPixVal;
    1555             : 
    1556         400 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1557             :                               static_cast<GByte *>(pData) +
    1558         400 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1559         400 :                                   iCol * nPixelSpace,
    1560             :                               eBufType, nPixelSpace, 1);
    1561             :             }
    1562             :         }
    1563             :     }
    1564             : 
    1565             :     /* ---- Return success ---- */
    1566           3 :     return CE_None;
    1567             : }  // IntensityPixelFunc
    1568             : 
    1569             : static const char pszSqrtPixelFuncMetadata[] =
    1570             :     "<PixelFunctionArgumentsList>"
    1571             :     "   <Argument type='builtin' value='NoData' optional='true'/>"
    1572             :     "</PixelFunctionArgumentsList>";
    1573             : 
    1574           3 : static CPLErr SqrtPixelFunc(void **papoSources, int nSources, void *pData,
    1575             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1576             :                             GDALDataType eBufType, int nPixelSpace,
    1577             :                             int nLineSpace, CSLConstList papszArgs)
    1578             : {
    1579             :     /* ---- Init ---- */
    1580           3 :     if (nSources != 1)
    1581           1 :         return CE_Failure;
    1582           2 :     if (GDALDataTypeIsComplex(eSrcType))
    1583           0 :         return CE_Failure;
    1584             : 
    1585           2 :     double dfNoData{0};
    1586           2 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1587           2 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1588           0 :         return CE_Failure;
    1589             : 
    1590             :     /* ---- Set pixels ---- */
    1591           2 :     size_t ii = 0;
    1592          23 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1593             :     {
    1594         423 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1595             :         {
    1596             :             // Source raster pixels may be obtained with GetSrcVal macro.
    1597         402 :             double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1598             : 
    1599         402 :             if (bHasNoData && IsNoData(dfPixVal, dfNoData))
    1600             :             {
    1601           2 :                 dfPixVal = dfNoData;
    1602             :             }
    1603             :             else
    1604             :             {
    1605         400 :                 dfPixVal = std::sqrt(dfPixVal);
    1606             :             }
    1607             : 
    1608         402 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1609             :                           static_cast<GByte *>(pData) +
    1610         402 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1611         402 :                               iCol * nPixelSpace,
    1612             :                           eBufType, nPixelSpace, 1);
    1613             :         }
    1614             :     }
    1615             : 
    1616             :     /* ---- Return success ---- */
    1617           2 :     return CE_None;
    1618             : }  // SqrtPixelFunc
    1619             : 
    1620          13 : static CPLErr Log10PixelFuncHelper(void **papoSources, int nSources,
    1621             :                                    void *pData, int nXSize, int nYSize,
    1622             :                                    GDALDataType eSrcType, GDALDataType eBufType,
    1623             :                                    int nPixelSpace, int nLineSpace,
    1624             :                                    CSLConstList papszArgs, double fact)
    1625             : {
    1626             :     /* ---- Init ---- */
    1627          13 :     if (nSources != 1)
    1628           2 :         return CE_Failure;
    1629             : 
    1630          11 :     double dfNoData{0};
    1631          11 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1632          11 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1633           0 :         return CE_Failure;
    1634             : 
    1635          11 :     if (GDALDataTypeIsComplex(eSrcType))
    1636             :     {
    1637             :         // Complex input datatype.
    1638           5 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1639           5 :         const void *const pReal = papoSources[0];
    1640           5 :         const void *const pImag =
    1641           5 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1642             : 
    1643             :         /* We should compute fact * log10( sqrt( dfReal * dfReal + dfImag *
    1644             :          * dfImag ) ) */
    1645             :         /* Given that log10(sqrt(x)) = 0.5 * log10(x) */
    1646             :         /* we can remove the sqrt() by multiplying fact by 0.5 */
    1647           5 :         fact *= 0.5;
    1648             : 
    1649             :         /* ---- Set pixels ---- */
    1650           5 :         size_t ii = 0;
    1651          35 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1652             :         {
    1653         180 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1654             :             {
    1655             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1656         150 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1657         150 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1658             : 
    1659             :                 const double dfPixVal =
    1660         150 :                     fact * log10(dfReal * dfReal + dfImag * dfImag);
    1661             : 
    1662         150 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1663             :                               static_cast<GByte *>(pData) +
    1664         150 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1665         150 :                                   iCol * nPixelSpace,
    1666             :                               eBufType, nPixelSpace, 1);
    1667             :             }
    1668             :         }
    1669             :     }
    1670             :     else
    1671             :     {
    1672             :         /* ---- Set pixels ---- */
    1673           6 :         size_t ii = 0;
    1674          88 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1675             :         {
    1676        1686 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1677             :             {
    1678             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1679        1604 :                 const double dfSrcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1680             :                 const double dfPixVal =
    1681           4 :                     bHasNoData && IsNoData(dfSrcVal, dfNoData)
    1682        1608 :                         ? dfNoData
    1683        1600 :                         : fact * std::log10(std::abs(dfSrcVal));
    1684             : 
    1685        1604 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1686             :                               static_cast<GByte *>(pData) +
    1687        1604 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1688        1604 :                                   iCol * nPixelSpace,
    1689             :                               eBufType, nPixelSpace, 1);
    1690             :             }
    1691             :         }
    1692             :     }
    1693             : 
    1694             :     /* ---- Return success ---- */
    1695          11 :     return CE_None;
    1696             : }  // Log10PixelFuncHelper
    1697             : 
    1698             : static const char pszLog10PixelFuncMetadata[] =
    1699             :     "<PixelFunctionArgumentsList>"
    1700             :     "   <Argument type='builtin' value='NoData' optional='true'/>"
    1701             :     "</PixelFunctionArgumentsList>";
    1702             : 
    1703           5 : static CPLErr Log10PixelFunc(void **papoSources, int nSources, void *pData,
    1704             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    1705             :                              GDALDataType eBufType, int nPixelSpace,
    1706             :                              int nLineSpace, CSLConstList papszArgs)
    1707             : {
    1708           5 :     return Log10PixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1709             :                                 eSrcType, eBufType, nPixelSpace, nLineSpace,
    1710           5 :                                 papszArgs, 1.0);
    1711             : }  // Log10PixelFunc
    1712             : 
    1713             : static const char pszDBPixelFuncMetadata[] =
    1714             :     "<PixelFunctionArgumentsList>"
    1715             :     "   <Argument name='fact' description='Factor' type='double' "
    1716             :     "default='20.0' />"
    1717             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1718             :     "</PixelFunctionArgumentsList>";
    1719             : 
    1720           8 : static CPLErr DBPixelFunc(void **papoSources, int nSources, void *pData,
    1721             :                           int nXSize, int nYSize, GDALDataType eSrcType,
    1722             :                           GDALDataType eBufType, int nPixelSpace,
    1723             :                           int nLineSpace, CSLConstList papszArgs)
    1724             : {
    1725           8 :     double dfFact = 20.;
    1726           8 :     if (FetchDoubleArg(papszArgs, "fact", &dfFact, &dfFact) != CE_None)
    1727           0 :         return CE_Failure;
    1728             : 
    1729           8 :     return Log10PixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1730             :                                 eSrcType, eBufType, nPixelSpace, nLineSpace,
    1731           8 :                                 papszArgs, dfFact);
    1732             : }  // DBPixelFunc
    1733             : 
    1734           8 : static CPLErr ExpPixelFuncHelper(void **papoSources, int nSources, void *pData,
    1735             :                                  int nXSize, int nYSize, GDALDataType eSrcType,
    1736             :                                  GDALDataType eBufType, int nPixelSpace,
    1737             :                                  int nLineSpace, CSLConstList papszArgs,
    1738             :                                  double base, double fact)
    1739             : {
    1740             :     /* ---- Init ---- */
    1741           8 :     if (nSources != 1)
    1742           2 :         return CE_Failure;
    1743           6 :     if (GDALDataTypeIsComplex(eSrcType))
    1744           0 :         return CE_Failure;
    1745             : 
    1746           6 :     double dfNoData{0};
    1747           6 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1748           6 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1749           0 :         return CE_Failure;
    1750             : 
    1751             :     /* ---- Set pixels ---- */
    1752           6 :     size_t ii = 0;
    1753         107 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1754             :     {
    1755        2103 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1756             :         {
    1757             :             // Source raster pixels may be obtained with GetSrcVal macro.
    1758        2002 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1759           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    1760        2004 :                                         ? dfNoData
    1761        2000 :                                         : pow(base, dfVal * fact);
    1762             : 
    1763        2002 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1764             :                           static_cast<GByte *>(pData) +
    1765        2002 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1766        2002 :                               iCol * nPixelSpace,
    1767             :                           eBufType, nPixelSpace, 1);
    1768             :         }
    1769             :     }
    1770             : 
    1771             :     /* ---- Return success ---- */
    1772           6 :     return CE_None;
    1773             : }  // ExpPixelFuncHelper
    1774             : 
    1775             : static const char pszExpPixelFuncMetadata[] =
    1776             :     "<PixelFunctionArgumentsList>"
    1777             :     "   <Argument name='base' description='Base' type='double' "
    1778             :     "default='2.7182818284590452353602874713526624' />"
    1779             :     "   <Argument name='fact' description='Factor' type='double' default='1' />"
    1780             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1781             :     "</PixelFunctionArgumentsList>";
    1782             : 
    1783           4 : static CPLErr ExpPixelFunc(void **papoSources, int nSources, void *pData,
    1784             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1785             :                            GDALDataType eBufType, int nPixelSpace,
    1786             :                            int nLineSpace, CSLConstList papszArgs)
    1787             : {
    1788           4 :     double dfBase = 2.7182818284590452353602874713526624;
    1789           4 :     double dfFact = 1.;
    1790             : 
    1791           4 :     if (FetchDoubleArg(papszArgs, "base", &dfBase, &dfBase) != CE_None)
    1792           0 :         return CE_Failure;
    1793             : 
    1794           4 :     if (FetchDoubleArg(papszArgs, "fact", &dfFact, &dfFact) != CE_None)
    1795           0 :         return CE_Failure;
    1796             : 
    1797           4 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1798             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    1799           4 :                               papszArgs, dfBase, dfFact);
    1800             : }  // ExpPixelFunc
    1801             : 
    1802           2 : static CPLErr dB2AmpPixelFunc(void **papoSources, int nSources, void *pData,
    1803             :                               int nXSize, int nYSize, GDALDataType eSrcType,
    1804             :                               GDALDataType eBufType, int nPixelSpace,
    1805             :                               int nLineSpace)
    1806             : {
    1807           2 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1808             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    1809           2 :                               nullptr, 10.0, 1. / 20);
    1810             : }  // dB2AmpPixelFunc
    1811             : 
    1812           2 : static CPLErr dB2PowPixelFunc(void **papoSources, int nSources, void *pData,
    1813             :                               int nXSize, int nYSize, GDALDataType eSrcType,
    1814             :                               GDALDataType eBufType, int nPixelSpace,
    1815             :                               int nLineSpace)
    1816             : {
    1817           2 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1818             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    1819           2 :                               nullptr, 10.0, 1. / 10);
    1820             : }  // dB2PowPixelFunc
    1821             : 
    1822             : static const char pszPowPixelFuncMetadata[] =
    1823             :     "<PixelFunctionArgumentsList>"
    1824             :     "   <Argument name='power' description='Exponent' type='double' "
    1825             :     "mandatory='1' />"
    1826             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1827             :     "</PixelFunctionArgumentsList>";
    1828             : 
    1829           2 : static CPLErr PowPixelFunc(void **papoSources, int nSources, void *pData,
    1830             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1831             :                            GDALDataType eBufType, int nPixelSpace,
    1832             :                            int nLineSpace, CSLConstList papszArgs)
    1833             : {
    1834             :     /* ---- Init ---- */
    1835           2 :     if (nSources != 1)
    1836           0 :         return CE_Failure;
    1837           2 :     if (GDALDataTypeIsComplex(eSrcType))
    1838           0 :         return CE_Failure;
    1839             : 
    1840             :     double power;
    1841           2 :     if (FetchDoubleArg(papszArgs, "power", &power) != CE_None)
    1842           0 :         return CE_Failure;
    1843             : 
    1844           2 :     double dfNoData{0};
    1845           2 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1846           2 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1847           0 :         return CE_Failure;
    1848             : 
    1849             :     /* ---- Set pixels ---- */
    1850           2 :     size_t ii = 0;
    1851          23 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1852             :     {
    1853         423 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1854             :         {
    1855         402 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1856             : 
    1857           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    1858         404 :                                         ? dfNoData
    1859         400 :                                         : std::pow(dfVal, power);
    1860             : 
    1861         402 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1862             :                           static_cast<GByte *>(pData) +
    1863         402 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1864         402 :                               iCol * nPixelSpace,
    1865             :                           eBufType, nPixelSpace, 1);
    1866             :         }
    1867             :     }
    1868             : 
    1869             :     /* ---- Return success ---- */
    1870           2 :     return CE_None;
    1871             : }
    1872             : 
    1873             : // Given nt intervals spaced by dt and beginning at t0, return the index of
    1874             : // the lower bound of the interval that should be used to
    1875             : // interpolate/extrapolate a value for t.
    1876          17 : static std::size_t intervalLeft(double t0, double dt, std::size_t nt, double t)
    1877             : {
    1878          17 :     if (t < t0)
    1879             :     {
    1880           4 :         return 0;
    1881             :     }
    1882             : 
    1883          13 :     std::size_t n = static_cast<std::size_t>((t - t0) / dt);
    1884             : 
    1885          13 :     if (n >= nt - 1)
    1886             :     {
    1887           3 :         return nt - 2;
    1888             :     }
    1889             : 
    1890          10 :     return n;
    1891             : }
    1892             : 
    1893          17 : static double InterpolateLinear(double dfX0, double dfX1, double dfY0,
    1894             :                                 double dfY1, double dfX)
    1895             : {
    1896          17 :     return dfY0 + (dfX - dfX0) * (dfY1 - dfY0) / (dfX1 - dfX0);
    1897             : }
    1898             : 
    1899          13 : static double InterpolateExponential(double dfX0, double dfX1, double dfY0,
    1900             :                                      double dfY1, double dfX)
    1901             : {
    1902          13 :     const double r = std::log(dfY1 / dfY0) / (dfX1 - dfX0);
    1903          13 :     return dfY0 * std::exp(r * (dfX - dfX0));
    1904             : }
    1905             : 
    1906             : static const char pszInterpolatePixelFuncMetadata[] =
    1907             :     "<PixelFunctionArgumentsList>"
    1908             :     "   <Argument name='t0' description='t0' type='double' mandatory='1' />"
    1909             :     "   <Argument name='dt' description='dt' type='double' mandatory='1' />"
    1910             :     "   <Argument name='t' description='t' type='double' mandatory='1' />"
    1911             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1912             :     "</PixelFunctionArgumentsList>";
    1913             : 
    1914             : template <decltype(InterpolateLinear) InterpolationFunction>
    1915          17 : CPLErr InterpolatePixelFunc(void **papoSources, int nSources, void *pData,
    1916             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1917             :                             GDALDataType eBufType, int nPixelSpace,
    1918             :                             int nLineSpace, CSLConstList papszArgs)
    1919             : {
    1920             :     /* ---- Init ---- */
    1921          17 :     if (GDALDataTypeIsComplex(eSrcType))
    1922           0 :         return CE_Failure;
    1923             : 
    1924             :     double dfT0;
    1925          17 :     if (FetchDoubleArg(papszArgs, "t0", &dfT0) == CE_Failure)
    1926           0 :         return CE_Failure;
    1927             : 
    1928             :     double dfT;
    1929          17 :     if (FetchDoubleArg(papszArgs, "t", &dfT) == CE_Failure)
    1930           0 :         return CE_Failure;
    1931             : 
    1932             :     double dfDt;
    1933          17 :     if (FetchDoubleArg(papszArgs, "dt", &dfDt) == CE_Failure)
    1934           0 :         return CE_Failure;
    1935             : 
    1936          17 :     double dfNoData{0};
    1937          17 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1938          17 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1939           0 :         return CE_Failure;
    1940             : 
    1941          17 :     if (nSources < 2)
    1942             :     {
    1943           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1944             :                  "At least two sources required for interpolation.");
    1945           0 :         return CE_Failure;
    1946             :     }
    1947             : 
    1948          17 :     if (dfT == 0 || !std::isfinite(dfT))
    1949             :     {
    1950           0 :         CPLError(CE_Failure, CPLE_AppDefined, "dt must be finite and non-zero");
    1951           0 :         return CE_Failure;
    1952             :     }
    1953             : 
    1954          17 :     const auto i0 = intervalLeft(dfT0, dfDt, nSources, dfT);
    1955          17 :     const auto i1 = i0 + 1;
    1956          17 :     const double dfX0 = dfT0 + static_cast<double>(i0) * dfDt;
    1957          17 :     const double dfX1 = dfT0 + static_cast<double>(i0 + 1) * dfDt;
    1958             : 
    1959             :     /* ---- Set pixels ---- */
    1960          17 :     size_t ii = 0;
    1961          41 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1962             :     {
    1963          72 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1964             :         {
    1965          48 :             const double dfY0 = GetSrcVal(papoSources[i0], eSrcType, ii);
    1966          48 :             const double dfY1 = GetSrcVal(papoSources[i1], eSrcType, ii);
    1967             : 
    1968          48 :             double dfPixVal = dfNoData;
    1969          48 :             if (dfT == dfX0)
    1970           8 :                 dfPixVal = dfY0;
    1971          40 :             else if (dfT == dfX1)
    1972           0 :                 dfPixVal = dfY1;
    1973          52 :             else if (!bHasNoData ||
    1974          12 :                      (!IsNoData(dfY0, dfNoData) && !IsNoData(dfY1, dfNoData)))
    1975          30 :                 dfPixVal = InterpolationFunction(dfX0, dfX1, dfY0, dfY1, dfT);
    1976             : 
    1977          48 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1978             :                           static_cast<GByte *>(pData) +
    1979          48 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1980          48 :                               iCol * nPixelSpace,
    1981             :                           eBufType, nPixelSpace, 1);
    1982             :         }
    1983             :     }
    1984             : 
    1985             :     /* ---- Return success ---- */
    1986          17 :     return CE_None;
    1987             : }
    1988             : 
    1989             : static const char pszReplaceNoDataPixelFuncMetadata[] =
    1990             :     "<PixelFunctionArgumentsList>"
    1991             :     "   <Argument type='builtin' value='NoData' />"
    1992             :     "   <Argument name='to' type='double' description='New NoData value to be "
    1993             :     "replaced' default='nan' />"
    1994             :     "</PixelFunctionArgumentsList>";
    1995             : 
    1996           2 : static CPLErr ReplaceNoDataPixelFunc(void **papoSources, int nSources,
    1997             :                                      void *pData, int nXSize, int nYSize,
    1998             :                                      GDALDataType eSrcType,
    1999             :                                      GDALDataType eBufType, int nPixelSpace,
    2000             :                                      int nLineSpace, CSLConstList papszArgs)
    2001             : {
    2002             :     /* ---- Init ---- */
    2003           2 :     if (nSources != 1)
    2004           0 :         return CE_Failure;
    2005           2 :     if (GDALDataTypeIsComplex(eSrcType))
    2006             :     {
    2007           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2008             :                  "replace_nodata cannot convert complex data types");
    2009           0 :         return CE_Failure;
    2010             :     }
    2011             : 
    2012           2 :     double dfOldNoData, dfNewNoData = NAN;
    2013           2 :     if (FetchDoubleArg(papszArgs, "NoData", &dfOldNoData) != CE_None)
    2014           0 :         return CE_Failure;
    2015           2 :     if (FetchDoubleArg(papszArgs, "to", &dfNewNoData, &dfNewNoData) != CE_None)
    2016           0 :         return CE_Failure;
    2017             : 
    2018           2 :     if (!GDALDataTypeIsFloating(eBufType) && std::isnan(dfNewNoData))
    2019             :     {
    2020           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2021             :                  "Using nan requires a floating point type output buffer");
    2022           0 :         return CE_Failure;
    2023             :     }
    2024             : 
    2025             :     /* ---- Set pixels ---- */
    2026           2 :     size_t ii = 0;
    2027         102 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2028             :     {
    2029        5100 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2030             :         {
    2031        5000 :             double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2032        5000 :             if (dfPixVal == dfOldNoData || std::isnan(dfPixVal))
    2033        3200 :                 dfPixVal = dfNewNoData;
    2034             : 
    2035        5000 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2036             :                           static_cast<GByte *>(pData) +
    2037        5000 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2038        5000 :                               iCol * nPixelSpace,
    2039             :                           eBufType, nPixelSpace, 1);
    2040             :         }
    2041             :     }
    2042             : 
    2043             :     /* ---- Return success ---- */
    2044           2 :     return CE_None;
    2045             : }
    2046             : 
    2047             : static const char pszScalePixelFuncMetadata[] =
    2048             :     "<PixelFunctionArgumentsList>"
    2049             :     "   <Argument type='builtin' value='offset' />"
    2050             :     "   <Argument type='builtin' value='scale' />"
    2051             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2052             :     "</PixelFunctionArgumentsList>";
    2053             : 
    2054           2 : static CPLErr ScalePixelFunc(void **papoSources, int nSources, void *pData,
    2055             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    2056             :                              GDALDataType eBufType, int nPixelSpace,
    2057             :                              int nLineSpace, CSLConstList papszArgs)
    2058             : {
    2059             :     /* ---- Init ---- */
    2060           2 :     if (nSources != 1)
    2061           0 :         return CE_Failure;
    2062           2 :     if (GDALDataTypeIsComplex(eSrcType))
    2063             :     {
    2064           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2065             :                  "scale cannot by applied to complex data types");
    2066           0 :         return CE_Failure;
    2067             :     }
    2068             : 
    2069           2 :     double dfNoData{0};
    2070           2 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2071           2 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2072           0 :         return CE_Failure;
    2073             : 
    2074             :     double dfScale, dfOffset;
    2075           2 :     if (FetchDoubleArg(papszArgs, "scale", &dfScale) != CE_None)
    2076           0 :         return CE_Failure;
    2077           2 :     if (FetchDoubleArg(papszArgs, "offset", &dfOffset) != CE_None)
    2078           0 :         return CE_Failure;
    2079             : 
    2080             :     /* ---- Set pixels ---- */
    2081           2 :     size_t ii = 0;
    2082          23 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2083             :     {
    2084         423 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2085             :         {
    2086         402 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2087             : 
    2088           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2089         404 :                                         ? dfNoData
    2090         400 :                                         : dfVal * dfScale + dfOffset;
    2091             : 
    2092         402 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2093             :                           static_cast<GByte *>(pData) +
    2094         402 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2095         402 :                               iCol * nPixelSpace,
    2096             :                           eBufType, nPixelSpace, 1);
    2097             :         }
    2098             :     }
    2099             : 
    2100             :     /* ---- Return success ---- */
    2101           2 :     return CE_None;
    2102             : }
    2103             : 
    2104             : static const char pszNormDiffPixelFuncMetadata[] =
    2105             :     "<PixelFunctionArgumentsList>"
    2106             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2107             :     "</PixelFunctionArgumentsList>";
    2108             : 
    2109           3 : static CPLErr NormDiffPixelFunc(void **papoSources, int nSources, void *pData,
    2110             :                                 int nXSize, int nYSize, GDALDataType eSrcType,
    2111             :                                 GDALDataType eBufType, int nPixelSpace,
    2112             :                                 int nLineSpace, CSLConstList papszArgs)
    2113             : {
    2114             :     /* ---- Init ---- */
    2115           3 :     if (nSources != 2)
    2116           0 :         return CE_Failure;
    2117             : 
    2118           3 :     if (GDALDataTypeIsComplex(eSrcType))
    2119             :     {
    2120           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2121             :                  "norm_diff cannot by applied to complex data types");
    2122           0 :         return CE_Failure;
    2123             :     }
    2124             : 
    2125           3 :     double dfNoData{0};
    2126           3 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2127           3 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2128           0 :         return CE_Failure;
    2129             : 
    2130             :     /* ---- Set pixels ---- */
    2131           3 :     size_t ii = 0;
    2132          11 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2133             :     {
    2134          42 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2135             :         {
    2136          34 :             const double dfLeftVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2137          34 :             const double dfRightVal = GetSrcVal(papoSources[1], eSrcType, ii);
    2138             : 
    2139          34 :             double dfPixVal = dfNoData;
    2140             : 
    2141          35 :             if (!bHasNoData || (!IsNoData(dfLeftVal, dfNoData) &&
    2142           1 :                                 !IsNoData(dfRightVal, dfNoData)))
    2143             :             {
    2144          30 :                 const double dfDenom = (dfLeftVal + dfRightVal);
    2145             :                 // coverity[divide_by_zero]
    2146          30 :                 dfPixVal = dfDenom == 0
    2147          30 :                                ? std::numeric_limits<double>::infinity()
    2148          30 :                                : (dfLeftVal - dfRightVal) / dfDenom;
    2149             :             }
    2150             : 
    2151          34 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2152             :                           static_cast<GByte *>(pData) +
    2153          34 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2154          34 :                               iCol * nPixelSpace,
    2155             :                           eBufType, nPixelSpace, 1);
    2156             :         }
    2157             :     }
    2158             : 
    2159             :     /* ---- Return success ---- */
    2160           3 :     return CE_None;
    2161             : }  // NormDiffPixelFunc
    2162             : 
    2163             : /************************************************************************/
    2164             : /*                   pszMinMaxFuncMetadataNodata                        */
    2165             : /************************************************************************/
    2166             : 
    2167             : static const char pszMinMaxFuncMetadataNodata[] =
    2168             :     "<PixelFunctionArgumentsList>"
    2169             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2170             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2171             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2172             :     "default='false' />"
    2173             :     "</PixelFunctionArgumentsList>";
    2174             : 
    2175             : template <class Comparator>
    2176          12 : static CPLErr MinOrMaxPixelFunc(void **papoSources, int nSources, void *pData,
    2177             :                                 int nXSize, int nYSize, GDALDataType eSrcType,
    2178             :                                 GDALDataType eBufType, int nPixelSpace,
    2179             :                                 int nLineSpace, CSLConstList papszArgs)
    2180             : {
    2181             :     /* ---- Init ---- */
    2182          12 :     if (GDALDataTypeIsComplex(eSrcType))
    2183             :     {
    2184           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2185             :                  "Complex data type not supported for min/max().");
    2186           0 :         return CE_Failure;
    2187             :     }
    2188             : 
    2189          12 :     double dfNoData = std::numeric_limits<double>::quiet_NaN();
    2190          12 :     if (FetchDoubleArg(papszArgs, "NoData", &dfNoData, &dfNoData) != CE_None)
    2191           0 :         return CE_Failure;
    2192          12 :     const bool bPropagateNoData = CPLTestBool(
    2193             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2194             : 
    2195             :     /* ---- Set pixels ---- */
    2196          12 :     size_t ii = 0;
    2197         269 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2198             :     {
    2199       12773 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2200             :         {
    2201       12516 :             double dfRes = std::numeric_limits<double>::quiet_NaN();
    2202             : 
    2203       43445 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    2204             :             {
    2205       32985 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    2206             : 
    2207       32985 :                 if (std::isnan(dfVal) || dfVal == dfNoData)
    2208             :                 {
    2209       14370 :                     if (bPropagateNoData)
    2210             :                     {
    2211        2056 :                         dfRes = dfNoData;
    2212        2056 :                         break;
    2213             :                     }
    2214             :                 }
    2215       18615 :                 else if (Comparator::compare(dfVal, dfRes))
    2216             :                 {
    2217        9961 :                     dfRes = dfVal;
    2218             :                 }
    2219             :             }
    2220             : 
    2221       12516 :             if (!bPropagateNoData && std::isnan(dfRes))
    2222             :             {
    2223        3203 :                 dfRes = dfNoData;
    2224             :             }
    2225             : 
    2226       12516 :             GDALCopyWords(&dfRes, GDT_Float64, 0,
    2227             :                           static_cast<GByte *>(pData) +
    2228       12516 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2229       12516 :                               iCol * nPixelSpace,
    2230             :                           eBufType, nPixelSpace, 1);
    2231             :         }
    2232             :     }
    2233             : 
    2234             :     /* ---- Return success ---- */
    2235          12 :     return CE_None;
    2236             : } /* MinOrMaxPixelFunc */
    2237             : 
    2238           7 : static CPLErr MinPixelFunc(void **papoSources, int nSources, void *pData,
    2239             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2240             :                            GDALDataType eBufType, int nPixelSpace,
    2241             :                            int nLineSpace, CSLConstList papszArgs)
    2242             : {
    2243             :     struct Comparator
    2244             :     {
    2245        2712 :         static bool compare(double x, double resVal)
    2246             :         {
    2247             :             // Written this way to deal with resVal being NaN
    2248        2712 :             return !(x >= resVal);
    2249             :         }
    2250             :     };
    2251             : 
    2252           7 :     return MinOrMaxPixelFunc<Comparator>(papoSources, nSources, pData, nXSize,
    2253             :                                          nYSize, eSrcType, eBufType,
    2254           7 :                                          nPixelSpace, nLineSpace, papszArgs);
    2255             : }
    2256             : 
    2257           5 : static CPLErr MaxPixelFunc(void **papoSources, int nSources, void *pData,
    2258             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2259             :                            GDALDataType eBufType, int nPixelSpace,
    2260             :                            int nLineSpace, CSLConstList papszArgs)
    2261             : {
    2262             :     struct Comparator
    2263             :     {
    2264       15903 :         static bool compare(double x, double resVal)
    2265             :         {
    2266             :             // Written this way to deal with resVal being NaN
    2267       15903 :             return !(x <= resVal);
    2268             :         }
    2269             :     };
    2270             : 
    2271           5 :     return MinOrMaxPixelFunc<Comparator>(papoSources, nSources, pData, nXSize,
    2272             :                                          nYSize, eSrcType, eBufType,
    2273           5 :                                          nPixelSpace, nLineSpace, papszArgs);
    2274             : }
    2275             : 
    2276             : static const char pszExprPixelFuncMetadata[] =
    2277             :     "<PixelFunctionArgumentsList>"
    2278             :     "   <Argument name='expression' "
    2279             :     "             description='Expression to be evaluated' "
    2280             :     "             type='string'></Argument>"
    2281             :     "   <Argument name='dialect' "
    2282             :     "             description='Expression dialect' "
    2283             :     "             type='string-select'"
    2284             :     "             default='muparser'>"
    2285             :     "       <Value>exprtk</Value>"
    2286             :     "       <Value>muparser</Value>"
    2287             :     "   </Argument>"
    2288             :     "   <Argument type='builtin' value='source_names' />"
    2289             :     "</PixelFunctionArgumentsList>";
    2290             : 
    2291          60 : static CPLErr ExprPixelFunc(void **papoSources, int nSources, void *pData,
    2292             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    2293             :                             GDALDataType eBufType, int nPixelSpace,
    2294             :                             int nLineSpace, CSLConstList papszArgs)
    2295             : {
    2296             :     /* ---- Init ---- */
    2297             : 
    2298          60 :     if (GDALDataTypeIsComplex(eSrcType))
    2299             :     {
    2300           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2301             :                  "expression cannot by applied to complex data types");
    2302           0 :         return CE_Failure;
    2303             :     }
    2304             : 
    2305          60 :     std::unique_ptr<gdal::MathExpression> poExpression;
    2306             : 
    2307          60 :     const char *pszExpression = CSLFetchNameValue(papszArgs, "expression");
    2308             : 
    2309          60 :     const char *pszSourceNames = CSLFetchNameValue(papszArgs, "source_names");
    2310             :     const CPLStringList aosSourceNames(
    2311         120 :         CSLTokenizeString2(pszSourceNames, "|", 0));
    2312             : 
    2313         120 :     std::vector<double> adfValuesForPixel(nSources);
    2314             : 
    2315          60 :     const char *pszDialect = CSLFetchNameValue(papszArgs, "dialect");
    2316          60 :     if (!pszDialect)
    2317             :     {
    2318           0 :         pszDialect = "muparser";
    2319             :     }
    2320             : 
    2321          60 :     poExpression = gdal::MathExpression::Create(pszExpression, pszDialect);
    2322             : 
    2323             :     // cppcheck-suppress knownConditionTrueFalse
    2324          60 :     if (!poExpression)
    2325             :     {
    2326           0 :         return CE_Failure;
    2327             :     }
    2328             : 
    2329             :     {
    2330          60 :         int iSource = 0;
    2331         181 :         for (const auto &osName : aosSourceNames)
    2332             :         {
    2333         242 :             poExpression->RegisterVariable(osName,
    2334         121 :                                            &adfValuesForPixel[iSource++]);
    2335             :         }
    2336             :     }
    2337             : 
    2338          60 :     if (strstr(pszExpression, "BANDS"))
    2339             :     {
    2340           2 :         poExpression->RegisterVector("BANDS", &adfValuesForPixel);
    2341             :     }
    2342             : 
    2343             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    2344         120 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    2345          60 :     if (!padfResults)
    2346           0 :         return CE_Failure;
    2347             : 
    2348             :     /* ---- Set pixels ---- */
    2349          60 :     size_t ii = 0;
    2350         598 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2351             :     {
    2352       19278 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2353             :         {
    2354       67602 :             for (int iSrc = 0; iSrc < nSources; iSrc++)
    2355             :             {
    2356             :                 // cppcheck-suppress unreadVariable
    2357       48862 :                 adfValuesForPixel[iSrc] =
    2358       48862 :                     GetSrcVal(papoSources[iSrc], eSrcType, ii);
    2359             :             }
    2360             : 
    2361       18740 :             if (auto eErr = poExpression->Evaluate(); eErr != CE_None)
    2362             :             {
    2363          12 :                 return CE_Failure;
    2364             :             }
    2365             :             else
    2366             :             {
    2367       18728 :                 padfResults.get()[iCol] = poExpression->Results()[0];
    2368             :             }
    2369             :         }
    2370             : 
    2371         538 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    2372             :                       static_cast<GByte *>(pData) +
    2373         538 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    2374             :                       eBufType, nPixelSpace, nXSize);
    2375             :     }
    2376             : 
    2377             :     /* ---- Return success ---- */
    2378          48 :     return CE_None;
    2379             : }  // ExprPixelFunc
    2380             : 
    2381             : static const char pszReclassifyPixelFuncMetadata[] =
    2382             :     "<PixelFunctionArgumentsList>"
    2383             :     "   <Argument name='mapping' "
    2384             :     "             description='Lookup table for mapping, in format "
    2385             :     "from=to,from=to' "
    2386             :     "             type='string'></Argument>"
    2387             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2388             :     "</PixelFunctionArgumentsList>";
    2389             : 
    2390          33 : static CPLErr ReclassifyPixelFunc(void **papoSources, int nSources, void *pData,
    2391             :                                   int nXSize, int nYSize, GDALDataType eSrcType,
    2392             :                                   GDALDataType eBufType, int nPixelSpace,
    2393             :                                   int nLineSpace, CSLConstList papszArgs)
    2394             : {
    2395          33 :     if (GDALDataTypeIsComplex(eSrcType))
    2396             :     {
    2397           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2398             :                  "reclassify cannot by applied to complex data types");
    2399           0 :         return CE_Failure;
    2400             :     }
    2401             : 
    2402          33 :     if (nSources != 1)
    2403             :     {
    2404           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2405             :                  "reclassify only be applied to a single source at a time");
    2406           0 :         return CE_Failure;
    2407             :     }
    2408          33 :     std::optional<double> noDataValue{};
    2409             : 
    2410          33 :     const char *pszNoData = CSLFetchNameValue(papszArgs, "NoData");
    2411          33 :     if (pszNoData != nullptr)
    2412             :     {
    2413          10 :         noDataValue = CPLAtof(pszNoData);
    2414             :     }
    2415             : 
    2416          33 :     const char *pszMappings = CSLFetchNameValue(papszArgs, "mapping");
    2417          33 :     if (pszMappings == nullptr)
    2418             :     {
    2419           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2420             :                  "reclassify must be called with 'mapping' argument");
    2421           0 :         return CE_Failure;
    2422             :     }
    2423             : 
    2424          66 :     gdal::Reclassifier oReclassifier;
    2425          33 :     if (auto eErr = oReclassifier.Init(pszMappings, noDataValue, eBufType);
    2426             :         eErr != CE_None)
    2427             :     {
    2428          14 :         return eErr;
    2429             :     }
    2430             : 
    2431             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    2432          38 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    2433          19 :     if (!padfResults)
    2434           0 :         return CE_Failure;
    2435             : 
    2436          19 :     size_t ii = 0;
    2437          19 :     bool bSuccess = false;
    2438         435 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2439             :     {
    2440       20805 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2441             :         {
    2442       20389 :             double srcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2443       40778 :             padfResults.get()[iCol] =
    2444       20389 :                 oReclassifier.Reclassify(srcVal, bSuccess);
    2445       20389 :             if (!bSuccess)
    2446             :             {
    2447           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2448             :                          "Encountered value %g with no specified mapping",
    2449             :                          srcVal);
    2450           2 :                 return CE_Failure;
    2451             :             }
    2452             :         }
    2453             : 
    2454         416 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    2455             :                       static_cast<GByte *>(pData) +
    2456         416 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    2457             :                       eBufType, nPixelSpace, nXSize);
    2458             :     }
    2459             : 
    2460          17 :     return CE_None;
    2461             : }  // ReclassifyPixelFunc
    2462             : 
    2463             : struct MeanKernel
    2464             : {
    2465             :     static constexpr const char *pszName = "mean";
    2466             : 
    2467             :     double dfSum = 0;
    2468             :     int nValidSources = 0;
    2469             : 
    2470           6 :     void Reset()
    2471             :     {
    2472           6 :         dfSum = 0;
    2473           6 :         nValidSources = 0;
    2474           6 :     }
    2475             : 
    2476           3 :     static CPLErr ProcessArguments(CSLConstList)
    2477             :     {
    2478           3 :         return CE_None;
    2479             :     }
    2480             : 
    2481           3 :     void ProcessPixel(double dfVal)
    2482             :     {
    2483           3 :         dfSum += dfVal;
    2484           3 :         nValidSources++;
    2485           3 :     }
    2486             : 
    2487           4 :     bool HasValue() const
    2488             :     {
    2489           4 :         return nValidSources > 0;
    2490             :     }
    2491             : 
    2492           1 :     double GetValue() const
    2493             :     {
    2494           1 :         return dfSum / nValidSources;
    2495             :     }
    2496             : };
    2497             : 
    2498             : struct GeoMeanKernel
    2499             : {
    2500             :     static constexpr const char *pszName = "geometric_mean";
    2501             : 
    2502             :     double dfProduct = 1;
    2503             :     int nValidSources = 0;
    2504             : 
    2505           6 :     void Reset()
    2506             :     {
    2507           6 :         dfProduct = 1;
    2508           6 :         nValidSources = 0;
    2509           6 :     }
    2510             : 
    2511           3 :     static CPLErr ProcessArguments(CSLConstList)
    2512             :     {
    2513           3 :         return CE_None;
    2514             :     }
    2515             : 
    2516           3 :     void ProcessPixel(double dfVal)
    2517             :     {
    2518           3 :         dfProduct *= dfVal;
    2519           3 :         nValidSources++;
    2520           3 :     }
    2521             : 
    2522           4 :     bool HasValue() const
    2523             :     {
    2524           4 :         return nValidSources > 0;
    2525             :     }
    2526             : 
    2527           1 :     double GetValue() const
    2528             :     {
    2529           1 :         return std::pow(dfProduct, 1.0 / nValidSources);
    2530             :     }
    2531             : };
    2532             : 
    2533             : struct HarmonicMeanKernel
    2534             : {
    2535             :     static constexpr const char *pszName = "harmonic_mean";
    2536             : 
    2537             :     double dfDenom = 0;
    2538             :     int nValidSources = 0;
    2539             :     bool bValueIsZero = false;
    2540             :     bool bPropagateZero = false;
    2541             : 
    2542          10 :     void Reset()
    2543             :     {
    2544          10 :         dfDenom = 0;
    2545          10 :         nValidSources = 0;
    2546          10 :         bValueIsZero = false;
    2547          10 :     }
    2548             : 
    2549           7 :     void ProcessPixel(double dfVal)
    2550             :     {
    2551           7 :         if (dfVal == 0)
    2552             :         {
    2553           2 :             bValueIsZero = true;
    2554             :         }
    2555             :         else
    2556             :         {
    2557           5 :             dfDenom += 1 / dfVal;
    2558             :         }
    2559           7 :         nValidSources++;
    2560           7 :     }
    2561             : 
    2562           5 :     CPLErr ProcessArguments(CSLConstList papszArgs)
    2563             :     {
    2564           5 :         bPropagateZero =
    2565           5 :             CPLTestBool(CSLFetchNameValueDef(papszArgs, "propagateZero", "0"));
    2566           5 :         return CE_None;
    2567             :     }
    2568             : 
    2569           8 :     bool HasValue() const
    2570             :     {
    2571           8 :         return dfDenom > 0 && (bPropagateZero || !bValueIsZero);
    2572             :     }
    2573             : 
    2574           2 :     double GetValue() const
    2575             :     {
    2576           2 :         if (bPropagateZero && bValueIsZero)
    2577             :         {
    2578           1 :             return 0;
    2579             :         }
    2580           1 :         return static_cast<double>(nValidSources) / dfDenom;
    2581             :     }
    2582             : };
    2583             : 
    2584             : struct MedianKernel
    2585             : {
    2586             :     static constexpr const char *pszName = "median";
    2587             : 
    2588             :     mutable std::vector<double> values{};
    2589             : 
    2590           8 :     void Reset()
    2591             :     {
    2592           8 :         values.clear();
    2593           8 :     }
    2594             : 
    2595           4 :     static CPLErr ProcessArguments(CSLConstList)
    2596             :     {
    2597           4 :         return CE_None;
    2598             :     }
    2599             : 
    2600           6 :     void ProcessPixel(double dfVal)
    2601             :     {
    2602           6 :         if (!std::isnan(dfVal))
    2603             :         {
    2604           6 :             values.push_back(dfVal);
    2605             :         }
    2606           6 :     }
    2607             : 
    2608           6 :     bool HasValue() const
    2609             :     {
    2610           6 :         return !values.empty();
    2611             :     }
    2612             : 
    2613           2 :     double GetValue() const
    2614             :     {
    2615           2 :         std::sort(values.begin(), values.end());
    2616           2 :         if (values.size() % 2 == 0)
    2617             :         {
    2618             :             return 0.5 *
    2619           1 :                    (values[values.size() / 2 - 1] + values[values.size() / 2]);
    2620             :         }
    2621             : 
    2622           1 :         return values[values.size() / 2];
    2623             :     }
    2624             : };
    2625             : 
    2626             : struct ModeKernel
    2627             : {
    2628             :     static constexpr const char *pszName = "mode";
    2629             : 
    2630             :     std::map<double, size_t> counts{};
    2631             :     std::size_t nanCount{0};
    2632             :     double dfMax = std::numeric_limits<double>::quiet_NaN();
    2633             :     decltype(counts.begin()) oMax = counts.end();
    2634             : 
    2635           6 :     void Reset()
    2636             :     {
    2637           6 :         nanCount = 0;
    2638           6 :         counts.clear();
    2639           6 :         oMax = counts.end();
    2640           6 :     }
    2641             : 
    2642           3 :     static CPLErr ProcessArguments(CSLConstList)
    2643             :     {
    2644           3 :         return CE_None;
    2645             :     }
    2646             : 
    2647           8 :     void ProcessPixel(double dfVal)
    2648             :     {
    2649           8 :         if (std::isnan(dfVal))
    2650             :         {
    2651           2 :             nanCount += 1;
    2652           2 :             return;
    2653             :         }
    2654             : 
    2655             :         // if dfVal is NaN, try_emplace will return an entry for a different key!
    2656           6 :         auto [it, inserted] = counts.try_emplace(dfVal, 0);
    2657             : 
    2658           6 :         it->second += 1;
    2659             : 
    2660           6 :         if (oMax == counts.end() || it->second > oMax->second)
    2661             :         {
    2662           4 :             oMax = it;
    2663             :         }
    2664             :     }
    2665             : 
    2666           4 :     bool HasValue() const
    2667             :     {
    2668           4 :         return nanCount > 0 || oMax != counts.end();
    2669             :     }
    2670             : 
    2671           2 :     double GetValue() const
    2672             :     {
    2673           2 :         double ret = std::numeric_limits<double>::quiet_NaN();
    2674           2 :         if (oMax != counts.end())
    2675             :         {
    2676           2 :             const size_t nCount = oMax->second;
    2677           2 :             if (nCount > nanCount)
    2678           1 :                 ret = oMax->first;
    2679             :         }
    2680           2 :         return ret;
    2681             :     }
    2682             : };
    2683             : 
    2684             : static const char pszBasicPixelFuncMetadata[] =
    2685             :     "<PixelFunctionArgumentsList>"
    2686             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2687             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2688             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2689             :     "default='false' />"
    2690             :     "</PixelFunctionArgumentsList>";
    2691             : 
    2692             : template <typename T>
    2693          18 : static CPLErr BasicPixelFunc(void **papoSources, int nSources, void *pData,
    2694             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    2695             :                              GDALDataType eBufType, int nPixelSpace,
    2696             :                              int nLineSpace, CSLConstList papszArgs)
    2697             : {
    2698             :     /* ---- Init ---- */
    2699          25 :     T oKernel;
    2700             : 
    2701          18 :     if (GDALDataTypeIsComplex(eSrcType))
    2702             :     {
    2703           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2704             :                  "Complex data types not supported by %s", oKernel.pszName);
    2705           0 :         return CE_Failure;
    2706             :     }
    2707             : 
    2708          18 :     double dfNoData{0};
    2709          18 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2710          18 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2711           0 :         return CE_Failure;
    2712             : 
    2713          18 :     const bool bPropagateNoData = CPLTestBool(
    2714             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2715             : 
    2716          18 :     if (oKernel.ProcessArguments(papszArgs) == CE_Failure)
    2717             :     {
    2718           0 :         return CE_Failure;
    2719             :     }
    2720             : 
    2721             :     /* ---- Set pixels ---- */
    2722          18 :     size_t ii = 0;
    2723          36 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2724             :     {
    2725          54 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2726             :         {
    2727          36 :             oKernel.Reset();
    2728          36 :             bool bWriteNoData = false;
    2729             : 
    2730         127 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    2731             :             {
    2732         101 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    2733             : 
    2734         101 :                 if (bHasNoData && IsNoData(dfVal, dfNoData))
    2735             :                 {
    2736          74 :                     if (bPropagateNoData)
    2737             :                     {
    2738          10 :                         bWriteNoData = true;
    2739          10 :                         break;
    2740             :                     }
    2741             :                 }
    2742             :                 else
    2743             :                 {
    2744          27 :                     oKernel.ProcessPixel(dfVal);
    2745             :                 }
    2746             :             }
    2747             : 
    2748          36 :             double dfPixVal{dfNoData};
    2749          36 :             if (!bWriteNoData && oKernel.HasValue())
    2750             :             {
    2751           8 :                 dfPixVal = oKernel.GetValue();
    2752             :             }
    2753             : 
    2754          36 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2755             :                           static_cast<GByte *>(pData) +
    2756          36 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2757          36 :                               iCol * nPixelSpace,
    2758             :                           eBufType, nPixelSpace, 1);
    2759             :         }
    2760             :     }
    2761             : 
    2762             :     /* ---- Return success ---- */
    2763          18 :     return CE_None;
    2764             : }  // BasicPixelFunc
    2765             : 
    2766             : /************************************************************************/
    2767             : /*                     GDALRegisterDefaultPixelFunc()                   */
    2768             : /************************************************************************/
    2769             : 
    2770             : /**
    2771             :  * This adds a default set of pixel functions to the global list of
    2772             :  * available pixel functions for derived bands:
    2773             :  *
    2774             :  * - "real": extract real part from a single raster band (just a copy if the
    2775             :  *           input is non-complex)
    2776             :  * - "imag": extract imaginary part from a single raster band (0 for
    2777             :  *           non-complex)
    2778             :  * - "complex": make a complex band merging two bands used as real and
    2779             :  *              imag values
    2780             :  * - "polar": make a complex band using input bands for amplitude and
    2781             :  *            phase values (b1 * exp( j * b2 ))
    2782             :  * - "mod": extract module from a single raster band (real or complex)
    2783             :  * - "phase": extract phase from a single raster band [-PI,PI] (0 or PI for
    2784             :               non-complex)
    2785             :  * - "conj": computes the complex conjugate of a single raster band (just a
    2786             :  *           copy if the input is non-complex)
    2787             :  * - "sum": sum 2 or more raster bands
    2788             :  * - "diff": computes the difference between 2 raster bands (b1 - b2)
    2789             :  * - "mul": multiply 2 or more raster bands
    2790             :  * - "div": divide one raster band by another (b1 / b2).
    2791             :  * - "min": minimum value of 2 or more raster bands
    2792             :  * - "max": maximum value of 2 or more raster bands
    2793             :  * - "norm_diff": computes the normalized difference between two raster bands:
    2794             :  *                ``(b1 - b2)/(b1 + b2)``.
    2795             :  * - "cmul": multiply the first band for the complex conjugate of the second
    2796             :  * - "inv": inverse (1./x).
    2797             :  * - "intensity": computes the intensity Re(x*conj(x)) of a single raster band
    2798             :  *                (real or complex)
    2799             :  * - "sqrt": perform the square root of a single raster band (real only)
    2800             :  * - "log10": compute the logarithm (base 10) of the abs of a single raster
    2801             :  *            band (real or complex): log10( abs( x ) )
    2802             :  * - "dB": perform conversion to dB of the abs of a single raster
    2803             :  *         band (real or complex): 20. * log10( abs( x ) ).
    2804             :  *         Note: the optional fact parameter can be set to 10. to get the
    2805             :  *         alternative formula: 10. * log10( abs( x ) )
    2806             :  * - "exp": computes the exponential of each element in the input band ``x``
    2807             :  *          (of real values): ``e ^ x``.
    2808             :  *          The function also accepts two optional parameters: ``base`` and
    2809             :  ``fact``
    2810             :  *          that allow to compute the generalized formula: ``base ^ ( fact *
    2811             :  x)``.
    2812             :  *          Note: this function is the recommended one to perform conversion
    2813             :  *          form logarithmic scale (dB): `` 10. ^ (x / 20.)``, in this case
    2814             :  *          ``base = 10.`` and ``fact = 1./20``
    2815             :  * - "dB2amp": perform scale conversion from logarithmic to linear
    2816             :  *             (amplitude) (i.e. 10 ^ ( x / 20 ) ) of a single raster
    2817             :  *             band (real only).
    2818             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    2819             :  with
    2820             :  *             ``base = 10.`` and ``fact = 0.05`` i.e. ``1./20``
    2821             :  * - "dB2pow": perform scale conversion from logarithmic to linear
    2822             :  *             (power) (i.e. 10 ^ ( x / 10 ) ) of a single raster
    2823             :  *             band (real only)
    2824             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    2825             :  with
    2826             :  *             ``base = 10.`` and ``fact = 0.1`` i.e. ``1./10``
    2827             :  * - "pow": raise a single raster band to a constant power
    2828             :  * - "interpolate_linear": interpolate values between two raster bands
    2829             :  *                         using linear interpolation
    2830             :  * - "interpolate_exp": interpolate values between two raster bands using
    2831             :  *                      exponential interpolation
    2832             :  * - "scale": Apply the RasterBand metadata values of "offset" and "scale"
    2833             :  * - "reclassify": Reclassify values matching ranges in a table
    2834             :  * - "nan": Convert incoming NoData values to IEEE 754 nan
    2835             :  *
    2836             :  * @see GDALAddDerivedBandPixelFunc
    2837             :  *
    2838             :  * @return CE_None
    2839             :  */
    2840        1407 : CPLErr GDALRegisterDefaultPixelFunc()
    2841             : {
    2842        1407 :     GDALAddDerivedBandPixelFunc("real", RealPixelFunc);
    2843        1407 :     GDALAddDerivedBandPixelFunc("imag", ImagPixelFunc);
    2844        1407 :     GDALAddDerivedBandPixelFunc("complex", ComplexPixelFunc);
    2845        1407 :     GDALAddDerivedBandPixelFuncWithArgs("polar", PolarPixelFunc,
    2846             :                                         pszPolarPixelFuncMetadata);
    2847        1407 :     GDALAddDerivedBandPixelFunc("mod", ModulePixelFunc);
    2848        1407 :     GDALAddDerivedBandPixelFunc("phase", PhasePixelFunc);
    2849        1407 :     GDALAddDerivedBandPixelFunc("conj", ConjPixelFunc);
    2850        1407 :     GDALAddDerivedBandPixelFuncWithArgs("sum", SumPixelFunc,
    2851             :                                         pszSumPixelFuncMetadata);
    2852        1407 :     GDALAddDerivedBandPixelFuncWithArgs("diff", DiffPixelFunc,
    2853             :                                         pszDiffPixelFuncMetadata);
    2854        1407 :     GDALAddDerivedBandPixelFuncWithArgs("mul", MulPixelFunc,
    2855             :                                         pszMulPixelFuncMetadata);
    2856        1407 :     GDALAddDerivedBandPixelFuncWithArgs("div", DivPixelFunc,
    2857             :                                         pszDivPixelFuncMetadata);
    2858        1407 :     GDALAddDerivedBandPixelFunc("cmul", CMulPixelFunc);
    2859        1407 :     GDALAddDerivedBandPixelFuncWithArgs("inv", InvPixelFunc,
    2860             :                                         pszInvPixelFuncMetadata);
    2861        1407 :     GDALAddDerivedBandPixelFunc("intensity", IntensityPixelFunc);
    2862        1407 :     GDALAddDerivedBandPixelFuncWithArgs("sqrt", SqrtPixelFunc,
    2863             :                                         pszSqrtPixelFuncMetadata);
    2864        1407 :     GDALAddDerivedBandPixelFuncWithArgs("log10", Log10PixelFunc,
    2865             :                                         pszLog10PixelFuncMetadata);
    2866        1407 :     GDALAddDerivedBandPixelFuncWithArgs("dB", DBPixelFunc,
    2867             :                                         pszDBPixelFuncMetadata);
    2868        1407 :     GDALAddDerivedBandPixelFuncWithArgs("exp", ExpPixelFunc,
    2869             :                                         pszExpPixelFuncMetadata);
    2870        1407 :     GDALAddDerivedBandPixelFunc("dB2amp",
    2871             :                                 dB2AmpPixelFunc);  // deprecated in v3.5
    2872        1407 :     GDALAddDerivedBandPixelFunc("dB2pow",
    2873             :                                 dB2PowPixelFunc);  // deprecated in v3.5
    2874        1407 :     GDALAddDerivedBandPixelFuncWithArgs("pow", PowPixelFunc,
    2875             :                                         pszPowPixelFuncMetadata);
    2876        1407 :     GDALAddDerivedBandPixelFuncWithArgs("interpolate_linear",
    2877             :                                         InterpolatePixelFunc<InterpolateLinear>,
    2878             :                                         pszInterpolatePixelFuncMetadata);
    2879        1407 :     GDALAddDerivedBandPixelFuncWithArgs(
    2880             :         "interpolate_exp", InterpolatePixelFunc<InterpolateExponential>,
    2881             :         pszInterpolatePixelFuncMetadata);
    2882        1407 :     GDALAddDerivedBandPixelFuncWithArgs("replace_nodata",
    2883             :                                         ReplaceNoDataPixelFunc,
    2884             :                                         pszReplaceNoDataPixelFuncMetadata);
    2885        1407 :     GDALAddDerivedBandPixelFuncWithArgs("scale", ScalePixelFunc,
    2886             :                                         pszScalePixelFuncMetadata);
    2887        1407 :     GDALAddDerivedBandPixelFuncWithArgs("norm_diff", NormDiffPixelFunc,
    2888             :                                         pszNormDiffPixelFuncMetadata);
    2889        1407 :     GDALAddDerivedBandPixelFuncWithArgs("min", MinPixelFunc,
    2890             :                                         pszMinMaxFuncMetadataNodata);
    2891        1407 :     GDALAddDerivedBandPixelFuncWithArgs("max", MaxPixelFunc,
    2892             :                                         pszMinMaxFuncMetadataNodata);
    2893        1407 :     GDALAddDerivedBandPixelFuncWithArgs("expression", ExprPixelFunc,
    2894             :                                         pszExprPixelFuncMetadata);
    2895        1407 :     GDALAddDerivedBandPixelFuncWithArgs("reclassify", ReclassifyPixelFunc,
    2896             :                                         pszReclassifyPixelFuncMetadata);
    2897        1407 :     GDALAddDerivedBandPixelFuncWithArgs("mean", BasicPixelFunc<MeanKernel>,
    2898             :                                         pszBasicPixelFuncMetadata);
    2899        1407 :     GDALAddDerivedBandPixelFuncWithArgs("geometric_mean",
    2900             :                                         BasicPixelFunc<GeoMeanKernel>,
    2901             :                                         pszBasicPixelFuncMetadata);
    2902        1407 :     GDALAddDerivedBandPixelFuncWithArgs("harmonic_mean",
    2903             :                                         BasicPixelFunc<HarmonicMeanKernel>,
    2904             :                                         pszBasicPixelFuncMetadata);
    2905        1407 :     GDALAddDerivedBandPixelFuncWithArgs("median", BasicPixelFunc<MedianKernel>,
    2906             :                                         pszBasicPixelFuncMetadata);
    2907        1407 :     GDALAddDerivedBandPixelFuncWithArgs("mode", BasicPixelFunc<ModeKernel>,
    2908             :                                         pszBasicPixelFuncMetadata);
    2909        1407 :     return CE_None;
    2910             : }

Generated by: LCOV version 1.14