LCOV - code coverage report
Current view: top level - frmts/vrt - pixelfunctions.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1762 1875 94.0 %
Date: 2026-02-12 06:20:29 Functions: 221 222 99.5 %

          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 <array>
      15             : #include <charconv>
      16             : #include <cmath>
      17             : #include "gdal.h"
      18             : #include "vrtdataset.h"
      19             : #include "vrtexpression.h"
      20             : #include "vrtreclassifier.h"
      21             : #include "cpl_float.h"
      22             : 
      23             : #include "geodesic.h"  // from PROJ
      24             : 
      25             : #if defined(__x86_64) || defined(_M_X64) || defined(USE_NEON_OPTIMIZATIONS)
      26             : #define USE_SSE2
      27             : #include "gdalsse_priv.h"
      28             : 
      29             : #if !defined(USE_NEON_OPTIMIZATIONS)
      30             : #define LIBDIVIDE_SSE2
      31             : #ifdef __GNUC__
      32             : #pragma GCC diagnostic push
      33             : #pragma GCC diagnostic ignored "-Wold-style-cast"
      34             : #pragma GCC diagnostic ignored "-Weffc++"
      35             : #endif
      36             : #include "../../third_party/libdivide/libdivide.h"
      37             : #ifdef __GNUC__
      38             : #pragma GCC diagnostic pop
      39             : #endif
      40             : #endif
      41             : 
      42             : #endif
      43             : 
      44             : #include "gdal_priv_templates.hpp"
      45             : 
      46             : #include <algorithm>
      47             : #include <cassert>
      48             : #include <limits>
      49             : 
      50             : namespace gdal
      51             : {
      52             : MathExpression::~MathExpression() = default;
      53             : }
      54             : 
      55             : template <typename T>
      56    26234300 : inline double GetSrcVal(const void *pSource, GDALDataType eSrcType, T ii)
      57             : {
      58    26234300 :     switch (eSrcType)
      59             :     {
      60           0 :         case GDT_Unknown:
      61           0 :             return 0;
      62       39932 :         case GDT_UInt8:
      63       39932 :             return static_cast<const GByte *>(pSource)[ii];
      64           0 :         case GDT_Int8:
      65           0 :             return static_cast<const GInt8 *>(pSource)[ii];
      66        4295 :         case GDT_UInt16:
      67        4295 :             return static_cast<const GUInt16 *>(pSource)[ii];
      68        4299 :         case GDT_Int16:
      69        4299 :             return static_cast<const GInt16 *>(pSource)[ii];
      70       31356 :         case GDT_UInt32:
      71       31356 :             return static_cast<const GUInt32 *>(pSource)[ii];
      72        4738 :         case GDT_Int32:
      73        4738 :             return static_cast<const GInt32 *>(pSource)[ii];
      74             :         // Precision loss currently for int64/uint64
      75         804 :         case GDT_UInt64:
      76             :             return static_cast<double>(
      77         804 :                 static_cast<const uint64_t *>(pSource)[ii]);
      78        2680 :         case GDT_Int64:
      79             :             return static_cast<double>(
      80        2680 :                 static_cast<const int64_t *>(pSource)[ii]);
      81           0 :         case GDT_Float16:
      82           0 :             return static_cast<const GFloat16 *>(pSource)[ii];
      83        9827 :         case GDT_Float32:
      84        9827 :             return static_cast<const float *>(pSource)[ii];
      85    26079300 :         case GDT_Float64:
      86    26079300 :             return static_cast<const double *>(pSource)[ii];
      87        1432 :         case GDT_CInt16:
      88        1432 :             return static_cast<const GInt16 *>(pSource)[2 * ii];
      89        3216 :         case GDT_CInt32:
      90        3216 :             return static_cast<const GInt32 *>(pSource)[2 * ii];
      91           0 :         case GDT_CFloat16:
      92           0 :             return static_cast<const GFloat16 *>(pSource)[2 * ii];
      93        9064 :         case GDT_CFloat32:
      94        9064 :             return static_cast<const float *>(pSource)[2 * ii];
      95       43318 :         case GDT_CFloat64:
      96       43318 :             return static_cast<const double *>(pSource)[2 * ii];
      97           0 :         case GDT_TypeCount:
      98           0 :             break;
      99             :     }
     100           0 :     return 0;
     101             : }
     102             : 
     103       10324 : static bool IsNoData(double dfVal, double dfNoData)
     104             : {
     105       10324 :     return dfVal == dfNoData || (std::isnan(dfVal) && std::isnan(dfNoData));
     106             : }
     107             : 
     108        2343 : static CPLErr FetchDoubleArg(CSLConstList papszArgs, const char *pszName,
     109             :                              double *pdfX, double *pdfDefault = nullptr)
     110             : {
     111        2343 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
     112             : 
     113        2343 :     if (pszVal == nullptr)
     114             :     {
     115        1140 :         if (pdfDefault == nullptr)
     116             :         {
     117           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     118             :                      "Missing pixel function argument: %s", pszName);
     119           0 :             return CE_Failure;
     120             :         }
     121             :         else
     122             :         {
     123        1140 :             *pdfX = *pdfDefault;
     124        1140 :             return CE_None;
     125             :         }
     126             :     }
     127             : 
     128        1203 :     char *pszEnd = nullptr;
     129        1203 :     *pdfX = std::strtod(pszVal, &pszEnd);
     130        1203 :     if (pszEnd == pszVal)
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     133             :                  "Failed to parse pixel function argument: %s", pszName);
     134           0 :         return CE_Failure;
     135             :     }
     136             : 
     137        1203 :     return CE_None;
     138             : }
     139             : 
     140           5 : static CPLErr FetchIntegerArg(CSLConstList papszArgs, const char *pszName,
     141             :                               int *pnX, int *pnDefault = nullptr)
     142             : {
     143           5 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
     144             : 
     145           5 :     if (pszVal == nullptr)
     146             :     {
     147           2 :         if (pnDefault == nullptr)
     148             :         {
     149           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     150             :                      "Missing pixel function argument: %s", pszName);
     151           0 :             return CE_Failure;
     152             :         }
     153             :         else
     154             :         {
     155           2 :             *pnX = *pnDefault;
     156           2 :             return CE_None;
     157             :         }
     158             :     }
     159             : 
     160           3 :     char *pszEnd = nullptr;
     161           3 :     const auto ret = std::strtol(pszVal, &pszEnd, 10);
     162           3 :     while (std::isspace(*pszEnd))
     163             :     {
     164           0 :         pszEnd++;
     165             :     }
     166           3 :     if (*pszEnd != '\0')
     167             :     {
     168           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     169             :                  "Failed to parse pixel function argument: %s", pszName);
     170           1 :         return CE_Failure;
     171             :     }
     172             : 
     173           2 :     if (ret > std::numeric_limits<int>::max())
     174             :     {
     175           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     176             :                  "Pixel function argument %s is above the maximum value of %d",
     177             :                  pszName, std::numeric_limits<int>::max());
     178           0 :         return CE_Failure;
     179             :     }
     180             : 
     181           2 :     *pnX = static_cast<int>(ret);
     182             : 
     183           2 :     return CE_None;
     184             : }
     185             : 
     186           7 : static CPLErr RealPixelFunc(void **papoSources, int nSources, void *pData,
     187             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     188             :                             GDALDataType eBufType, int nPixelSpace,
     189             :                             int nLineSpace)
     190             : {
     191             :     /* ---- Init ---- */
     192           7 :     if (nSources != 1)
     193           1 :         return CE_Failure;
     194             : 
     195           6 :     const int nPixelSpaceSrc = GDALGetDataTypeSizeBytes(eSrcType);
     196           6 :     const size_t nLineSpaceSrc = static_cast<size_t>(nPixelSpaceSrc) * nXSize;
     197             : 
     198             :     /* ---- Set pixels ---- */
     199          98 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     200             :     {
     201          92 :         GDALCopyWords(static_cast<GByte *>(papoSources[0]) +
     202          92 :                           nLineSpaceSrc * iLine,
     203             :                       eSrcType, nPixelSpaceSrc,
     204             :                       static_cast<GByte *>(pData) +
     205          92 :                           static_cast<GSpacing>(nLineSpace) * iLine,
     206             :                       eBufType, nPixelSpace, nXSize);
     207             :     }
     208             : 
     209             :     /* ---- Return success ---- */
     210           6 :     return CE_None;
     211             : }  // RealPixelFunc
     212             : 
     213           8 : static CPLErr ImagPixelFunc(void **papoSources, int nSources, void *pData,
     214             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     215             :                             GDALDataType eBufType, int nPixelSpace,
     216             :                             int nLineSpace)
     217             : {
     218             :     /* ---- Init ---- */
     219           8 :     if (nSources != 1)
     220           1 :         return CE_Failure;
     221             : 
     222           7 :     if (GDALDataTypeIsComplex(eSrcType))
     223             :     {
     224           6 :         const GDALDataType eSrcBaseType = GDALGetNonComplexDataType(eSrcType);
     225           6 :         const int nPixelSpaceSrc = GDALGetDataTypeSizeBytes(eSrcType);
     226           6 :         const size_t nLineSpaceSrc =
     227           6 :             static_cast<size_t>(nPixelSpaceSrc) * nXSize;
     228             : 
     229           6 :         const void *const pImag = static_cast<GByte *>(papoSources[0]) +
     230           6 :                                   GDALGetDataTypeSizeBytes(eSrcType) / 2;
     231             : 
     232             :         /* ---- Set pixels ---- */
     233          56 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     234             :         {
     235          50 :             GDALCopyWords(static_cast<const GByte *>(pImag) +
     236          50 :                               nLineSpaceSrc * iLine,
     237             :                           eSrcBaseType, nPixelSpaceSrc,
     238             :                           static_cast<GByte *>(pData) +
     239          50 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     240             :                           eBufType, nPixelSpace, nXSize);
     241             :         }
     242             :     }
     243             :     else
     244             :     {
     245           1 :         const double dfImag = 0;
     246             : 
     247             :         /* ---- Set pixels ---- */
     248          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     249             :         {
     250             :             // Always copy from the same location.
     251          20 :             GDALCopyWords(&dfImag, eSrcType, 0,
     252             :                           static_cast<GByte *>(pData) +
     253          20 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     254             :                           eBufType, nPixelSpace, nXSize);
     255             :         }
     256             :     }
     257             : 
     258             :     /* ---- Return success ---- */
     259           7 :     return CE_None;
     260             : }  // ImagPixelFunc
     261             : 
     262           6 : static CPLErr ComplexPixelFunc(void **papoSources, int nSources, void *pData,
     263             :                                int nXSize, int nYSize, GDALDataType eSrcType,
     264             :                                GDALDataType eBufType, int nPixelSpace,
     265             :                                int nLineSpace)
     266             : {
     267             :     /* ---- Init ---- */
     268           6 :     if (nSources != 2)
     269           1 :         return CE_Failure;
     270             : 
     271           5 :     const void *const pReal = papoSources[0];
     272           5 :     const void *const pImag = papoSources[1];
     273             : 
     274             :     /* ---- Set pixels ---- */
     275           5 :     size_t ii = 0;
     276         281 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     277             :     {
     278       17060 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     279             :         {
     280             :             // Source raster pixels may be obtained with GetSrcVal macro.
     281             :             const double adfPixVal[2] = {
     282       16784 :                 GetSrcVal(pReal, eSrcType, ii),  // re
     283       33568 :                 GetSrcVal(pImag, eSrcType, ii)   // im
     284       16784 :             };
     285             : 
     286       16784 :             GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     287             :                           static_cast<GByte *>(pData) +
     288       16784 :                               static_cast<GSpacing>(nLineSpace) * iLine +
     289       16784 :                               iCol * nPixelSpace,
     290             :                           eBufType, nPixelSpace, 1);
     291             :         }
     292             :     }
     293             : 
     294             :     /* ---- Return success ---- */
     295           5 :     return CE_None;
     296             : }  // ComplexPixelFunc
     297             : 
     298             : typedef enum
     299             : {
     300             :     GAT_amplitude,
     301             :     GAT_intensity,
     302             :     GAT_dB
     303             : } PolarAmplitudeType;
     304             : 
     305             : static const char pszPolarPixelFuncMetadata[] =
     306             :     "<PixelFunctionArgumentsList>"
     307             :     "   <Argument name='amplitude_type' description='Amplitude Type' "
     308             :     "type='string-select' default='AMPLITUDE'>"
     309             :     "       <Value>INTENSITY</Value>"
     310             :     "       <Value>dB</Value>"
     311             :     "       <Value>AMPLITUDE</Value>"
     312             :     "   </Argument>"
     313             :     "</PixelFunctionArgumentsList>";
     314             : 
     315           4 : static CPLErr PolarPixelFunc(void **papoSources, int nSources, void *pData,
     316             :                              int nXSize, int nYSize, GDALDataType eSrcType,
     317             :                              GDALDataType eBufType, int nPixelSpace,
     318             :                              int nLineSpace, CSLConstList papszArgs)
     319             : {
     320             :     /* ---- Init ---- */
     321           4 :     if (nSources != 2)
     322           0 :         return CE_Failure;
     323             : 
     324           4 :     const char pszName[] = "amplitude_type";
     325           4 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
     326           4 :     PolarAmplitudeType amplitudeType = GAT_amplitude;
     327           4 :     if (pszVal != nullptr)
     328             :     {
     329           3 :         if (strcmp(pszVal, "INTENSITY") == 0)
     330           1 :             amplitudeType = GAT_intensity;
     331           2 :         else if (strcmp(pszVal, "dB") == 0)
     332           1 :             amplitudeType = GAT_dB;
     333           1 :         else if (strcmp(pszVal, "AMPLITUDE") != 0)
     334             :         {
     335           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     336             :                      "Invalid value for pixel function argument '%s': %s",
     337             :                      pszName, pszVal);
     338           0 :             return CE_Failure;
     339             :         }
     340             :     }
     341             : 
     342           4 :     const void *const pAmp = papoSources[0];
     343           4 :     const void *const pPhase = papoSources[1];
     344             : 
     345             :     /* ---- Set pixels ---- */
     346           4 :     size_t ii = 0;
     347          84 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     348             :     {
     349        1680 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     350             :         {
     351             :             // Source raster pixels may be obtained with GetSrcVal macro.
     352        1600 :             double dfAmp = GetSrcVal(pAmp, eSrcType, ii);
     353        1600 :             switch (amplitudeType)
     354             :             {
     355         400 :                 case GAT_intensity:
     356             :                     // clip to zero
     357         400 :                     dfAmp = dfAmp <= 0 ? 0 : std::sqrt(dfAmp);
     358         400 :                     break;
     359         400 :                 case GAT_dB:
     360         400 :                     dfAmp = dfAmp <= 0
     361         400 :                                 ? -std::numeric_limits<double>::infinity()
     362         400 :                                 : pow(10, dfAmp / 20.);
     363         400 :                     break;
     364         800 :                 case GAT_amplitude:
     365         800 :                     break;
     366             :             }
     367        1600 :             const double dfPhase = GetSrcVal(pPhase, eSrcType, ii);
     368             :             const double adfPixVal[2] = {
     369        1600 :                 dfAmp * std::cos(dfPhase),  // re
     370        1600 :                 dfAmp * std::sin(dfPhase)   // im
     371        1600 :             };
     372             : 
     373        1600 :             GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     374             :                           static_cast<GByte *>(pData) +
     375        1600 :                               static_cast<GSpacing>(nLineSpace) * iLine +
     376        1600 :                               iCol * nPixelSpace,
     377             :                           eBufType, nPixelSpace, 1);
     378             :         }
     379             :     }
     380             : 
     381             :     /* ---- Return success ---- */
     382           4 :     return CE_None;
     383             : }  // PolarPixelFunc
     384             : 
     385          11 : static CPLErr ModulePixelFunc(void **papoSources, int nSources, void *pData,
     386             :                               int nXSize, int nYSize, GDALDataType eSrcType,
     387             :                               GDALDataType eBufType, int nPixelSpace,
     388             :                               int nLineSpace)
     389             : {
     390             :     /* ---- Init ---- */
     391          11 :     if (nSources != 1)
     392           1 :         return CE_Failure;
     393             : 
     394          10 :     if (GDALDataTypeIsComplex(eSrcType))
     395             :     {
     396           2 :         const void *pReal = papoSources[0];
     397           2 :         const void *pImag = static_cast<GByte *>(papoSources[0]) +
     398           2 :                             GDALGetDataTypeSizeBytes(eSrcType) / 2;
     399             : 
     400             :         /* ---- Set pixels ---- */
     401           2 :         size_t ii = 0;
     402          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     403             :         {
     404          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     405             :             {
     406             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     407          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     408          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
     409             : 
     410          60 :                 const double dfPixVal = sqrt(dfReal * dfReal + dfImag * dfImag);
     411             : 
     412          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     413             :                               static_cast<GByte *>(pData) +
     414          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     415          60 :                                   iCol * nPixelSpace,
     416             :                               eBufType, nPixelSpace, 1);
     417             :             }
     418             :         }
     419             :     }
     420             :     else
     421             :     {
     422             :         /* ---- Set pixels ---- */
     423           8 :         size_t ii = 0;
     424          35 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     425             :         {
     426         434 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     427             :             {
     428             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     429             :                 const double dfPixVal =
     430         407 :                     fabs(GetSrcVal(papoSources[0], eSrcType, ii));
     431             : 
     432         407 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     433             :                               static_cast<GByte *>(pData) +
     434         407 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     435         407 :                                   iCol * nPixelSpace,
     436             :                               eBufType, nPixelSpace, 1);
     437             :             }
     438             :         }
     439             :     }
     440             : 
     441             :     /* ---- Return success ---- */
     442          10 :     return CE_None;
     443             : }  // ModulePixelFunc
     444             : 
     445           5 : static CPLErr PhasePixelFunc(void **papoSources, int nSources, void *pData,
     446             :                              int nXSize, int nYSize, GDALDataType eSrcType,
     447             :                              GDALDataType eBufType, int nPixelSpace,
     448             :                              int nLineSpace)
     449             : {
     450             :     /* ---- Init ---- */
     451           5 :     if (nSources != 1)
     452           1 :         return CE_Failure;
     453             : 
     454           4 :     if (GDALDataTypeIsComplex(eSrcType))
     455             :     {
     456           2 :         const void *const pReal = papoSources[0];
     457           2 :         const void *const pImag = static_cast<GByte *>(papoSources[0]) +
     458           2 :                                   GDALGetDataTypeSizeBytes(eSrcType) / 2;
     459             : 
     460             :         /* ---- Set pixels ---- */
     461           2 :         size_t ii = 0;
     462          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     463             :         {
     464          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     465             :             {
     466             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     467          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     468          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
     469             : 
     470          60 :                 const double dfPixVal = atan2(dfImag, dfReal);
     471             : 
     472          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     473             :                               static_cast<GByte *>(pData) +
     474          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     475          60 :                                   iCol * nPixelSpace,
     476             :                               eBufType, nPixelSpace, 1);
     477             :             }
     478             :         }
     479             :     }
     480           2 :     else if (GDALDataTypeIsInteger(eSrcType) && !GDALDataTypeIsSigned(eSrcType))
     481             :     {
     482           1 :         constexpr double dfZero = 0;
     483           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     484             :         {
     485           6 :             GDALCopyWords(&dfZero, GDT_Float64, 0,
     486             :                           static_cast<GByte *>(pData) +
     487           6 :                               static_cast<GSpacing>(nLineSpace) * iLine,
     488             :                           eBufType, nPixelSpace, nXSize);
     489             :         }
     490             :     }
     491             :     else
     492             :     {
     493             :         /* ---- Set pixels ---- */
     494           1 :         size_t ii = 0;
     495           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     496             :         {
     497          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     498             :             {
     499          30 :                 const void *const pReal = papoSources[0];
     500             : 
     501             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     502          30 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
     503          30 :                 const double dfPixVal = (dfReal < 0) ? M_PI : 0.0;
     504             : 
     505          30 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     506             :                               static_cast<GByte *>(pData) +
     507          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     508          30 :                                   iCol * nPixelSpace,
     509             :                               eBufType, nPixelSpace, 1);
     510             :             }
     511             :         }
     512             :     }
     513             : 
     514             :     /* ---- Return success ---- */
     515           4 :     return CE_None;
     516             : }  // PhasePixelFunc
     517             : 
     518           4 : static CPLErr ConjPixelFunc(void **papoSources, int nSources, void *pData,
     519             :                             int nXSize, int nYSize, GDALDataType eSrcType,
     520             :                             GDALDataType eBufType, int nPixelSpace,
     521             :                             int nLineSpace)
     522             : {
     523             :     /* ---- Init ---- */
     524           4 :     if (nSources != 1)
     525           1 :         return CE_Failure;
     526             : 
     527           3 :     if (GDALDataTypeIsComplex(eSrcType) && GDALDataTypeIsComplex(eBufType))
     528             :     {
     529           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
     530           2 :         const void *const pReal = papoSources[0];
     531           2 :         const void *const pImag =
     532           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
     533             : 
     534             :         /* ---- Set pixels ---- */
     535           2 :         size_t ii = 0;
     536          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     537             :         {
     538          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     539             :             {
     540             :                 // Source raster pixels may be obtained with GetSrcVal macro.
     541             :                 const double adfPixVal[2] = {
     542          60 :                     +GetSrcVal(pReal, eSrcType, ii),  // re
     543         120 :                     -GetSrcVal(pImag, eSrcType, ii)   // im
     544          60 :                 };
     545             : 
     546          60 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
     547             :                               static_cast<GByte *>(pData) +
     548          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     549          60 :                                   iCol * nPixelSpace,
     550             :                               eBufType, nPixelSpace, 1);
     551             :             }
     552             :         }
     553             :     }
     554             :     else
     555             :     {
     556             :         // No complex data type.
     557           1 :         return RealPixelFunc(papoSources, nSources, pData, nXSize, nYSize,
     558           1 :                              eSrcType, eBufType, nPixelSpace, nLineSpace);
     559             :     }
     560             : 
     561             :     /* ---- Return success ---- */
     562           2 :     return CE_None;
     563             : }  // ConjPixelFunc
     564             : 
     565             : static constexpr char pszRoundPixelFuncMetadata[] =
     566             :     "<PixelFunctionArgumentsList>"
     567             :     "   <Argument name='digits' description='Digits' type='integer' "
     568             :     "default='0' />"
     569             :     "   <Argument type='builtin' value='NoData' optional='true' />"
     570             :     "</PixelFunctionArgumentsList>";
     571             : 
     572           6 : static CPLErr RoundPixelFunc(void **papoSources, int nSources, void *pData,
     573             :                              int nXSize, int nYSize, GDALDataType eSrcType,
     574             :                              GDALDataType eBufType, int nPixelSpace,
     575             :                              int nLineSpace, CSLConstList papszArgs)
     576             : {
     577             :     /* ---- Init ---- */
     578           6 :     if (nSources != 1)
     579             :     {
     580           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     581             :                  "round: input must be a single band");
     582           1 :         return CE_Failure;
     583             :     }
     584             : 
     585           5 :     if (GDALDataTypeIsComplex(eSrcType))
     586             :     {
     587           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     588             :                  "round: complex data types not supported");
     589           0 :         return CE_Failure;
     590             :     }
     591             : 
     592           5 :     double dfNoData{0};
     593           5 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
     594           5 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
     595           0 :         return CE_Failure;
     596             : 
     597           5 :     int nDigits{0};
     598           5 :     if (FetchIntegerArg(papszArgs, "digits", &nDigits, &nDigits) != CE_None)
     599           1 :         return CE_Failure;
     600             : 
     601           4 :     const double dfScaleVal = std::pow(10, nDigits);
     602           4 :     const double dfInvScaleVal = 1. / dfScaleVal;
     603             : 
     604             :     /* ---- Set pixels ---- */
     605           4 :     size_t ii = 0;
     606           8 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     607             :     {
     608          12 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     609             :         {
     610             :             // Source raster pixels may be obtained with GetSrcVal macro.
     611           8 :             const double dfSrcVal = GetSrcVal(papoSources[0], eSrcType, ii);
     612             : 
     613             :             const double dfDstVal =
     614           8 :                 bHasNoData && IsNoData(dfSrcVal, dfNoData)
     615          13 :                     ? dfNoData
     616           3 :                     : std::round(dfSrcVal * dfScaleVal) * dfInvScaleVal;
     617             : 
     618           8 :             GDALCopyWords(&dfDstVal, GDT_Float64, 0,
     619             :                           static_cast<GByte *>(pData) +
     620           8 :                               static_cast<GSpacing>(nLineSpace) * iLine +
     621           8 :                               iCol * nPixelSpace,
     622             :                           eBufType, nPixelSpace, 1);
     623             :         }
     624             :     }
     625             : 
     626             :     /* ---- Return success ---- */
     627           4 :     return CE_None;
     628             : }  // RoundPixelFunc
     629             : 
     630             : #ifdef USE_SSE2
     631             : 
     632             : /************************************************************************/
     633             : /*                      OptimizedSumToFloat_SSE2()                      */
     634             : /************************************************************************/
     635             : 
     636             : template <typename Tsrc>
     637          87 : static void OptimizedSumToFloat_SSE2(double dfK, void *pOutBuffer,
     638             :                                      int nLineSpace, int nXSize, int nYSize,
     639             :                                      int nSources,
     640             :                                      const void *const *papoSources)
     641             : {
     642          87 :     const XMMReg4Float cst = XMMReg4Float::Set1(static_cast<float>(dfK));
     643             : 
     644         279 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     645             :     {
     646         192 :         float *CPL_RESTRICT const pDest = reinterpret_cast<float *>(
     647             :             static_cast<GByte *>(pOutBuffer) +
     648         192 :             static_cast<GSpacing>(nLineSpace) * iLine);
     649         192 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     650             : 
     651         192 :         constexpr int VALUES_PER_REG = 4;
     652         192 :         constexpr int UNROLLING = 4 * VALUES_PER_REG;
     653         192 :         int iCol = 0;
     654         900 :         for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     655             :         {
     656         708 :             XMMReg4Float d0(cst);
     657         708 :             XMMReg4Float d1(cst);
     658         708 :             XMMReg4Float d2(cst);
     659         708 :             XMMReg4Float d3(cst);
     660        2104 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     661             :             {
     662        1396 :                 XMMReg4Float t0, t1, t2, t3;
     663        1396 :                 XMMReg4Float::Load16Val(
     664        1396 :                     static_cast<const Tsrc * CPL_RESTRICT>(papoSources[iSrc]) +
     665        1396 :                         iOffsetLine + iCol,
     666             :                     t0, t1, t2, t3);
     667        1396 :                 d0 += t0;
     668        1396 :                 d1 += t1;
     669        1396 :                 d2 += t2;
     670        1396 :                 d3 += t3;
     671             :             }
     672         708 :             d0.Store4Val(pDest + iCol + VALUES_PER_REG * 0);
     673         708 :             d1.Store4Val(pDest + iCol + VALUES_PER_REG * 1);
     674         708 :             d2.Store4Val(pDest + iCol + VALUES_PER_REG * 2);
     675         708 :             d3.Store4Val(pDest + iCol + VALUES_PER_REG * 3);
     676             :         }
     677             : 
     678         788 :         for (; iCol < nXSize; iCol++)
     679             :         {
     680         596 :             float d = static_cast<float>(dfK);
     681        1708 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     682             :             {
     683        1112 :                 d += static_cast<const Tsrc * CPL_RESTRICT>(
     684        1112 :                     papoSources[iSrc])[iOffsetLine + iCol];
     685             :             }
     686         596 :             pDest[iCol] = d;
     687             :         }
     688             :     }
     689          87 : }
     690             : 
     691             : /************************************************************************/
     692             : /*                     OptimizedSumToDouble_SSE2()                      */
     693             : /************************************************************************/
     694             : 
     695             : template <typename Tsrc>
     696         111 : static void OptimizedSumToDouble_SSE2(double dfK, void *pOutBuffer,
     697             :                                       int nLineSpace, int nXSize, int nYSize,
     698             :                                       int nSources,
     699             :                                       const void *const *papoSources)
     700             : {
     701         111 :     const XMMReg4Double cst = XMMReg4Double::Set1(dfK);
     702             : 
     703         367 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     704             :     {
     705         256 :         double *CPL_RESTRICT const pDest = reinterpret_cast<double *>(
     706             :             static_cast<GByte *>(pOutBuffer) +
     707         256 :             static_cast<GSpacing>(nLineSpace) * iLine);
     708         256 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     709             : 
     710         256 :         constexpr int VALUES_PER_REG = 4;
     711         256 :         constexpr int UNROLLING = 2 * VALUES_PER_REG;
     712         256 :         int iCol = 0;
     713        2048 :         for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     714             :         {
     715        1792 :             XMMReg4Double d0(cst);
     716        1792 :             XMMReg4Double d1(cst);
     717        5296 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     718             :             {
     719        3504 :                 XMMReg4Double t0, t1;
     720        3504 :                 XMMReg4Double::Load8Val(
     721        3504 :                     static_cast<const Tsrc * CPL_RESTRICT>(papoSources[iSrc]) +
     722        3504 :                         iOffsetLine + iCol,
     723             :                     t0, t1);
     724        3504 :                 d0 += t0;
     725        3504 :                 d1 += t1;
     726             :             }
     727        1792 :             d0.Store4Val(pDest + iCol + VALUES_PER_REG * 0);
     728        1792 :             d1.Store4Val(pDest + iCol + VALUES_PER_REG * 1);
     729             :         }
     730             : 
     731        1060 :         for (; iCol < nXSize; iCol++)
     732             :         {
     733         804 :             double d = dfK;
     734        2252 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     735             :             {
     736        1448 :                 d += static_cast<const Tsrc * CPL_RESTRICT>(
     737        1448 :                     papoSources[iSrc])[iOffsetLine + iCol];
     738             :             }
     739         804 :             pDest[iCol] = d;
     740             :         }
     741             :     }
     742         111 : }
     743             : 
     744             : /************************************************************************/
     745             : /*                     OptimizedSumSameType_SSE2()                      */
     746             : /************************************************************************/
     747             : 
     748             : template <typename T, typename Tsigned, typename Tacc, class SSEWrapper>
     749        1011 : static void OptimizedSumSameType_SSE2(double dfK, void *pOutBuffer,
     750             :                                       int nLineSpace, int nXSize, int nYSize,
     751             :                                       int nSources,
     752             :                                       const void *const *papoSources)
     753             : {
     754             :     static_assert(std::numeric_limits<T>::is_integer);
     755             :     static_assert(!std::numeric_limits<T>::is_signed);
     756             :     static_assert(std::numeric_limits<Tsigned>::is_integer);
     757             :     static_assert(std::numeric_limits<Tsigned>::is_signed);
     758             :     static_assert(sizeof(T) == sizeof(Tsigned));
     759        1011 :     const T nK = static_cast<T>(dfK);
     760             :     Tsigned nKSigned;
     761        1011 :     memcpy(&nKSigned, &nK, sizeof(T));
     762        1011 :     const __m128i valInit = SSEWrapper::Set1(nKSigned);
     763        1011 :     constexpr int VALUES_PER_REG =
     764             :         static_cast<int>(sizeof(valInit) / sizeof(T));
     765        2040 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     766             :     {
     767        1029 :         T *CPL_RESTRICT const pDest =
     768             :             reinterpret_cast<T *>(static_cast<GByte *>(pOutBuffer) +
     769        1029 :                                   static_cast<GSpacing>(nLineSpace) * iLine);
     770        1029 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     771        1029 :         int iCol = 0;
     772        8057 :         for (; iCol < nXSize - (4 * VALUES_PER_REG - 1);
     773             :              iCol += 4 * VALUES_PER_REG)
     774             :         {
     775        7028 :             __m128i reg0 = valInit;
     776        7028 :             __m128i reg1 = valInit;
     777        7028 :             __m128i reg2 = valInit;
     778        7028 :             __m128i reg3 = valInit;
     779       21084 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     780             :             {
     781       14056 :                 reg0 = SSEWrapper::AddSaturate(
     782             :                     reg0,
     783             :                     _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     784       14056 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
     785       14056 :                         iOffsetLine + iCol)));
     786       14056 :                 reg1 = SSEWrapper::AddSaturate(
     787             :                     reg1,
     788             :                     _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     789       14056 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
     790       14056 :                         iOffsetLine + iCol + VALUES_PER_REG)));
     791       14056 :                 reg2 = SSEWrapper::AddSaturate(
     792             :                     reg2,
     793             :                     _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     794       14056 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
     795       14056 :                         iOffsetLine + iCol + 2 * VALUES_PER_REG)));
     796       14056 :                 reg3 = SSEWrapper::AddSaturate(
     797             :                     reg3,
     798             :                     _mm_loadu_si128(reinterpret_cast<const __m128i *>(
     799       14056 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
     800       14056 :                         iOffsetLine + iCol + 3 * VALUES_PER_REG)));
     801             :             }
     802        7028 :             _mm_storeu_si128(reinterpret_cast<__m128i *>(pDest + iCol), reg0);
     803        7028 :             _mm_storeu_si128(
     804        7028 :                 reinterpret_cast<__m128i *>(pDest + iCol + VALUES_PER_REG),
     805             :                 reg1);
     806        7028 :             _mm_storeu_si128(
     807        7028 :                 reinterpret_cast<__m128i *>(pDest + iCol + 2 * VALUES_PER_REG),
     808             :                 reg2);
     809        7028 :             _mm_storeu_si128(
     810        7028 :                 reinterpret_cast<__m128i *>(pDest + iCol + 3 * VALUES_PER_REG),
     811             :                 reg3);
     812             :         }
     813       53184 :         for (; iCol < nXSize; ++iCol)
     814             :         {
     815       52155 :             Tacc nAcc = nK;
     816      156364 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
     817             :             {
     818      208418 :                 nAcc = std::min<Tacc>(
     819      208418 :                     nAcc + static_cast<const T * CPL_RESTRICT>(
     820      104209 :                                papoSources[iSrc])[iOffsetLine + iCol],
     821      104209 :                     std::numeric_limits<T>::max());
     822             :             }
     823       52155 :             pDest[iCol] = static_cast<T>(nAcc);
     824             :         }
     825             :     }
     826        1011 : }
     827             : #endif  // USE_SSE2
     828             : 
     829             : /************************************************************************/
     830             : /*                      OptimizedSumPackedOutput()                      */
     831             : /************************************************************************/
     832             : 
     833             : template <typename Tsrc, typename Tdest>
     834         237 : static void OptimizedSumPackedOutput(double dfK, void *pOutBuffer,
     835             :                                      int nLineSpace, int nXSize, int nYSize,
     836             :                                      int nSources,
     837             :                                      const void *const *papoSources)
     838             : {
     839             : #ifdef USE_SSE2
     840             :     if constexpr (std::is_same_v<Tdest, float> && !std::is_same_v<Tsrc, double>)
     841             :     {
     842          87 :         OptimizedSumToFloat_SSE2<Tsrc>(dfK, pOutBuffer, nLineSpace, nXSize,
     843             :                                        nYSize, nSources, papoSources);
     844             :     }
     845             :     else if constexpr (std::is_same_v<Tdest, double>)
     846             :     {
     847         111 :         OptimizedSumToDouble_SSE2<Tsrc>(dfK, pOutBuffer, nLineSpace, nXSize,
     848             :                                         nYSize, nSources, papoSources);
     849             :     }
     850             :     else
     851             : #endif  // USE_SSE2
     852             :     {
     853          39 :         const Tdest nCst = static_cast<Tdest>(dfK);
     854         153 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     855             :         {
     856         114 :             Tdest *CPL_RESTRICT const pDest = reinterpret_cast<Tdest *>(
     857             :                 static_cast<GByte *>(pOutBuffer) +
     858         114 :                 static_cast<GSpacing>(nLineSpace) * iLine);
     859         114 :             const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
     860             : 
     861             : #define LOAD_SRCVAL(iSrc_, j_)                                                 \
     862             :     static_cast<Tdest>(static_cast<const Tsrc * CPL_RESTRICT>(                 \
     863             :         papoSources[(iSrc_)])[iOffsetLine + iCol + (j_)])
     864             : 
     865         114 :             constexpr int UNROLLING = 4;
     866         114 :             int iCol = 0;
     867        1498 :             for (; iCol < nXSize - (UNROLLING - 1); iCol += UNROLLING)
     868             :             {
     869        1384 :                 Tdest d[4] = {nCst, nCst, nCst, nCst};
     870        4352 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
     871             :                 {
     872        2968 :                     d[0] += LOAD_SRCVAL(iSrc, 0);
     873        2968 :                     d[1] += LOAD_SRCVAL(iSrc, 1);
     874        2968 :                     d[2] += LOAD_SRCVAL(iSrc, 2);
     875        2968 :                     d[3] += LOAD_SRCVAL(iSrc, 3);
     876             :                 }
     877        1384 :                 pDest[iCol + 0] = d[0];
     878        1384 :                 pDest[iCol + 1] = d[1];
     879        1384 :                 pDest[iCol + 2] = d[2];
     880        1384 :                 pDest[iCol + 3] = d[3];
     881             :             }
     882         336 :             for (; iCol < nXSize; iCol++)
     883             :             {
     884         222 :                 Tdest d0 = nCst;
     885         666 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
     886             :                 {
     887         444 :                     d0 += LOAD_SRCVAL(iSrc, 0);
     888             :                 }
     889         222 :                 pDest[iCol] = d0;
     890             :             }
     891             : #undef LOAD_SRCVAL
     892             :         }
     893             :     }
     894         237 : }
     895             : 
     896             : /************************************************************************/
     897             : /*                      OptimizedSumPackedOutput()                      */
     898             : /************************************************************************/
     899             : 
     900             : template <typename Tdest>
     901         253 : static bool OptimizedSumPackedOutput(GDALDataType eSrcType, double dfK,
     902             :                                      void *pOutBuffer, int nLineSpace,
     903             :                                      int nXSize, int nYSize, int nSources,
     904             :                                      const void *const *papoSources)
     905             : {
     906         253 :     switch (eSrcType)
     907             :     {
     908          32 :         case GDT_UInt8:
     909          32 :             OptimizedSumPackedOutput<uint8_t, Tdest>(dfK, pOutBuffer,
     910             :                                                      nLineSpace, nXSize, nYSize,
     911             :                                                      nSources, papoSources);
     912          32 :             return true;
     913             : 
     914          32 :         case GDT_UInt16:
     915          32 :             OptimizedSumPackedOutput<uint16_t, Tdest>(
     916             :                 dfK, pOutBuffer, nLineSpace, nXSize, nYSize, nSources,
     917             :                 papoSources);
     918          32 :             return true;
     919             : 
     920          32 :         case GDT_Int16:
     921          32 :             OptimizedSumPackedOutput<int16_t, Tdest>(dfK, pOutBuffer,
     922             :                                                      nLineSpace, nXSize, nYSize,
     923             :                                                      nSources, papoSources);
     924          32 :             return true;
     925             : 
     926          32 :         case GDT_Int32:
     927          32 :             OptimizedSumPackedOutput<int32_t, Tdest>(dfK, pOutBuffer,
     928             :                                                      nLineSpace, nXSize, nYSize,
     929             :                                                      nSources, papoSources);
     930          32 :             return true;
     931             : 
     932          39 :         case GDT_Float32:
     933          39 :             OptimizedSumPackedOutput<float, Tdest>(dfK, pOutBuffer, nLineSpace,
     934             :                                                    nXSize, nYSize, nSources,
     935             :                                                    papoSources);
     936          39 :             return true;
     937             : 
     938          54 :         case GDT_Float64:
     939          54 :             OptimizedSumPackedOutput<double, Tdest>(dfK, pOutBuffer, nLineSpace,
     940             :                                                     nXSize, nYSize, nSources,
     941             :                                                     papoSources);
     942          54 :             return true;
     943             : 
     944          32 :         default:
     945          32 :             break;
     946             :     }
     947          32 :     return false;
     948             : }
     949             : 
     950             : /************************************************************************/
     951             : /*                   OptimizedSumThroughLargerType()                    */
     952             : /************************************************************************/
     953             : 
     954             : namespace
     955             : {
     956             : template <typename Tsrc, typename Tdest, typename Enable = void>
     957             : struct TintermediateS
     958             : {
     959             :     using type = double;
     960             : };
     961             : 
     962             : template <typename Tsrc, typename Tdest>
     963             : struct TintermediateS<
     964             :     Tsrc, Tdest,
     965             :     std::enable_if_t<
     966             :         (std::is_same_v<Tsrc, uint8_t> || std::is_same_v<Tsrc, int16_t> ||
     967             :          std::is_same_v<Tsrc, uint16_t>) &&
     968             :             (std::is_same_v<Tdest, uint8_t> || std::is_same_v<Tdest, int16_t> ||
     969             :              std::is_same_v<Tdest, uint16_t>),
     970             :         bool>>
     971             : {
     972             :     using type = int32_t;
     973             : };
     974             : 
     975             : }  // namespace
     976             : 
     977             : template <typename Tsrc, typename Tdest>
     978         396 : static bool OptimizedSumThroughLargerType(double dfK, void *pOutBuffer,
     979             :                                           int nPixelSpace, int nLineSpace,
     980             :                                           int nXSize, int nYSize, int nSources,
     981             :                                           const void *const *papoSources)
     982             : {
     983             :     using Tintermediate = typename TintermediateS<Tsrc, Tdest>::type;
     984         396 :     const Tintermediate k = static_cast<Tintermediate>(dfK);
     985             : 
     986         396 :     size_t ii = 0;
     987        1185 :     for (int iLine = 0; iLine < nYSize; ++iLine)
     988             :     {
     989         789 :         GByte *CPL_RESTRICT pDest = static_cast<GByte *>(pOutBuffer) +
     990         789 :                                     static_cast<GSpacing>(nLineSpace) * iLine;
     991             : 
     992         789 :         constexpr int UNROLLING = 4;
     993         789 :         int iCol = 0;
     994       13365 :         for (; iCol < nXSize - (UNROLLING - 1);
     995             :              iCol += UNROLLING, ii += UNROLLING)
     996             :         {
     997       12576 :             Tintermediate aSum[4] = {k, k, k, k};
     998             : 
     999       37728 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1000             :             {
    1001       25152 :                 aSum[0] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 0];
    1002       25152 :                 aSum[1] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 1];
    1003       25152 :                 aSum[2] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 2];
    1004       25152 :                 aSum[3] += static_cast<const Tsrc *>(papoSources[iSrc])[ii + 3];
    1005             :             }
    1006             : 
    1007       12576 :             GDALCopyWord(aSum[0], *reinterpret_cast<Tdest *>(pDest));
    1008       12576 :             pDest += nPixelSpace;
    1009       12576 :             GDALCopyWord(aSum[1], *reinterpret_cast<Tdest *>(pDest));
    1010       12576 :             pDest += nPixelSpace;
    1011       12576 :             GDALCopyWord(aSum[2], *reinterpret_cast<Tdest *>(pDest));
    1012       12576 :             pDest += nPixelSpace;
    1013       12576 :             GDALCopyWord(aSum[3], *reinterpret_cast<Tdest *>(pDest));
    1014       12576 :             pDest += nPixelSpace;
    1015             :         }
    1016        3150 :         for (; iCol < nXSize; ++iCol, ++ii, pDest += nPixelSpace)
    1017             :         {
    1018        2361 :             Tintermediate sum = k;
    1019        7081 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1020             :             {
    1021        4720 :                 sum += static_cast<const Tsrc *>(papoSources[iSrc])[ii];
    1022             :             }
    1023             : 
    1024        2361 :             auto pDst = reinterpret_cast<Tdest *>(pDest);
    1025        2361 :             GDALCopyWord(sum, *pDst);
    1026             :         }
    1027             :     }
    1028         396 :     return true;
    1029             : }
    1030             : 
    1031             : /************************************************************************/
    1032             : /*                   OptimizedSumThroughLargerType()                    */
    1033             : /************************************************************************/
    1034             : 
    1035             : template <typename Tsrc>
    1036         495 : static bool OptimizedSumThroughLargerType(GDALDataType eBufType, double dfK,
    1037             :                                           void *pOutBuffer, int nPixelSpace,
    1038             :                                           int nLineSpace, int nXSize,
    1039             :                                           int nYSize, int nSources,
    1040             :                                           const void *const *papoSources)
    1041             : {
    1042         495 :     switch (eBufType)
    1043             :     {
    1044         103 :         case GDT_UInt8:
    1045         103 :             return OptimizedSumThroughLargerType<Tsrc, uint8_t>(
    1046             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
    1047         103 :                 nSources, papoSources);
    1048             : 
    1049          99 :         case GDT_UInt16:
    1050          99 :             return OptimizedSumThroughLargerType<Tsrc, uint16_t>(
    1051             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
    1052          99 :                 nSources, papoSources);
    1053             : 
    1054         105 :         case GDT_Int16:
    1055         105 :             return OptimizedSumThroughLargerType<Tsrc, int16_t>(
    1056             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
    1057         105 :                 nSources, papoSources);
    1058             : 
    1059          89 :         case GDT_Int32:
    1060          89 :             return OptimizedSumThroughLargerType<Tsrc, int32_t>(
    1061             :                 dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize, nYSize,
    1062          89 :                 nSources, papoSources);
    1063             : 
    1064             :         // Float32 and Float64 already covered by OptimizedSum() for packed case
    1065          99 :         default:
    1066          99 :             break;
    1067             :     }
    1068          99 :     return false;
    1069             : }
    1070             : 
    1071             : /************************************************************************/
    1072             : /*                   OptimizedSumThroughLargerType()                    */
    1073             : /************************************************************************/
    1074             : 
    1075         625 : static bool OptimizedSumThroughLargerType(GDALDataType eSrcType,
    1076             :                                           GDALDataType eBufType, double dfK,
    1077             :                                           void *pOutBuffer, int nPixelSpace,
    1078             :                                           int nLineSpace, int nXSize,
    1079             :                                           int nYSize, int nSources,
    1080             :                                           const void *const *papoSources)
    1081             : {
    1082         625 :     switch (eSrcType)
    1083             :     {
    1084          61 :         case GDT_UInt8:
    1085          61 :             return OptimizedSumThroughLargerType<uint8_t>(
    1086             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1087          61 :                 nYSize, nSources, papoSources);
    1088             : 
    1089          78 :         case GDT_UInt16:
    1090          78 :             return OptimizedSumThroughLargerType<uint16_t>(
    1091             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1092          78 :                 nYSize, nSources, papoSources);
    1093             : 
    1094          85 :         case GDT_Int16:
    1095          85 :             return OptimizedSumThroughLargerType<int16_t>(
    1096             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1097          85 :                 nYSize, nSources, papoSources);
    1098             : 
    1099          91 :         case GDT_Int32:
    1100          91 :             return OptimizedSumThroughLargerType<int32_t>(
    1101             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1102          91 :                 nYSize, nSources, papoSources);
    1103             : 
    1104          86 :         case GDT_Float32:
    1105          86 :             return OptimizedSumThroughLargerType<float>(
    1106             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1107          86 :                 nYSize, nSources, papoSources);
    1108             : 
    1109          94 :         case GDT_Float64:
    1110          94 :             return OptimizedSumThroughLargerType<double>(
    1111             :                 eBufType, dfK, pOutBuffer, nPixelSpace, nLineSpace, nXSize,
    1112          94 :                 nYSize, nSources, papoSources);
    1113             : 
    1114         130 :         default:
    1115         130 :             break;
    1116             :     }
    1117             : 
    1118         130 :     return false;
    1119             : }
    1120             : 
    1121             : /************************************************************************/
    1122             : /*                            SumPixelFunc()                            */
    1123             : /************************************************************************/
    1124             : 
    1125             : static const char pszSumPixelFuncMetadata[] =
    1126             :     "<PixelFunctionArgumentsList>"
    1127             :     "   <Argument name='k' description='Optional constant term' type='double' "
    1128             :     "default='0.0' />"
    1129             :     "   <Argument name='propagateNoData' description='Whether the output value "
    1130             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    1131             :     "default='false' />"
    1132             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1133             :     "</PixelFunctionArgumentsList>";
    1134             : 
    1135        1994 : static CPLErr SumPixelFunc(void **papoSources, int nSources, void *pData,
    1136             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1137             :                            GDALDataType eBufType, int nPixelSpace,
    1138             :                            int nLineSpace, CSLConstList papszArgs)
    1139             : {
    1140             :     /* ---- Init ---- */
    1141        1994 :     if (nSources < 1)
    1142             :     {
    1143           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1144             :                  "sum requires at least one source");
    1145           1 :         return CE_Failure;
    1146             :     }
    1147             : 
    1148        1993 :     double dfK = 0.0;
    1149        1993 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1150           0 :         return CE_Failure;
    1151             : 
    1152        1993 :     double dfNoData{0};
    1153        1993 :     bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1154        1993 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1155           0 :         return CE_Failure;
    1156             : 
    1157        1993 :     const bool bPropagateNoData = CPLTestBool(
    1158             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    1159             : 
    1160        1993 :     if (dfNoData == 0 && !bPropagateNoData)
    1161        1909 :         bHasNoData = false;
    1162             : 
    1163             :     /* ---- Set pixels ---- */
    1164        1993 :     if (GDALDataTypeIsComplex(eSrcType))
    1165             :     {
    1166          36 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1167             : 
    1168             :         /* ---- Set pixels ---- */
    1169          36 :         size_t ii = 0;
    1170         112 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1171             :         {
    1172        4796 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1173             :             {
    1174        4720 :                 double adfSum[2] = {dfK, 0.0};
    1175             : 
    1176       14190 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1177             :                 {
    1178        9470 :                     const void *const pReal = papoSources[iSrc];
    1179        9470 :                     const void *const pImag =
    1180        9470 :                         static_cast<const GByte *>(pReal) + nOffset;
    1181             : 
    1182             :                     // Source raster pixels may be obtained with GetSrcVal
    1183             :                     // macro.
    1184        9470 :                     adfSum[0] += GetSrcVal(pReal, eSrcType, ii);
    1185        9470 :                     adfSum[1] += GetSrcVal(pImag, eSrcType, ii);
    1186             :                 }
    1187             : 
    1188        4720 :                 GDALCopyWords(adfSum, GDT_CFloat64, 0,
    1189             :                               static_cast<GByte *>(pData) +
    1190        4720 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1191        4720 :                                   iCol * nPixelSpace,
    1192             :                               eBufType, nPixelSpace, 1);
    1193             :             }
    1194             :         }
    1195             :     }
    1196             :     else
    1197             :     {
    1198             :         /* ---- Set pixels ---- */
    1199        1957 :         bool bGeneralCase = true;
    1200        1957 :         if (dfNoData == 0 && !bPropagateNoData)
    1201             :         {
    1202             : #ifdef USE_SSE2
    1203        1128 :             if (eBufType == GDT_UInt8 && nPixelSpace == sizeof(uint8_t) &&
    1204        1019 :                 eSrcType == GDT_UInt8 &&
    1205        1019 :                 dfK >= std::numeric_limits<uint8_t>::min() &&
    1206        4020 :                 dfK <= std::numeric_limits<uint8_t>::max() &&
    1207        1019 :                 static_cast<int>(dfK) == dfK)
    1208             :             {
    1209        1006 :                 bGeneralCase = false;
    1210             : 
    1211             :                 struct SSEWrapper
    1212             :                 {
    1213        1006 :                     inline static __m128i Set1(int8_t x)
    1214             :                     {
    1215        2012 :                         return _mm_set1_epi8(x);
    1216             :                     }
    1217             : 
    1218       56064 :                     inline static __m128i AddSaturate(__m128i x, __m128i y)
    1219             :                     {
    1220       56064 :                         return _mm_adds_epu8(x, y);
    1221             :                     }
    1222             :                 };
    1223             : 
    1224             :                 OptimizedSumSameType_SSE2<uint8_t, int8_t, uint32_t,
    1225        1006 :                                           SSEWrapper>(dfK, pData, nLineSpace,
    1226             :                                                       nXSize, nYSize, nSources,
    1227             :                                                       papoSources);
    1228             :             }
    1229         123 :             else if (eBufType == GDT_UInt16 &&
    1230         123 :                      nPixelSpace == sizeof(uint16_t) &&
    1231          18 :                      eSrcType == GDT_UInt16 &&
    1232          18 :                      dfK >= std::numeric_limits<uint16_t>::min() &&
    1233        1008 :                      dfK <= std::numeric_limits<uint16_t>::max() &&
    1234          18 :                      static_cast<int>(dfK) == dfK)
    1235             :             {
    1236           5 :                 bGeneralCase = false;
    1237             : 
    1238             :                 struct SSEWrapper
    1239             :                 {
    1240           5 :                     inline static __m128i Set1(int16_t x)
    1241             :                     {
    1242          10 :                         return _mm_set1_epi16(x);
    1243             :                     }
    1244             : 
    1245         160 :                     inline static __m128i AddSaturate(__m128i x, __m128i y)
    1246             :                     {
    1247         160 :                         return _mm_adds_epu16(x, y);
    1248             :                     }
    1249             :                 };
    1250             : 
    1251             :                 OptimizedSumSameType_SSE2<uint16_t, int16_t, uint32_t,
    1252           5 :                                           SSEWrapper>(dfK, pData, nLineSpace,
    1253             :                                                       nXSize, nYSize, nSources,
    1254             :                                                       papoSources);
    1255             :             }
    1256             :             else
    1257             : #endif
    1258         862 :                 if (eBufType == GDT_Float32 && nPixelSpace == sizeof(float))
    1259             :             {
    1260         126 :                 bGeneralCase = !OptimizedSumPackedOutput<float>(
    1261             :                     eSrcType, dfK, pData, nLineSpace, nXSize, nYSize, nSources,
    1262             :                     papoSources);
    1263             :             }
    1264         736 :             else if (eBufType == GDT_Float64 && nPixelSpace == sizeof(double))
    1265             :             {
    1266         127 :                 bGeneralCase = !OptimizedSumPackedOutput<double>(
    1267             :                     eSrcType, dfK, pData, nLineSpace, nXSize, nYSize, nSources,
    1268             :                     papoSources);
    1269             :             }
    1270         609 :             else if (
    1271         609 :                 dfK >= 0 && dfK <= INT_MAX && eBufType == GDT_Int32 &&
    1272        1234 :                 nPixelSpace == sizeof(int32_t) && eSrcType == GDT_UInt8 &&
    1273             :                 // Limitation to avoid overflow of int32 if all source values are at the max of their data type
    1274          16 :                 nSources <=
    1275          16 :                     (INT_MAX - dfK) / std::numeric_limits<uint8_t>::max())
    1276             :             {
    1277          16 :                 bGeneralCase = false;
    1278          16 :                 OptimizedSumPackedOutput<uint8_t, int32_t>(
    1279             :                     dfK, pData, nLineSpace, nXSize, nYSize, nSources,
    1280             :                     papoSources);
    1281             :             }
    1282             : 
    1283        2498 :             if (bGeneralCase && dfK >= 0 && dfK <= INT_MAX &&
    1284         625 :                 nSources <=
    1285         625 :                     (INT_MAX - dfK) / std::numeric_limits<uint16_t>::max())
    1286             :             {
    1287         625 :                 bGeneralCase = !OptimizedSumThroughLargerType(
    1288             :                     eSrcType, eBufType, dfK, pData, nPixelSpace, nLineSpace,
    1289             :                     nXSize, nYSize, nSources, papoSources);
    1290             :             }
    1291             :         }
    1292             : 
    1293        1957 :         if (bGeneralCase)
    1294             :         {
    1295         313 :             size_t ii = 0;
    1296        1249 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    1297             :             {
    1298       51729 :                 for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1299             :                 {
    1300       50793 :                     double dfSum = dfK;
    1301      152320 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1302             :                     {
    1303             :                         const double dfVal =
    1304      101537 :                             GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1305             : 
    1306      101537 :                         if (bHasNoData && IsNoData(dfVal, dfNoData))
    1307             :                         {
    1308          23 :                             if (bPropagateNoData)
    1309             :                             {
    1310          10 :                                 dfSum = dfNoData;
    1311          10 :                                 break;
    1312             :                             }
    1313             :                         }
    1314             :                         else
    1315             :                         {
    1316      101514 :                             dfSum += dfVal;
    1317             :                         }
    1318             :                     }
    1319             : 
    1320       50793 :                     GDALCopyWords(&dfSum, GDT_Float64, 0,
    1321             :                                   static_cast<GByte *>(pData) +
    1322       50793 :                                       static_cast<GSpacing>(nLineSpace) *
    1323       50793 :                                           iLine +
    1324       50793 :                                       iCol * nPixelSpace,
    1325             :                                   eBufType, nPixelSpace, 1);
    1326             :                 }
    1327             :             }
    1328             :         }
    1329             :     }
    1330             : 
    1331             :     /* ---- Return success ---- */
    1332        1993 :     return CE_None;
    1333             : } /* SumPixelFunc */
    1334             : 
    1335             : static const char pszDiffPixelFuncMetadata[] =
    1336             :     "<PixelFunctionArgumentsList>"
    1337             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1338             :     "</PixelFunctionArgumentsList>";
    1339             : 
    1340          12 : static CPLErr DiffPixelFunc(void **papoSources, int nSources, void *pData,
    1341             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1342             :                             GDALDataType eBufType, int nPixelSpace,
    1343             :                             int nLineSpace, CSLConstList papszArgs)
    1344             : {
    1345             :     /* ---- Init ---- */
    1346          12 :     if (nSources != 2)
    1347           1 :         return CE_Failure;
    1348             : 
    1349          11 :     double dfNoData{0};
    1350          11 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1351          11 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1352           0 :         return CE_Failure;
    1353             : 
    1354          11 :     if (GDALDataTypeIsComplex(eSrcType))
    1355             :     {
    1356           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1357           1 :         const void *const pReal0 = papoSources[0];
    1358           1 :         const void *const pImag0 =
    1359           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1360           1 :         const void *const pReal1 = papoSources[1];
    1361           1 :         const void *const pImag1 =
    1362           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1363             : 
    1364             :         /* ---- Set pixels ---- */
    1365           1 :         size_t ii = 0;
    1366           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1367             :         {
    1368          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1369             :             {
    1370             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1371          30 :                 double adfPixVal[2] = {GetSrcVal(pReal0, eSrcType, ii) -
    1372          30 :                                            GetSrcVal(pReal1, eSrcType, ii),
    1373          90 :                                        GetSrcVal(pImag0, eSrcType, ii) -
    1374          30 :                                            GetSrcVal(pImag1, eSrcType, ii)};
    1375             : 
    1376          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1377             :                               static_cast<GByte *>(pData) +
    1378          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1379          30 :                                   iCol * nPixelSpace,
    1380             :                               eBufType, nPixelSpace, 1);
    1381             :             }
    1382             :         }
    1383             :     }
    1384             :     else
    1385             :     {
    1386             :         /* ---- Set pixels ---- */
    1387          10 :         size_t ii = 0;
    1388          25 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1389             :         {
    1390          57 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1391             :             {
    1392          42 :                 const double dfA = GetSrcVal(papoSources[0], eSrcType, ii);
    1393          42 :                 const double dfB = GetSrcVal(papoSources[1], eSrcType, ii);
    1394             : 
    1395             :                 const double dfPixVal =
    1396          46 :                     bHasNoData &&
    1397           4 :                             (IsNoData(dfA, dfNoData) || IsNoData(dfB, dfNoData))
    1398          46 :                         ? dfNoData
    1399          42 :                         : dfA - dfB;
    1400             : 
    1401          42 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1402             :                               static_cast<GByte *>(pData) +
    1403          42 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1404          42 :                                   iCol * nPixelSpace,
    1405             :                               eBufType, nPixelSpace, 1);
    1406             :             }
    1407             :         }
    1408             :     }
    1409             : 
    1410             :     /* ---- Return success ---- */
    1411          11 :     return CE_None;
    1412             : }  // DiffPixelFunc
    1413             : 
    1414             : static const char pszMulPixelFuncMetadata[] =
    1415             :     "<PixelFunctionArgumentsList>"
    1416             :     "   <Argument name='k' description='Optional constant factor' "
    1417             :     "type='double' default='1.0' />"
    1418             :     "   <Argument name='propagateNoData' description='Whether the output value "
    1419             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    1420             :     "default='false' />"
    1421             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    1422             :     "</PixelFunctionArgumentsList>";
    1423             : 
    1424          68 : static CPLErr MulPixelFunc(void **papoSources, int nSources, void *pData,
    1425             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1426             :                            GDALDataType eBufType, int nPixelSpace,
    1427             :                            int nLineSpace, CSLConstList papszArgs)
    1428             : {
    1429             :     /* ---- Init ---- */
    1430          68 :     if (nSources < 2 && CSLFetchNameValue(papszArgs, "k") == nullptr)
    1431             :     {
    1432           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1433             :                  "mul requires at least two sources or a specified constant k");
    1434           1 :         return CE_Failure;
    1435             :     }
    1436             : 
    1437          67 :     double dfK = 1.0;
    1438          67 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1439           0 :         return CE_Failure;
    1440             : 
    1441          67 :     double dfNoData{0};
    1442          67 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1443          67 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1444           0 :         return CE_Failure;
    1445             : 
    1446          67 :     const bool bPropagateNoData = CPLTestBool(
    1447             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    1448             : 
    1449             :     /* ---- Set pixels ---- */
    1450          67 :     if (GDALDataTypeIsComplex(eSrcType))
    1451             :     {
    1452           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1453             : 
    1454             :         /* ---- Set pixels ---- */
    1455           1 :         size_t ii = 0;
    1456           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1457             :         {
    1458          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1459             :             {
    1460          30 :                 double adfPixVal[2] = {dfK, 0.0};
    1461             : 
    1462          90 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1463             :                 {
    1464          60 :                     const void *const pReal = papoSources[iSrc];
    1465          60 :                     const void *const pImag =
    1466          60 :                         static_cast<const GByte *>(pReal) + nOffset;
    1467             : 
    1468          60 :                     const double dfOldR = adfPixVal[0];
    1469          60 :                     const double dfOldI = adfPixVal[1];
    1470             : 
    1471             :                     // Source raster pixels may be obtained with GetSrcVal
    1472             :                     // macro.
    1473          60 :                     const double dfNewR = GetSrcVal(pReal, eSrcType, ii);
    1474          60 :                     const double dfNewI = GetSrcVal(pImag, eSrcType, ii);
    1475             : 
    1476          60 :                     adfPixVal[0] = dfOldR * dfNewR - dfOldI * dfNewI;
    1477          60 :                     adfPixVal[1] = dfOldR * dfNewI + dfOldI * dfNewR;
    1478             :                 }
    1479             : 
    1480          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1481             :                               static_cast<GByte *>(pData) +
    1482          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1483          30 :                                   iCol * nPixelSpace,
    1484             :                               eBufType, nPixelSpace, 1);
    1485             :             }
    1486             :         }
    1487             :     }
    1488             :     else
    1489             :     {
    1490             :         /* ---- Set pixels ---- */
    1491          66 :         size_t ii = 0;
    1492         679 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1493             :         {
    1494       26883 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1495             :             {
    1496       26270 :                 double dfPixVal = dfK;  // Not complex.
    1497             : 
    1498       54157 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1499             :                 {
    1500             :                     const double dfVal =
    1501       27893 :                         GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1502             : 
    1503       27893 :                     if (bHasNoData && IsNoData(dfVal, dfNoData))
    1504             :                     {
    1505          18 :                         if (bPropagateNoData)
    1506             :                         {
    1507           6 :                             dfPixVal = dfNoData;
    1508           6 :                             break;
    1509             :                         }
    1510             :                     }
    1511             :                     else
    1512             :                     {
    1513       27875 :                         dfPixVal *= dfVal;
    1514             :                     }
    1515             :                 }
    1516             : 
    1517       26270 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1518             :                               static_cast<GByte *>(pData) +
    1519       26270 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1520       26270 :                                   iCol * nPixelSpace,
    1521             :                               eBufType, nPixelSpace, 1);
    1522             :             }
    1523             :         }
    1524             :     }
    1525             : 
    1526             :     /* ---- Return success ---- */
    1527          67 :     return CE_None;
    1528             : }  // MulPixelFunc
    1529             : 
    1530             : static const char pszDivPixelFuncMetadata[] =
    1531             :     "<PixelFunctionArgumentsList>"
    1532             :     "   "
    1533             :     "<Argument type='builtin' value='NoData' optional='true' />"
    1534             :     "</PixelFunctionArgumentsList>";
    1535             : 
    1536          14 : static CPLErr DivPixelFunc(void **papoSources, int nSources, void *pData,
    1537             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1538             :                            GDALDataType eBufType, int nPixelSpace,
    1539             :                            int nLineSpace, CSLConstList papszArgs)
    1540             : {
    1541             :     /* ---- Init ---- */
    1542          14 :     if (nSources != 2)
    1543           0 :         return CE_Failure;
    1544             : 
    1545          14 :     double dfNoData{0};
    1546          14 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1547          14 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1548           0 :         return CE_Failure;
    1549             : 
    1550             :     /* ---- Set pixels ---- */
    1551          14 :     if (GDALDataTypeIsComplex(eSrcType))
    1552             :     {
    1553           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1554           1 :         const void *const pReal0 = papoSources[0];
    1555           1 :         const void *const pImag0 =
    1556           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1557           1 :         const void *const pReal1 = papoSources[1];
    1558           1 :         const void *const pImag1 =
    1559           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1560             : 
    1561             :         /* ---- Set pixels ---- */
    1562           1 :         size_t ii = 0;
    1563           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1564             :         {
    1565          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1566             :             {
    1567             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1568          30 :                 const double dfReal0 = GetSrcVal(pReal0, eSrcType, ii);
    1569          30 :                 const double dfReal1 = GetSrcVal(pReal1, eSrcType, ii);
    1570          30 :                 const double dfImag0 = GetSrcVal(pImag0, eSrcType, ii);
    1571          30 :                 const double dfImag1 = GetSrcVal(pImag1, eSrcType, ii);
    1572          30 :                 const double dfAux = dfReal1 * dfReal1 + dfImag1 * dfImag1;
    1573             : 
    1574             :                 const double adfPixVal[2] = {
    1575             :                     dfAux == 0
    1576          30 :                         ? std::numeric_limits<double>::infinity()
    1577          30 :                         : dfReal0 * dfReal1 / dfAux + dfImag0 * dfImag1 / dfAux,
    1578           0 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1579          30 :                                : dfReal1 / dfAux * dfImag0 -
    1580          30 :                                      dfReal0 * dfImag1 / dfAux};
    1581             : 
    1582          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1583             :                               static_cast<GByte *>(pData) +
    1584          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1585          30 :                                   iCol * nPixelSpace,
    1586             :                               eBufType, nPixelSpace, 1);
    1587             :             }
    1588             :         }
    1589             :     }
    1590             :     else
    1591             :     {
    1592             :         /* ---- Set pixels ---- */
    1593          13 :         size_t ii = 0;
    1594          31 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1595             :         {
    1596          63 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1597             :             {
    1598          45 :                 const double dfNum = GetSrcVal(papoSources[0], eSrcType, ii);
    1599          45 :                 const double dfDenom = GetSrcVal(papoSources[1], eSrcType, ii);
    1600             : 
    1601          45 :                 double dfPixVal = dfNoData;
    1602          49 :                 if (!bHasNoData || (!IsNoData(dfNum, dfNoData) &&
    1603           4 :                                     !IsNoData(dfDenom, dfNoData)))
    1604             :                 {
    1605             :                     // coverity[divide_by_zero]
    1606          41 :                     dfPixVal =
    1607             :                         dfDenom == 0
    1608          41 :                             ? std::numeric_limits<double>::infinity()
    1609             :                             : dfNum /
    1610             : #ifdef __COVERITY__
    1611             :                                   (dfDenom + std::numeric_limits<double>::min())
    1612             : #else
    1613             :                                   dfDenom
    1614             : #endif
    1615             :                         ;
    1616             :                 }
    1617             : 
    1618          45 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1619             :                               static_cast<GByte *>(pData) +
    1620          45 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1621          45 :                                   iCol * nPixelSpace,
    1622             :                               eBufType, nPixelSpace, 1);
    1623             :             }
    1624             :         }
    1625             :     }
    1626             : 
    1627             :     /* ---- Return success ---- */
    1628          14 :     return CE_None;
    1629             : }  // DivPixelFunc
    1630             : 
    1631           3 : static CPLErr CMulPixelFunc(void **papoSources, int nSources, void *pData,
    1632             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1633             :                             GDALDataType eBufType, int nPixelSpace,
    1634             :                             int nLineSpace)
    1635             : {
    1636             :     /* ---- Init ---- */
    1637           3 :     if (nSources != 2)
    1638           1 :         return CE_Failure;
    1639             : 
    1640             :     /* ---- Set pixels ---- */
    1641           2 :     if (GDALDataTypeIsComplex(eSrcType))
    1642             :     {
    1643           1 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1644           1 :         const void *const pReal0 = papoSources[0];
    1645           1 :         const void *const pImag0 =
    1646           1 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1647           1 :         const void *const pReal1 = papoSources[1];
    1648           1 :         const void *const pImag1 =
    1649           1 :             static_cast<GByte *>(papoSources[1]) + nOffset;
    1650             : 
    1651           1 :         size_t ii = 0;
    1652           7 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1653             :         {
    1654          36 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1655             :             {
    1656             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1657          30 :                 const double dfReal0 = GetSrcVal(pReal0, eSrcType, ii);
    1658          30 :                 const double dfReal1 = GetSrcVal(pReal1, eSrcType, ii);
    1659          30 :                 const double dfImag0 = GetSrcVal(pImag0, eSrcType, ii);
    1660          30 :                 const double dfImag1 = GetSrcVal(pImag1, eSrcType, ii);
    1661             :                 const double adfPixVal[2] = {
    1662          30 :                     dfReal0 * dfReal1 + dfImag0 * dfImag1,
    1663          30 :                     dfReal1 * dfImag0 - dfReal0 * dfImag1};
    1664             : 
    1665          30 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1666             :                               static_cast<GByte *>(pData) +
    1667          30 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1668          30 :                                   iCol * nPixelSpace,
    1669             :                               eBufType, nPixelSpace, 1);
    1670             :             }
    1671             :         }
    1672             :     }
    1673             :     else
    1674             :     {
    1675           1 :         size_t ii = 0;
    1676          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1677             :         {
    1678         420 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1679             :             {
    1680             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1681             :                 // Not complex.
    1682         400 :                 const double adfPixVal[2] = {
    1683         400 :                     GetSrcVal(papoSources[0], eSrcType, ii) *
    1684         400 :                         GetSrcVal(papoSources[1], eSrcType, ii),
    1685         400 :                     0.0};
    1686             : 
    1687         400 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1688             :                               static_cast<GByte *>(pData) +
    1689         400 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1690         400 :                                   iCol * nPixelSpace,
    1691             :                               eBufType, nPixelSpace, 1);
    1692             :             }
    1693             :         }
    1694             :     }
    1695             : 
    1696             :     /* ---- Return success ---- */
    1697           2 :     return CE_None;
    1698             : }  // CMulPixelFunc
    1699             : 
    1700             : static const char pszInvPixelFuncMetadata[] =
    1701             :     "<PixelFunctionArgumentsList>"
    1702             :     "   <Argument name='k' description='Optional constant factor' "
    1703             :     "type='double' default='1.0' />"
    1704             :     "   "
    1705             :     "<Argument type='builtin' value='NoData' optional='true' />"
    1706             :     "</PixelFunctionArgumentsList>";
    1707             : 
    1708          13 : static CPLErr InvPixelFunc(void **papoSources, int nSources, void *pData,
    1709             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    1710             :                            GDALDataType eBufType, int nPixelSpace,
    1711             :                            int nLineSpace, CSLConstList papszArgs)
    1712             : {
    1713             :     /* ---- Init ---- */
    1714          13 :     if (nSources != 1)
    1715           1 :         return CE_Failure;
    1716             : 
    1717          12 :     double dfK = 1.0;
    1718          12 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1719           0 :         return CE_Failure;
    1720             : 
    1721          12 :     double dfNoData{0};
    1722          12 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1723          12 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1724           0 :         return CE_Failure;
    1725             : 
    1726             :     /* ---- Set pixels ---- */
    1727          12 :     if (GDALDataTypeIsComplex(eSrcType))
    1728             :     {
    1729           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1730           2 :         const void *const pReal = papoSources[0];
    1731           2 :         const void *const pImag =
    1732           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1733             : 
    1734           2 :         size_t ii = 0;
    1735           9 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1736             :         {
    1737          38 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1738             :             {
    1739             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1740          31 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1741          31 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1742          31 :                 const double dfAux = dfReal * dfReal + dfImag * dfImag;
    1743             :                 const double adfPixVal[2] = {
    1744          31 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1745          30 :                                : dfK * dfReal / dfAux,
    1746           1 :                     dfAux == 0 ? std::numeric_limits<double>::infinity()
    1747          31 :                                : -dfK * dfImag / dfAux};
    1748             : 
    1749          31 :                 GDALCopyWords(adfPixVal, GDT_CFloat64, 0,
    1750             :                               static_cast<GByte *>(pData) +
    1751          31 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1752          31 :                                   iCol * nPixelSpace,
    1753             :                               eBufType, nPixelSpace, 1);
    1754             :             }
    1755             :         }
    1756             :     }
    1757             :     else
    1758             :     {
    1759             :         /* ---- Set pixels ---- */
    1760          10 :         size_t ii = 0;
    1761          58 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1762             :         {
    1763         860 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1764             :             {
    1765             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1766             :                 // Not complex.
    1767         812 :                 const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1768         812 :                 double dfPixVal = dfNoData;
    1769             : 
    1770         812 :                 if (!bHasNoData || !IsNoData(dfVal, dfNoData))
    1771             :                 {
    1772         807 :                     dfPixVal =
    1773             :                         dfVal == 0
    1774         807 :                             ? std::numeric_limits<double>::infinity()
    1775         806 :                             : dfK /
    1776             : #ifdef __COVERITY__
    1777             :                                   (dfVal + std::numeric_limits<double>::min())
    1778             : #else
    1779             :                                   dfVal
    1780             : #endif
    1781             :                         ;
    1782             :                 }
    1783             : 
    1784         812 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1785             :                               static_cast<GByte *>(pData) +
    1786         812 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1787         812 :                                   iCol * nPixelSpace,
    1788             :                               eBufType, nPixelSpace, 1);
    1789             :             }
    1790             :         }
    1791             :     }
    1792             : 
    1793             :     /* ---- Return success ---- */
    1794          12 :     return CE_None;
    1795             : }  // InvPixelFunc
    1796             : 
    1797           4 : static CPLErr IntensityPixelFunc(void **papoSources, int nSources, void *pData,
    1798             :                                  int nXSize, int nYSize, GDALDataType eSrcType,
    1799             :                                  GDALDataType eBufType, int nPixelSpace,
    1800             :                                  int nLineSpace)
    1801             : {
    1802             :     /* ---- Init ---- */
    1803           4 :     if (nSources != 1)
    1804           1 :         return CE_Failure;
    1805             : 
    1806           3 :     if (GDALDataTypeIsComplex(eSrcType))
    1807             :     {
    1808           2 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1809           2 :         const void *const pReal = papoSources[0];
    1810           2 :         const void *const pImag =
    1811           2 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1812             : 
    1813             :         /* ---- Set pixels ---- */
    1814           2 :         size_t ii = 0;
    1815          14 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1816             :         {
    1817          72 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1818             :             {
    1819             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1820          60 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1821          60 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1822             : 
    1823          60 :                 const double dfPixVal = dfReal * dfReal + dfImag * dfImag;
    1824             : 
    1825          60 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1826             :                               static_cast<GByte *>(pData) +
    1827          60 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1828          60 :                                   iCol * nPixelSpace,
    1829             :                               eBufType, nPixelSpace, 1);
    1830             :             }
    1831             :         }
    1832             :     }
    1833             :     else
    1834             :     {
    1835             :         /* ---- Set pixels ---- */
    1836           1 :         size_t ii = 0;
    1837          21 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1838             :         {
    1839         420 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1840             :             {
    1841             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1842         400 :                 double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1843         400 :                 dfPixVal *= dfPixVal;
    1844             : 
    1845         400 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1846             :                               static_cast<GByte *>(pData) +
    1847         400 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1848         400 :                                   iCol * nPixelSpace,
    1849             :                               eBufType, nPixelSpace, 1);
    1850             :             }
    1851             :         }
    1852             :     }
    1853             : 
    1854             :     /* ---- Return success ---- */
    1855           3 :     return CE_None;
    1856             : }  // IntensityPixelFunc
    1857             : 
    1858             : static const char pszSqrtPixelFuncMetadata[] =
    1859             :     "<PixelFunctionArgumentsList>"
    1860             :     "   <Argument type='builtin' value='NoData' optional='true'/>"
    1861             :     "</PixelFunctionArgumentsList>";
    1862             : 
    1863           7 : static CPLErr SqrtPixelFunc(void **papoSources, int nSources, void *pData,
    1864             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    1865             :                             GDALDataType eBufType, int nPixelSpace,
    1866             :                             int nLineSpace, CSLConstList papszArgs)
    1867             : {
    1868             :     /* ---- Init ---- */
    1869           7 :     if (nSources != 1)
    1870           1 :         return CE_Failure;
    1871           6 :     if (GDALDataTypeIsComplex(eSrcType))
    1872           0 :         return CE_Failure;
    1873             : 
    1874           6 :     double dfNoData{0};
    1875           6 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1876           6 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1877           0 :         return CE_Failure;
    1878             : 
    1879             :     /* ---- Set pixels ---- */
    1880           6 :     size_t ii = 0;
    1881          31 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1882             :     {
    1883         431 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1884             :         {
    1885             :             // Source raster pixels may be obtained with GetSrcVal macro.
    1886         406 :             double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1887             : 
    1888         406 :             if (bHasNoData && IsNoData(dfPixVal, dfNoData))
    1889             :             {
    1890           2 :                 dfPixVal = dfNoData;
    1891             :             }
    1892             :             else
    1893             :             {
    1894         404 :                 dfPixVal = std::sqrt(dfPixVal);
    1895             :             }
    1896             : 
    1897         406 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1898             :                           static_cast<GByte *>(pData) +
    1899         406 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1900         406 :                               iCol * nPixelSpace,
    1901             :                           eBufType, nPixelSpace, 1);
    1902             :         }
    1903             :     }
    1904             : 
    1905             :     /* ---- Return success ---- */
    1906           6 :     return CE_None;
    1907             : }  // SqrtPixelFunc
    1908             : 
    1909          17 : static CPLErr Log10PixelFuncHelper(void **papoSources, int nSources,
    1910             :                                    void *pData, int nXSize, int nYSize,
    1911             :                                    GDALDataType eSrcType, GDALDataType eBufType,
    1912             :                                    int nPixelSpace, int nLineSpace,
    1913             :                                    CSLConstList papszArgs, double fact)
    1914             : {
    1915             :     /* ---- Init ---- */
    1916          17 :     if (nSources != 1)
    1917           2 :         return CE_Failure;
    1918             : 
    1919          15 :     double dfNoData{0};
    1920          15 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1921          15 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1922           0 :         return CE_Failure;
    1923             : 
    1924          15 :     if (GDALDataTypeIsComplex(eSrcType))
    1925             :     {
    1926             :         // Complex input datatype.
    1927           5 :         const int nOffset = GDALGetDataTypeSizeBytes(eSrcType) / 2;
    1928           5 :         const void *const pReal = papoSources[0];
    1929           5 :         const void *const pImag =
    1930           5 :             static_cast<GByte *>(papoSources[0]) + nOffset;
    1931             : 
    1932             :         /* We should compute fact * log10( sqrt( dfReal * dfReal + dfImag *
    1933             :          * dfImag ) ) */
    1934             :         /* Given that log10(sqrt(x)) = 0.5 * log10(x) */
    1935             :         /* we can remove the sqrt() by multiplying fact by 0.5 */
    1936           5 :         fact *= 0.5;
    1937             : 
    1938             :         /* ---- Set pixels ---- */
    1939           5 :         size_t ii = 0;
    1940          35 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1941             :         {
    1942         180 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1943             :             {
    1944             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1945         150 :                 const double dfReal = GetSrcVal(pReal, eSrcType, ii);
    1946         150 :                 const double dfImag = GetSrcVal(pImag, eSrcType, ii);
    1947             : 
    1948             :                 const double dfPixVal =
    1949         150 :                     fact * log10(dfReal * dfReal + dfImag * dfImag);
    1950             : 
    1951         150 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1952             :                               static_cast<GByte *>(pData) +
    1953         150 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1954         150 :                                   iCol * nPixelSpace,
    1955             :                               eBufType, nPixelSpace, 1);
    1956             :             }
    1957             :         }
    1958             :     }
    1959             :     else
    1960             :     {
    1961             :         /* ---- Set pixels ---- */
    1962          10 :         size_t ii = 0;
    1963          96 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1964             :         {
    1965        1694 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1966             :             {
    1967             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1968        1608 :                 const double dfSrcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1969             :                 const double dfPixVal =
    1970           4 :                     bHasNoData && IsNoData(dfSrcVal, dfNoData)
    1971        1612 :                         ? dfNoData
    1972        1604 :                         : fact * std::log10(std::abs(dfSrcVal));
    1973             : 
    1974        1608 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1975             :                               static_cast<GByte *>(pData) +
    1976        1608 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1977        1608 :                                   iCol * nPixelSpace,
    1978             :                               eBufType, nPixelSpace, 1);
    1979             :             }
    1980             :         }
    1981             :     }
    1982             : 
    1983             :     /* ---- Return success ---- */
    1984          15 :     return CE_None;
    1985             : }  // Log10PixelFuncHelper
    1986             : 
    1987             : static const char pszLog10PixelFuncMetadata[] =
    1988             :     "<PixelFunctionArgumentsList>"
    1989             :     "   <Argument type='builtin' value='NoData' optional='true'/>"
    1990             :     "</PixelFunctionArgumentsList>";
    1991             : 
    1992           9 : static CPLErr Log10PixelFunc(void **papoSources, int nSources, void *pData,
    1993             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    1994             :                              GDALDataType eBufType, int nPixelSpace,
    1995             :                              int nLineSpace, CSLConstList papszArgs)
    1996             : {
    1997           9 :     return Log10PixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    1998             :                                 eSrcType, eBufType, nPixelSpace, nLineSpace,
    1999           9 :                                 papszArgs, 1.0);
    2000             : }  // Log10PixelFunc
    2001             : 
    2002             : static const char pszDBPixelFuncMetadata[] =
    2003             :     "<PixelFunctionArgumentsList>"
    2004             :     "   <Argument name='fact' description='Factor' type='double' "
    2005             :     "default='20.0' />"
    2006             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2007             :     "</PixelFunctionArgumentsList>";
    2008             : 
    2009           8 : static CPLErr DBPixelFunc(void **papoSources, int nSources, void *pData,
    2010             :                           int nXSize, int nYSize, GDALDataType eSrcType,
    2011             :                           GDALDataType eBufType, int nPixelSpace,
    2012             :                           int nLineSpace, CSLConstList papszArgs)
    2013             : {
    2014           8 :     double dfFact = 20.;
    2015           8 :     if (FetchDoubleArg(papszArgs, "fact", &dfFact, &dfFact) != CE_None)
    2016           0 :         return CE_Failure;
    2017             : 
    2018           8 :     return Log10PixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2019             :                                 eSrcType, eBufType, nPixelSpace, nLineSpace,
    2020           8 :                                 papszArgs, dfFact);
    2021             : }  // DBPixelFunc
    2022             : 
    2023          12 : static CPLErr ExpPixelFuncHelper(void **papoSources, int nSources, void *pData,
    2024             :                                  int nXSize, int nYSize, GDALDataType eSrcType,
    2025             :                                  GDALDataType eBufType, int nPixelSpace,
    2026             :                                  int nLineSpace, CSLConstList papszArgs,
    2027             :                                  double base, double fact)
    2028             : {
    2029             :     /* ---- Init ---- */
    2030          12 :     if (nSources != 1)
    2031           2 :         return CE_Failure;
    2032          10 :     if (GDALDataTypeIsComplex(eSrcType))
    2033           0 :         return CE_Failure;
    2034             : 
    2035          10 :     double dfNoData{0};
    2036          10 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2037          10 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2038           0 :         return CE_Failure;
    2039             : 
    2040             :     /* ---- Set pixels ---- */
    2041          10 :     size_t ii = 0;
    2042         115 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2043             :     {
    2044        2111 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2045             :         {
    2046             :             // Source raster pixels may be obtained with GetSrcVal macro.
    2047        2006 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2048           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2049        2008 :                                         ? dfNoData
    2050        2004 :                                         : pow(base, dfVal * fact);
    2051             : 
    2052        2006 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2053             :                           static_cast<GByte *>(pData) +
    2054        2006 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2055        2006 :                               iCol * nPixelSpace,
    2056             :                           eBufType, nPixelSpace, 1);
    2057             :         }
    2058             :     }
    2059             : 
    2060             :     /* ---- Return success ---- */
    2061          10 :     return CE_None;
    2062             : }  // ExpPixelFuncHelper
    2063             : 
    2064             : static const char pszExpPixelFuncMetadata[] =
    2065             :     "<PixelFunctionArgumentsList>"
    2066             :     "   <Argument name='base' description='Base' type='double' "
    2067             :     "default='2.7182818284590452353602874713526624' />"
    2068             :     "   <Argument name='fact' description='Factor' type='double' default='1' />"
    2069             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2070             :     "</PixelFunctionArgumentsList>";
    2071             : 
    2072           8 : static CPLErr ExpPixelFunc(void **papoSources, int nSources, void *pData,
    2073             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2074             :                            GDALDataType eBufType, int nPixelSpace,
    2075             :                            int nLineSpace, CSLConstList papszArgs)
    2076             : {
    2077           8 :     double dfBase = 2.7182818284590452353602874713526624;
    2078           8 :     double dfFact = 1.;
    2079             : 
    2080           8 :     if (FetchDoubleArg(papszArgs, "base", &dfBase, &dfBase) != CE_None)
    2081           0 :         return CE_Failure;
    2082             : 
    2083           8 :     if (FetchDoubleArg(papszArgs, "fact", &dfFact, &dfFact) != CE_None)
    2084           0 :         return CE_Failure;
    2085             : 
    2086           8 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2087             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    2088           8 :                               papszArgs, dfBase, dfFact);
    2089             : }  // ExpPixelFunc
    2090             : 
    2091           2 : static CPLErr dB2AmpPixelFunc(void **papoSources, int nSources, void *pData,
    2092             :                               int nXSize, int nYSize, GDALDataType eSrcType,
    2093             :                               GDALDataType eBufType, int nPixelSpace,
    2094             :                               int nLineSpace)
    2095             : {
    2096           2 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2097             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    2098           2 :                               nullptr, 10.0, 1. / 20);
    2099             : }  // dB2AmpPixelFunc
    2100             : 
    2101           2 : static CPLErr dB2PowPixelFunc(void **papoSources, int nSources, void *pData,
    2102             :                               int nXSize, int nYSize, GDALDataType eSrcType,
    2103             :                               GDALDataType eBufType, int nPixelSpace,
    2104             :                               int nLineSpace)
    2105             : {
    2106           2 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2107             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    2108           2 :                               nullptr, 10.0, 1. / 10);
    2109             : }  // dB2PowPixelFunc
    2110             : 
    2111             : static const char pszPowPixelFuncMetadata[] =
    2112             :     "<PixelFunctionArgumentsList>"
    2113             :     "   <Argument name='power' description='Exponent' type='double' "
    2114             :     "mandatory='1' />"
    2115             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2116             :     "</PixelFunctionArgumentsList>";
    2117             : 
    2118           6 : static CPLErr PowPixelFunc(void **papoSources, int nSources, void *pData,
    2119             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2120             :                            GDALDataType eBufType, int nPixelSpace,
    2121             :                            int nLineSpace, CSLConstList papszArgs)
    2122             : {
    2123             :     /* ---- Init ---- */
    2124           6 :     if (nSources != 1)
    2125           0 :         return CE_Failure;
    2126           6 :     if (GDALDataTypeIsComplex(eSrcType))
    2127           0 :         return CE_Failure;
    2128             : 
    2129             :     double power;
    2130           6 :     if (FetchDoubleArg(papszArgs, "power", &power) != CE_None)
    2131           0 :         return CE_Failure;
    2132             : 
    2133           6 :     double dfNoData{0};
    2134           6 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2135           6 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2136           0 :         return CE_Failure;
    2137             : 
    2138             :     /* ---- Set pixels ---- */
    2139           6 :     size_t ii = 0;
    2140          31 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2141             :     {
    2142         431 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2143             :         {
    2144         406 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2145             : 
    2146           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2147         408 :                                         ? dfNoData
    2148         404 :                                         : std::pow(dfVal, power);
    2149             : 
    2150         406 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2151             :                           static_cast<GByte *>(pData) +
    2152         406 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2153         406 :                               iCol * nPixelSpace,
    2154             :                           eBufType, nPixelSpace, 1);
    2155             :         }
    2156             :     }
    2157             : 
    2158             :     /* ---- Return success ---- */
    2159           6 :     return CE_None;
    2160             : }
    2161             : 
    2162             : // Given nt intervals spaced by dt and beginning at t0, return the index of
    2163             : // the lower bound of the interval that should be used to
    2164             : // interpolate/extrapolate a value for t.
    2165          17 : static std::size_t intervalLeft(double t0, double dt, std::size_t nt, double t)
    2166             : {
    2167          17 :     if (t < t0)
    2168             :     {
    2169           4 :         return 0;
    2170             :     }
    2171             : 
    2172          13 :     std::size_t n = static_cast<std::size_t>((t - t0) / dt);
    2173             : 
    2174          13 :     if (n >= nt - 1)
    2175             :     {
    2176           3 :         return nt - 2;
    2177             :     }
    2178             : 
    2179          10 :     return n;
    2180             : }
    2181             : 
    2182          17 : static double InterpolateLinear(double dfX0, double dfX1, double dfY0,
    2183             :                                 double dfY1, double dfX)
    2184             : {
    2185          17 :     return dfY0 + (dfX - dfX0) * (dfY1 - dfY0) / (dfX1 - dfX0);
    2186             : }
    2187             : 
    2188          13 : static double InterpolateExponential(double dfX0, double dfX1, double dfY0,
    2189             :                                      double dfY1, double dfX)
    2190             : {
    2191          13 :     const double r = std::log(dfY1 / dfY0) / (dfX1 - dfX0);
    2192          13 :     return dfY0 * std::exp(r * (dfX - dfX0));
    2193             : }
    2194             : 
    2195             : static const char pszInterpolatePixelFuncMetadata[] =
    2196             :     "<PixelFunctionArgumentsList>"
    2197             :     "   <Argument name='t0' description='t0' type='double' mandatory='1' />"
    2198             :     "   <Argument name='dt' description='dt' type='double' mandatory='1' />"
    2199             :     "   <Argument name='t' description='t' type='double' mandatory='1' />"
    2200             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2201             :     "</PixelFunctionArgumentsList>";
    2202             : 
    2203             : template <decltype(InterpolateLinear) InterpolationFunction>
    2204          17 : CPLErr InterpolatePixelFunc(void **papoSources, int nSources, void *pData,
    2205             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    2206             :                             GDALDataType eBufType, int nPixelSpace,
    2207             :                             int nLineSpace, CSLConstList papszArgs)
    2208             : {
    2209             :     /* ---- Init ---- */
    2210          17 :     if (GDALDataTypeIsComplex(eSrcType))
    2211           0 :         return CE_Failure;
    2212             : 
    2213             :     double dfT0;
    2214          17 :     if (FetchDoubleArg(papszArgs, "t0", &dfT0) == CE_Failure)
    2215           0 :         return CE_Failure;
    2216             : 
    2217             :     double dfT;
    2218          17 :     if (FetchDoubleArg(papszArgs, "t", &dfT) == CE_Failure)
    2219           0 :         return CE_Failure;
    2220             : 
    2221             :     double dfDt;
    2222          17 :     if (FetchDoubleArg(papszArgs, "dt", &dfDt) == CE_Failure)
    2223           0 :         return CE_Failure;
    2224             : 
    2225          17 :     double dfNoData{0};
    2226          17 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2227          17 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2228           0 :         return CE_Failure;
    2229             : 
    2230          17 :     if (nSources < 2)
    2231             :     {
    2232           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2233             :                  "At least two sources required for interpolation.");
    2234           0 :         return CE_Failure;
    2235             :     }
    2236             : 
    2237          17 :     if (dfT == 0 || !std::isfinite(dfT))
    2238             :     {
    2239           0 :         CPLError(CE_Failure, CPLE_AppDefined, "dt must be finite and non-zero");
    2240           0 :         return CE_Failure;
    2241             :     }
    2242             : 
    2243          17 :     const auto i0 = intervalLeft(dfT0, dfDt, nSources, dfT);
    2244          17 :     const auto i1 = i0 + 1;
    2245          17 :     const double dfX0 = dfT0 + static_cast<double>(i0) * dfDt;
    2246          17 :     const double dfX1 = dfT0 + static_cast<double>(i0 + 1) * dfDt;
    2247             : 
    2248             :     /* ---- Set pixels ---- */
    2249          17 :     size_t ii = 0;
    2250          41 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2251             :     {
    2252          72 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2253             :         {
    2254          48 :             const double dfY0 = GetSrcVal(papoSources[i0], eSrcType, ii);
    2255          48 :             const double dfY1 = GetSrcVal(papoSources[i1], eSrcType, ii);
    2256             : 
    2257          48 :             double dfPixVal = dfNoData;
    2258          48 :             if (dfT == dfX0)
    2259           8 :                 dfPixVal = dfY0;
    2260          40 :             else if (dfT == dfX1)
    2261           0 :                 dfPixVal = dfY1;
    2262          52 :             else if (!bHasNoData ||
    2263          12 :                      (!IsNoData(dfY0, dfNoData) && !IsNoData(dfY1, dfNoData)))
    2264          30 :                 dfPixVal = InterpolationFunction(dfX0, dfX1, dfY0, dfY1, dfT);
    2265             : 
    2266          48 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2267             :                           static_cast<GByte *>(pData) +
    2268          48 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2269          48 :                               iCol * nPixelSpace,
    2270             :                           eBufType, nPixelSpace, 1);
    2271             :         }
    2272             :     }
    2273             : 
    2274             :     /* ---- Return success ---- */
    2275          17 :     return CE_None;
    2276             : }
    2277             : 
    2278             : static const char pszReplaceNoDataPixelFuncMetadata[] =
    2279             :     "<PixelFunctionArgumentsList>"
    2280             :     "   <Argument type='builtin' value='NoData' />"
    2281             :     "   <Argument name='to' type='double' description='New NoData value to be "
    2282             :     "replaced' default='nan' />"
    2283             :     "</PixelFunctionArgumentsList>";
    2284             : 
    2285           2 : static CPLErr ReplaceNoDataPixelFunc(void **papoSources, int nSources,
    2286             :                                      void *pData, int nXSize, int nYSize,
    2287             :                                      GDALDataType eSrcType,
    2288             :                                      GDALDataType eBufType, int nPixelSpace,
    2289             :                                      int nLineSpace, CSLConstList papszArgs)
    2290             : {
    2291             :     /* ---- Init ---- */
    2292           2 :     if (nSources != 1)
    2293           0 :         return CE_Failure;
    2294           2 :     if (GDALDataTypeIsComplex(eSrcType))
    2295             :     {
    2296           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2297             :                  "replace_nodata cannot convert complex data types");
    2298           0 :         return CE_Failure;
    2299             :     }
    2300             : 
    2301           2 :     double dfOldNoData, dfNewNoData = NAN;
    2302           2 :     if (FetchDoubleArg(papszArgs, "NoData", &dfOldNoData) != CE_None)
    2303           0 :         return CE_Failure;
    2304           2 :     if (FetchDoubleArg(papszArgs, "to", &dfNewNoData, &dfNewNoData) != CE_None)
    2305           0 :         return CE_Failure;
    2306             : 
    2307           2 :     if (!GDALDataTypeIsFloating(eBufType) && std::isnan(dfNewNoData))
    2308             :     {
    2309           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2310             :                  "Using nan requires a floating point type output buffer");
    2311           0 :         return CE_Failure;
    2312             :     }
    2313             : 
    2314             :     /* ---- Set pixels ---- */
    2315           2 :     size_t ii = 0;
    2316         102 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2317             :     {
    2318        5100 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2319             :         {
    2320        5000 :             double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2321        5000 :             if (dfPixVal == dfOldNoData || std::isnan(dfPixVal))
    2322        3200 :                 dfPixVal = dfNewNoData;
    2323             : 
    2324        5000 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2325             :                           static_cast<GByte *>(pData) +
    2326        5000 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2327        5000 :                               iCol * nPixelSpace,
    2328             :                           eBufType, nPixelSpace, 1);
    2329             :         }
    2330             :     }
    2331             : 
    2332             :     /* ---- Return success ---- */
    2333           2 :     return CE_None;
    2334             : }
    2335             : 
    2336             : static const char pszScalePixelFuncMetadata[] =
    2337             :     "<PixelFunctionArgumentsList>"
    2338             :     "   <Argument type='builtin' value='offset' />"
    2339             :     "   <Argument type='builtin' value='scale' />"
    2340             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2341             :     "</PixelFunctionArgumentsList>";
    2342             : 
    2343           2 : static CPLErr ScalePixelFunc(void **papoSources, int nSources, void *pData,
    2344             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    2345             :                              GDALDataType eBufType, int nPixelSpace,
    2346             :                              int nLineSpace, CSLConstList papszArgs)
    2347             : {
    2348             :     /* ---- Init ---- */
    2349           2 :     if (nSources != 1)
    2350           0 :         return CE_Failure;
    2351           2 :     if (GDALDataTypeIsComplex(eSrcType))
    2352             :     {
    2353           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2354             :                  "scale cannot by applied to complex data types");
    2355           0 :         return CE_Failure;
    2356             :     }
    2357             : 
    2358           2 :     double dfNoData{0};
    2359           2 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2360           2 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2361           0 :         return CE_Failure;
    2362             : 
    2363             :     double dfScale, dfOffset;
    2364           2 :     if (FetchDoubleArg(papszArgs, "scale", &dfScale) != CE_None)
    2365           0 :         return CE_Failure;
    2366           2 :     if (FetchDoubleArg(papszArgs, "offset", &dfOffset) != CE_None)
    2367           0 :         return CE_Failure;
    2368             : 
    2369             :     /* ---- Set pixels ---- */
    2370           2 :     size_t ii = 0;
    2371          23 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2372             :     {
    2373         423 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2374             :         {
    2375         402 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2376             : 
    2377           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2378         404 :                                         ? dfNoData
    2379         400 :                                         : dfVal * dfScale + dfOffset;
    2380             : 
    2381         402 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2382             :                           static_cast<GByte *>(pData) +
    2383         402 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2384         402 :                               iCol * nPixelSpace,
    2385             :                           eBufType, nPixelSpace, 1);
    2386             :         }
    2387             :     }
    2388             : 
    2389             :     /* ---- Return success ---- */
    2390           2 :     return CE_None;
    2391             : }
    2392             : 
    2393             : static const char pszNormDiffPixelFuncMetadata[] =
    2394             :     "<PixelFunctionArgumentsList>"
    2395             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2396             :     "</PixelFunctionArgumentsList>";
    2397             : 
    2398           3 : static CPLErr NormDiffPixelFunc(void **papoSources, int nSources, void *pData,
    2399             :                                 int nXSize, int nYSize, GDALDataType eSrcType,
    2400             :                                 GDALDataType eBufType, int nPixelSpace,
    2401             :                                 int nLineSpace, CSLConstList papszArgs)
    2402             : {
    2403             :     /* ---- Init ---- */
    2404           3 :     if (nSources != 2)
    2405           0 :         return CE_Failure;
    2406             : 
    2407           3 :     if (GDALDataTypeIsComplex(eSrcType))
    2408             :     {
    2409           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2410             :                  "norm_diff cannot by applied to complex data types");
    2411           0 :         return CE_Failure;
    2412             :     }
    2413             : 
    2414           3 :     double dfNoData{0};
    2415           3 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2416           3 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2417           0 :         return CE_Failure;
    2418             : 
    2419             :     /* ---- Set pixels ---- */
    2420           3 :     size_t ii = 0;
    2421          11 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2422             :     {
    2423          42 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2424             :         {
    2425          34 :             const double dfLeftVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2426          34 :             const double dfRightVal = GetSrcVal(papoSources[1], eSrcType, ii);
    2427             : 
    2428          34 :             double dfPixVal = dfNoData;
    2429             : 
    2430          35 :             if (!bHasNoData || (!IsNoData(dfLeftVal, dfNoData) &&
    2431           1 :                                 !IsNoData(dfRightVal, dfNoData)))
    2432             :             {
    2433          30 :                 const double dfDenom = (dfLeftVal + dfRightVal);
    2434             :                 // coverity[divide_by_zero]
    2435          30 :                 dfPixVal =
    2436             :                     dfDenom == 0
    2437          30 :                         ? std::numeric_limits<double>::infinity()
    2438          30 :                         : (dfLeftVal - dfRightVal) /
    2439             : #ifdef __COVERITY__
    2440             :                               (dfDenom + std::numeric_limits<double>::min())
    2441             : #else
    2442             :                               dfDenom
    2443             : #endif
    2444             :                     ;
    2445             :             }
    2446             : 
    2447          34 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2448             :                           static_cast<GByte *>(pData) +
    2449          34 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2450          34 :                               iCol * nPixelSpace,
    2451             :                           eBufType, nPixelSpace, 1);
    2452             :         }
    2453             :     }
    2454             : 
    2455             :     /* ---- Return success ---- */
    2456           3 :     return CE_None;
    2457             : }  // NormDiffPixelFunc
    2458             : 
    2459             : /************************************************************************/
    2460             : /*                     pszMinMaxFuncMetadataNodata                      */
    2461             : /************************************************************************/
    2462             : 
    2463             : static const char pszArgMinMaxFuncMetadataNodata[] =
    2464             :     "<PixelFunctionArgumentsList>"
    2465             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2466             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2467             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2468             :     "default='false' />"
    2469             :     "</PixelFunctionArgumentsList>";
    2470             : 
    2471             : static const char pszMinMaxFuncMetadataNodata[] =
    2472             :     "<PixelFunctionArgumentsList>"
    2473             :     "   <Argument name='k' description='Optional constant term' type='double' "
    2474             :     "default='nan' />"
    2475             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2476             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2477             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2478             :     "default='false' />"
    2479             :     "</PixelFunctionArgumentsList>";
    2480             : 
    2481             : namespace
    2482             : {
    2483             : struct ReturnIndex;
    2484             : struct ReturnValue;
    2485             : }  // namespace
    2486             : 
    2487             : template <class Comparator, class ReturnType = ReturnValue>
    2488          38 : static CPLErr MinOrMaxPixelFunc(double dfK, void **papoSources, int nSources,
    2489             :                                 void *pData, int nXSize, int nYSize,
    2490             :                                 GDALDataType eSrcType, GDALDataType eBufType,
    2491             :                                 int nPixelSpace, int nLineSpace,
    2492             :                                 CSLConstList papszArgs)
    2493             : {
    2494             :     /* ---- Init ---- */
    2495          38 :     if (GDALDataTypeIsComplex(eSrcType))
    2496             :     {
    2497           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2498             :                  "Complex data type not supported for min/max().");
    2499           0 :         return CE_Failure;
    2500             :     }
    2501             : 
    2502          38 :     double dfNoData = std::numeric_limits<double>::quiet_NaN();
    2503          38 :     if (FetchDoubleArg(papszArgs, "NoData", &dfNoData, &dfNoData) != CE_None)
    2504           0 :         return CE_Failure;
    2505          38 :     const bool bPropagateNoData = CPLTestBool(
    2506             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2507             : 
    2508             :     /* ---- Set pixels ---- */
    2509          38 :     size_t ii = 0;
    2510         272 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2511             :     {
    2512       10296 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2513             :         {
    2514       10062 :             double dfRes = std::numeric_limits<double>::quiet_NaN();
    2515       10062 :             double dfResSrc = std::numeric_limits<double>::quiet_NaN();
    2516             : 
    2517       33596 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    2518             :             {
    2519       25596 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    2520             : 
    2521       25596 :                 if (std::isnan(dfVal) || dfVal == dfNoData)
    2522             :                 {
    2523       14425 :                     if (bPropagateNoData)
    2524             :                     {
    2525        2062 :                         dfRes = dfNoData;
    2526             :                         if constexpr (std::is_same_v<ReturnType, ReturnIndex>)
    2527             :                         {
    2528           4 :                             dfResSrc = std::numeric_limits<double>::quiet_NaN();
    2529             :                         }
    2530        2062 :                         break;
    2531             :                     }
    2532             :                 }
    2533       11171 :                 else if (Comparator::compare(dfVal, dfRes))
    2534             :                 {
    2535        6602 :                     dfRes = dfVal;
    2536             :                     if constexpr (std::is_same_v<ReturnType, ReturnIndex>)
    2537             :                     {
    2538           7 :                         dfResSrc = iSrc;
    2539             :                     }
    2540             :                 }
    2541             :             }
    2542             : 
    2543             :             if constexpr (std::is_same_v<ReturnType, ReturnIndex>)
    2544             :             {
    2545             :                 static_cast<void>(dfK);  // Placate gcc 9.4
    2546          12 :                 dfRes = std::isnan(dfResSrc) ? dfNoData : dfResSrc + 1;
    2547             :             }
    2548             :             else
    2549             :             {
    2550       10050 :                 if (std::isnan(dfRes))
    2551             :                 {
    2552        3211 :                     dfRes = dfNoData;
    2553             :                 }
    2554             : 
    2555       10050 :                 if (IsNoData(dfRes, dfNoData))
    2556             :                 {
    2557        5269 :                     if (!bPropagateNoData && !std::isnan(dfK))
    2558             :                     {
    2559           6 :                         dfRes = dfK;
    2560             :                     }
    2561             :                 }
    2562        4781 :                 else if (!std::isnan(dfK) && Comparator::compare(dfK, dfRes))
    2563             :                 {
    2564          10 :                     dfRes = dfK;
    2565             :                 }
    2566             :             }
    2567             : 
    2568       10062 :             GDALCopyWords(&dfRes, GDT_Float64, 0,
    2569             :                           static_cast<GByte *>(pData) +
    2570       10062 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2571       10062 :                               iCol * nPixelSpace,
    2572             :                           eBufType, nPixelSpace, 1);
    2573             :         }
    2574             :     }
    2575             : 
    2576             :     /* ---- Return success ---- */
    2577          38 :     return CE_None;
    2578             : } /* MinOrMaxPixelFunc */
    2579             : 
    2580             : #ifdef USE_SSE2
    2581             : 
    2582             : template <class T, class SSEWrapper>
    2583          27 : static void OptimizedMinOrMaxSSE2(const void *const *papoSources, int nSources,
    2584             :                                   void *pData, int nXSize, int nYSize,
    2585             :                                   int nLineSpace)
    2586             : {
    2587          27 :     assert(nSources >= 1);
    2588          27 :     constexpr int VALUES_PER_REG =
    2589             :         static_cast<int>(sizeof(typename SSEWrapper::Vec) / sizeof(T));
    2590         593 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2591             :     {
    2592         566 :         T *CPL_RESTRICT pDest =
    2593             :             reinterpret_cast<T *>(static_cast<GByte *>(pData) +
    2594         566 :                                   static_cast<GSpacing>(nLineSpace) * iLine);
    2595         566 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    2596         566 :         int iCol = 0;
    2597        3116 :         for (; iCol < nXSize - (2 * VALUES_PER_REG - 1);
    2598             :              iCol += 2 * VALUES_PER_REG)
    2599             :         {
    2600        2550 :             auto reg0 = SSEWrapper::LoadU(
    2601        2550 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    2602        2550 :                 iOffsetLine + iCol);
    2603        2550 :             auto reg1 = SSEWrapper::LoadU(
    2604        2550 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    2605        2550 :                 iOffsetLine + iCol + VALUES_PER_REG);
    2606        5150 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    2607             :             {
    2608        2600 :                 reg0 = SSEWrapper::MinOrMax(
    2609        2600 :                     reg0, SSEWrapper::LoadU(static_cast<const T * CPL_RESTRICT>(
    2610        2600 :                                                 papoSources[iSrc]) +
    2611        2600 :                                             iOffsetLine + iCol));
    2612        2600 :                 reg1 = SSEWrapper::MinOrMax(
    2613             :                     reg1,
    2614             :                     SSEWrapper::LoadU(
    2615        2600 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    2616        2600 :                         iOffsetLine + iCol + VALUES_PER_REG));
    2617             :             }
    2618        2550 :             SSEWrapper::StoreU(pDest + iCol, reg0);
    2619        2550 :             SSEWrapper::StoreU(pDest + iCol + VALUES_PER_REG, reg1);
    2620             :         }
    2621        4086 :         for (; iCol < nXSize; ++iCol)
    2622             :         {
    2623        3520 :             T v = static_cast<const T * CPL_RESTRICT>(
    2624        3520 :                 papoSources[0])[iOffsetLine + iCol];
    2625        7946 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    2626             :             {
    2627        4426 :                 v = SSEWrapper::MinOrMax(
    2628        4426 :                     v, static_cast<const T * CPL_RESTRICT>(
    2629        4426 :                            papoSources[iSrc])[iOffsetLine + iCol]);
    2630             :             }
    2631        3520 :             pDest[iCol] = v;
    2632             :         }
    2633             :     }
    2634          27 : }
    2635             : 
    2636             : // clang-format off
    2637             : namespace
    2638             : {
    2639             : struct SSEWrapperMinByte
    2640             : {
    2641             :     using T = uint8_t;
    2642             :     typedef __m128i Vec;
    2643             : 
    2644         400 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2645         100 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2646         200 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epu8(x, y); }
    2647         906 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2648             : };
    2649             : 
    2650             : struct SSEWrapperMaxByte
    2651             : {
    2652             :     using T = uint8_t;
    2653             :     typedef __m128i Vec;
    2654             : 
    2655        1000 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2656         200 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2657         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epu8(x, y); }
    2658        2706 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2659             : };
    2660             : 
    2661             : struct SSEWrapperMinUInt16
    2662             : {
    2663             :     using T = uint16_t;
    2664             :     typedef __m128i Vec;
    2665             : 
    2666        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2667         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2668             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
    2669             :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epu16(x, y); }
    2670             : #else
    2671         300 :     static inline Vec MinOrMax(Vec x, Vec y) { return
    2672        1800 :         _mm_add_epi16(
    2673             :            _mm_min_epi16(
    2674             :              _mm_add_epi16(x, _mm_set1_epi16(-32768)),
    2675             :              _mm_add_epi16(y, _mm_set1_epi16(-32768))),
    2676         300 :            _mm_set1_epi16(-32768)); }
    2677             : #endif
    2678         100 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2679             : };
    2680             : 
    2681             : struct SSEWrapperMaxUInt16
    2682             : {
    2683             :     using T = uint16_t;
    2684             :     typedef __m128i Vec;
    2685             : 
    2686        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2687         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2688             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
    2689             :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epu16(x, y); }
    2690             : #else
    2691         300 :     static inline Vec MinOrMax(Vec x, Vec y) { return
    2692        1800 :         _mm_add_epi16(
    2693             :            _mm_max_epi16(
    2694             :              _mm_add_epi16(x, _mm_set1_epi16(-32768)),
    2695             :              _mm_add_epi16(y, _mm_set1_epi16(-32768))),
    2696         300 :            _mm_set1_epi16(-32768)); }
    2697             : #endif
    2698         100 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2699             : };
    2700             : 
    2701             : struct SSEWrapperMinInt16
    2702             : {
    2703             :     using T = int16_t;
    2704             :     typedef __m128i Vec;
    2705             : 
    2706        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2707         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2708         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epi16(x, y); }
    2709         100 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2710             : };
    2711             : 
    2712             : struct SSEWrapperMaxInt16
    2713             : {
    2714             :     using T = int16_t;
    2715             :     typedef __m128i Vec;
    2716             : 
    2717        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2718         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2719         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epi16(x, y); }
    2720         100 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2721             : };
    2722             : 
    2723             : struct SSEWrapperMinFloat
    2724             : {
    2725             :     using T = float;
    2726             :     typedef __m128 Vec;
    2727             : 
    2728        2400 :     static inline Vec LoadU(const T *x) { return _mm_loadu_ps(x); }
    2729         600 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_ps(x, y); }
    2730        1200 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_ps(x, y); }
    2731         100 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2732             : };
    2733             : 
    2734             : struct SSEWrapperMaxFloat
    2735             : {
    2736             :     using T = float;
    2737             :     typedef __m128 Vec;
    2738             : 
    2739        2400 :     static inline Vec LoadU(const T *x) { return _mm_loadu_ps(x); }
    2740         600 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_ps(x, y); }
    2741        1200 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_ps(x, y); }
    2742         100 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2743             : };
    2744             : 
    2745             : struct SSEWrapperMinDouble
    2746             : {
    2747             :     using T = double;
    2748             :     typedef __m128d Vec;
    2749             : 
    2750        4800 :     static inline Vec LoadU(const T *x) { return _mm_loadu_pd(x); }
    2751        1200 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_pd(x, y); }
    2752        2400 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_pd(x, y); }
    2753         107 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2754             : };
    2755             : 
    2756             : struct SSEWrapperMaxDouble
    2757             : {
    2758             :     using T = double;
    2759             :     typedef __m128d Vec;
    2760             : 
    2761        4800 :     static inline Vec LoadU(const T *x) { return _mm_loadu_pd(x); }
    2762        1200 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_pd(x, y); }
    2763        2400 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_pd(x, y); }
    2764         107 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2765             : };
    2766             : 
    2767             : }  // namespace
    2768             : 
    2769             : // clang-format on
    2770             : 
    2771             : #endif  // USE_SSE2
    2772             : 
    2773             : template <typename ReturnType>
    2774          35 : static CPLErr MinPixelFunc(void **papoSources, int nSources, void *pData,
    2775             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2776             :                            GDALDataType eBufType, int nPixelSpace,
    2777             :                            int nLineSpace, CSLConstList papszArgs)
    2778             : {
    2779             :     struct Comparator
    2780             :     {
    2781        2750 :         static bool compare(double x, double resVal)
    2782             :         {
    2783             :             // Written this way to deal with resVal being NaN
    2784        2750 :             return !(x >= resVal);
    2785             :         }
    2786             :     };
    2787             : 
    2788          35 :     double dfK = std::numeric_limits<double>::quiet_NaN();
    2789             :     if constexpr (std::is_same_v<ReturnType, ReturnValue>)
    2790             :     {
    2791          32 :         if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    2792           0 :             return CE_Failure;
    2793             : 
    2794             : #ifdef USE_SSE2
    2795          32 :         const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2796          54 :         if (std::isnan(dfK) && nSources > 0 && !bHasNoData &&
    2797          54 :             eSrcType == eBufType &&
    2798          13 :             nPixelSpace == GDALGetDataTypeSizeBytes(eSrcType))
    2799             :         {
    2800          13 :             if (eSrcType == GDT_UInt8)
    2801             :             {
    2802           4 :                 OptimizedMinOrMaxSSE2<uint8_t, SSEWrapperMinByte>(
    2803             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2804           4 :                 return CE_None;
    2805             :             }
    2806           9 :             else if (eSrcType == GDT_UInt16)
    2807             :             {
    2808           1 :                 OptimizedMinOrMaxSSE2<uint16_t, SSEWrapperMinUInt16>(
    2809             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2810           1 :                 return CE_None;
    2811             :             }
    2812           8 :             else if (eSrcType == GDT_Int16)
    2813             :             {
    2814           1 :                 OptimizedMinOrMaxSSE2<int16_t, SSEWrapperMinInt16>(
    2815             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2816           1 :                 return CE_None;
    2817             :             }
    2818           7 :             else if (eSrcType == GDT_Float32)
    2819             :             {
    2820           1 :                 OptimizedMinOrMaxSSE2<float, SSEWrapperMinFloat>(
    2821             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2822           1 :                 return CE_None;
    2823             :             }
    2824           6 :             else if (eSrcType == GDT_Float64)
    2825             :             {
    2826           6 :                 OptimizedMinOrMaxSSE2<double, SSEWrapperMinDouble>(
    2827             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2828           6 :                 return CE_None;
    2829             :             }
    2830             :         }
    2831             : #endif
    2832             :     }
    2833             : 
    2834          22 :     return MinOrMaxPixelFunc<Comparator, ReturnType>(
    2835             :         dfK, papoSources, nSources, pData, nXSize, nYSize, eSrcType, eBufType,
    2836          25 :         nPixelSpace, nLineSpace, papszArgs);
    2837             : }
    2838             : 
    2839             : template <typename ReturnType>
    2840          30 : static CPLErr MaxPixelFunc(void **papoSources, int nSources, void *pData,
    2841             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2842             :                            GDALDataType eBufType, int nPixelSpace,
    2843             :                            int nLineSpace, CSLConstList papszArgs)
    2844             : {
    2845             :     struct Comparator
    2846             :     {
    2847        8441 :         static bool compare(double x, double resVal)
    2848             :         {
    2849             :             // Written this way to deal with resVal being NaN
    2850        8441 :             return !(x <= resVal);
    2851             :         }
    2852             :     };
    2853             : 
    2854          30 :     double dfK = std::numeric_limits<double>::quiet_NaN();
    2855             :     if constexpr (std::is_same_v<ReturnType, ReturnValue>)
    2856             :     {
    2857          27 :         if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    2858           0 :             return CE_Failure;
    2859             : 
    2860             : #ifdef USE_SSE2
    2861          27 :         const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2862          46 :         if (std::isnan(dfK) && nSources > 0 && !bHasNoData &&
    2863          46 :             eSrcType == eBufType &&
    2864          14 :             nPixelSpace == GDALGetDataTypeSizeBytes(eSrcType))
    2865             :         {
    2866          14 :             if (eSrcType == GDT_UInt8)
    2867             :             {
    2868           5 :                 OptimizedMinOrMaxSSE2<uint8_t, SSEWrapperMaxByte>(
    2869             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2870           5 :                 return CE_None;
    2871             :             }
    2872           9 :             else if (eSrcType == GDT_UInt16)
    2873             :             {
    2874           1 :                 OptimizedMinOrMaxSSE2<uint16_t, SSEWrapperMaxUInt16>(
    2875             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2876           1 :                 return CE_None;
    2877             :             }
    2878           8 :             else if (eSrcType == GDT_Int16)
    2879             :             {
    2880           1 :                 OptimizedMinOrMaxSSE2<int16_t, SSEWrapperMaxInt16>(
    2881             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2882           1 :                 return CE_None;
    2883             :             }
    2884           7 :             else if (eSrcType == GDT_Float32)
    2885             :             {
    2886           1 :                 OptimizedMinOrMaxSSE2<float, SSEWrapperMaxFloat>(
    2887             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2888           1 :                 return CE_None;
    2889             :             }
    2890           6 :             else if (eSrcType == GDT_Float64)
    2891             :             {
    2892           6 :                 OptimizedMinOrMaxSSE2<double, SSEWrapperMaxDouble>(
    2893             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2894           6 :                 return CE_None;
    2895             :             }
    2896             :         }
    2897             : #endif
    2898             :     }
    2899             : 
    2900          16 :     return MinOrMaxPixelFunc<Comparator, ReturnType>(
    2901             :         dfK, papoSources, nSources, pData, nXSize, nYSize, eSrcType, eBufType,
    2902          19 :         nPixelSpace, nLineSpace, papszArgs);
    2903             : }
    2904             : 
    2905             : static const char pszExprPixelFuncMetadata[] =
    2906             :     "<PixelFunctionArgumentsList>"
    2907             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2908             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2909             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2910             :     "default='false' />"
    2911             :     "   <Argument name='expression' "
    2912             :     "             description='Expression to be evaluated' "
    2913             :     "             type='string'></Argument>"
    2914             :     "   <Argument name='dialect' "
    2915             :     "             description='Expression dialect' "
    2916             :     "             type='string-select'"
    2917             :     "             default='muparser'>"
    2918             :     "       <Value>exprtk</Value>"
    2919             :     "       <Value>muparser</Value>"
    2920             :     "   </Argument>"
    2921             :     "   <Argument type='builtin' value='source_names' />"
    2922             :     "   <Argument type='builtin' value='xoff' />"
    2923             :     "   <Argument type='builtin' value='yoff' />"
    2924             :     "   <Argument type='builtin' value='geotransform' />"
    2925             :     "</PixelFunctionArgumentsList>";
    2926             : 
    2927         375 : static CPLErr ExprPixelFunc(void **papoSources, int nSources, void *pData,
    2928             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    2929             :                             GDALDataType eBufType, int nPixelSpace,
    2930             :                             int nLineSpace, CSLConstList papszArgs)
    2931             : {
    2932             :     /* ---- Init ---- */
    2933         375 :     if (GDALDataTypeIsComplex(eSrcType))
    2934             :     {
    2935           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2936             :                  "expression cannot by applied to complex data types");
    2937           0 :         return CE_Failure;
    2938             :     }
    2939             : 
    2940         375 :     double dfNoData{0};
    2941         375 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2942         375 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2943           0 :         return CE_Failure;
    2944             : 
    2945         375 :     const bool bPropagateNoData = CPLTestBool(
    2946             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2947             : 
    2948         375 :     const char *pszExpression = CSLFetchNameValue(papszArgs, "expression");
    2949         375 :     if (!pszExpression)
    2950             :     {
    2951           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2952             :                  "Missing 'expression' pixel function argument");
    2953           1 :         return CE_Failure;
    2954             :     }
    2955             : 
    2956         374 :     const char *pszSourceNames = CSLFetchNameValue(papszArgs, "source_names");
    2957             :     const CPLStringList aosSourceNames(
    2958         748 :         CSLTokenizeString2(pszSourceNames, "|", 0));
    2959         374 :     if (aosSourceNames.size() != nSources)
    2960             :     {
    2961           7 :         CPLError(CE_Failure, CPLE_AppDefined,
    2962             :                  "The source_names variable passed to ExprPixelFunc() has %d "
    2963             :                  "values, whereas %d were expected. An invalid variable name "
    2964             :                  "has likely been used",
    2965             :                  aosSourceNames.size(), nSources);
    2966           7 :         return CE_Failure;
    2967             :     }
    2968             : 
    2969         734 :     std::vector<double> adfValuesForPixel(nSources);
    2970             : 
    2971         367 :     const char *pszDialect = CSLFetchNameValue(papszArgs, "dialect");
    2972         367 :     if (!pszDialect)
    2973             :     {
    2974         230 :         pszDialect = "muparser";
    2975             :     }
    2976             : 
    2977         734 :     auto poExpression = gdal::MathExpression::Create(pszExpression, pszDialect);
    2978             : 
    2979             :     // cppcheck-suppress knownConditionTrueFalse
    2980         367 :     if (!poExpression)
    2981             :     {
    2982           0 :         return CE_Failure;
    2983             :     }
    2984             : 
    2985         367 :     int nXOff = 0;
    2986         367 :     int nYOff = 0;
    2987         367 :     GDALGeoTransform gt;
    2988         367 :     double dfCenterX = 0;
    2989         367 :     double dfCenterY = 0;
    2990             : 
    2991         367 :     bool includeCenterCoords = false;
    2992         367 :     if (strstr(pszExpression, "_CENTER_X_") ||
    2993         365 :         strstr(pszExpression, "_CENTER_Y_"))
    2994             :     {
    2995           2 :         includeCenterCoords = true;
    2996             : 
    2997           2 :         const char *pszXOff = CSLFetchNameValue(papszArgs, "xoff");
    2998           2 :         nXOff = std::atoi(pszXOff);
    2999             : 
    3000           2 :         const char *pszYOff = CSLFetchNameValue(papszArgs, "yoff");
    3001           2 :         nYOff = std::atoi(pszYOff);
    3002             : 
    3003           2 :         const char *pszGT = CSLFetchNameValue(papszArgs, "geotransform");
    3004           2 :         if (pszGT == nullptr)
    3005             :         {
    3006           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    3007             :                      "To use _CENTER_X_ or _CENTER_Y_ in an expression, "
    3008             :                      "VRTDataset must have a <GeoTransform> element.");
    3009           1 :             return CE_Failure;
    3010             :         }
    3011             : 
    3012           1 :         if (!gt.Init(pszGT))
    3013             :         {
    3014           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3015             :                      "Invalid GeoTransform argument");
    3016           0 :             return CE_Failure;
    3017             :         }
    3018             :     }
    3019             : 
    3020             :     {
    3021         366 :         int iSource = 0;
    3022         979 :         for (const auto &osName : aosSourceNames)
    3023             :         {
    3024        1226 :             poExpression->RegisterVariable(osName,
    3025         613 :                                            &adfValuesForPixel[iSource++]);
    3026             :         }
    3027             :     }
    3028             : 
    3029         366 :     if (includeCenterCoords)
    3030             :     {
    3031           1 :         poExpression->RegisterVariable("_CENTER_X_", &dfCenterX);
    3032           1 :         poExpression->RegisterVariable("_CENTER_Y_", &dfCenterY);
    3033             :     }
    3034             : 
    3035         366 :     if (bHasNoData)
    3036             :     {
    3037          10 :         poExpression->RegisterVariable("NODATA", &dfNoData);
    3038             :     }
    3039             : 
    3040         366 :     if (strstr(pszExpression, "BANDS"))
    3041             :     {
    3042           2 :         poExpression->RegisterVector("BANDS", &adfValuesForPixel);
    3043             :     }
    3044             : 
    3045             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    3046         732 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    3047         366 :     if (!padfResults)
    3048           0 :         return CE_Failure;
    3049             : 
    3050             :     /* ---- Set pixels ---- */
    3051         366 :     size_t ii = 0;
    3052        5144 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3053             :     {
    3054    12996900 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    3055             :         {
    3056    12992100 :             double &dfResult = padfResults.get()[iCol];
    3057    12992100 :             bool resultIsNoData = false;
    3058             : 
    3059    38979800 :             for (int iSrc = 0; iSrc < nSources; iSrc++)
    3060             :             {
    3061             :                 // cppcheck-suppress unreadVariable
    3062    25987700 :                 double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    3063             : 
    3064    25987700 :                 if (bHasNoData && bPropagateNoData && IsNoData(dfVal, dfNoData))
    3065             :                 {
    3066           1 :                     resultIsNoData = true;
    3067             :                 }
    3068             : 
    3069    25987700 :                 adfValuesForPixel[iSrc] = dfVal;
    3070             :             }
    3071             : 
    3072    12992100 :             if (includeCenterCoords)
    3073             :             {
    3074             :                 // Add 0.5 to pixel / line to move from pixel corner to cell center
    3075         400 :                 gt.Apply(static_cast<double>(iCol + nXOff) + 0.5,
    3076         400 :                          static_cast<double>(iLine + nYOff) + 0.5, &dfCenterX,
    3077             :                          &dfCenterY);
    3078             :             }
    3079             : 
    3080    12992100 :             if (resultIsNoData)
    3081             :             {
    3082           1 :                 dfResult = dfNoData;
    3083             :             }
    3084             :             else
    3085             :             {
    3086    12992100 :                 if (auto eErr = poExpression->Evaluate(); eErr != CE_None)
    3087             :                 {
    3088           5 :                     return CE_Failure;
    3089             :                 }
    3090             : 
    3091    12992100 :                 dfResult = poExpression->Results()[0];
    3092             :             }
    3093             :         }
    3094             : 
    3095        4778 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    3096             :                       static_cast<GByte *>(pData) +
    3097        4778 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    3098             :                       eBufType, nPixelSpace, nXSize);
    3099             :     }
    3100             : 
    3101             :     /* ---- Return success ---- */
    3102         361 :     return CE_None;
    3103             : }  // ExprPixelFunc
    3104             : 
    3105             : static constexpr char pszAreaPixelFuncMetadata[] =
    3106             :     "<PixelFunctionArgumentsList>"
    3107             :     "   <Argument type='builtin' value='crs' />"
    3108             :     "   <Argument type='builtin' value='xoff' />"
    3109             :     "   <Argument type='builtin' value='yoff' />"
    3110             :     "   <Argument type='builtin' value='geotransform' />"
    3111             :     "</PixelFunctionArgumentsList>";
    3112             : 
    3113           4 : static CPLErr AreaPixelFunc(void ** /*papoSources*/, int nSources, void *pData,
    3114             :                             int nXSize, int nYSize, GDALDataType /* eSrcType */,
    3115             :                             GDALDataType eBufType, int nPixelSpace,
    3116             :                             int nLineSpace, CSLConstList papszArgs)
    3117             : {
    3118           4 :     if (nSources)
    3119             :     {
    3120           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3121             :                  "area: unexpected source band(s)");
    3122           1 :         return CE_Failure;
    3123             :     }
    3124             : 
    3125           3 :     const char *pszGT = CSLFetchNameValue(papszArgs, "geotransform");
    3126           3 :     if (pszGT == nullptr)
    3127             :     {
    3128           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3129             :                  "area: VRTDataset has no <GeoTransform>");
    3130           1 :         return CE_Failure;
    3131             :     }
    3132             : 
    3133           2 :     GDALGeoTransform gt;
    3134           2 :     if (!gt.Init(pszGT))
    3135             :     {
    3136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3137             :                  "area: Invalid GeoTransform argument");
    3138           0 :         return CE_Failure;
    3139             :     }
    3140             : 
    3141           2 :     const char *pszXOff = CSLFetchNameValue(papszArgs, "xoff");
    3142           2 :     const int nXOff = std::atoi(pszXOff);
    3143             : 
    3144           2 :     const char *pszYOff = CSLFetchNameValue(papszArgs, "yoff");
    3145           2 :     const int nYOff = std::atoi(pszYOff);
    3146             : 
    3147           2 :     const char *pszCrsPtr = CSLFetchNameValue(papszArgs, "crs");
    3148             : 
    3149           2 :     if (!pszCrsPtr)
    3150             :     {
    3151           0 :         CPLError(CE_Failure, CPLE_AppDefined, "area: VRTDataset has no <SRS>");
    3152           0 :         return CE_Failure;
    3153             :     }
    3154             : 
    3155           2 :     std::uintptr_t nCrsPtr = 0;
    3156           2 :     if (auto [end, ec] =
    3157           2 :             std::from_chars(pszCrsPtr, pszCrsPtr + strlen(pszCrsPtr), nCrsPtr);
    3158             :         ec != std::errc())
    3159             :     {
    3160             :         // Since "crs" is populated by GDAL, this should never happen.
    3161           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read CRS");
    3162           0 :         return CE_Failure;
    3163             :     }
    3164             : 
    3165           2 :     const OGRSpatialReference *poCRS =
    3166             :         reinterpret_cast<const OGRSpatialReference *>(nCrsPtr);
    3167             : 
    3168           2 :     if (!poCRS)
    3169             :     {
    3170             :         // can't get here, but cppcheck doesn't know that
    3171           0 :         return CE_Failure;
    3172             :     }
    3173             : 
    3174           2 :     const OGRSpatialReference *poGeographicCRS = nullptr;
    3175           2 :     std::unique_ptr<OGRSpatialReference> poGeographicCRSHolder;
    3176           2 :     std::unique_ptr<OGRCoordinateTransformation> poTransform;
    3177             : 
    3178           2 :     if (!poCRS->IsGeographic())
    3179             :     {
    3180           1 :         poGeographicCRSHolder = std::make_unique<OGRSpatialReference>();
    3181           1 :         if (poGeographicCRSHolder->CopyGeogCSFrom(poCRS) != OGRERR_NONE)
    3182             :         {
    3183           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3184             :                      "Cannot reproject geometry to geographic CRS");
    3185           0 :             return CE_Failure;
    3186             :         }
    3187           1 :         poGeographicCRSHolder->SetAxisMappingStrategy(
    3188             :             OAMS_TRADITIONAL_GIS_ORDER);
    3189             : 
    3190           1 :         poTransform.reset(OGRCreateCoordinateTransformation(
    3191           1 :             poCRS, poGeographicCRSHolder.get()));
    3192             : 
    3193           1 :         if (!poTransform)
    3194             :         {
    3195           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3196             :                      "Cannot reproject geometry to geographic CRS");
    3197           0 :             return CE_Failure;
    3198             :         }
    3199             : 
    3200           1 :         poGeographicCRS = poGeographicCRSHolder.get();
    3201             :     }
    3202             :     else
    3203             :     {
    3204           1 :         poGeographicCRS = poCRS;
    3205             :     }
    3206             : 
    3207           2 :     geod_geodesic g{};
    3208           2 :     OGRErr eErr = OGRERR_NONE;
    3209           2 :     double dfSemiMajor = poGeographicCRS->GetSemiMajor(&eErr);
    3210           2 :     if (eErr != OGRERR_NONE)
    3211           0 :         return CE_Failure;
    3212           2 :     const double dfInvFlattening = poGeographicCRS->GetInvFlattening(&eErr);
    3213           2 :     if (eErr != OGRERR_NONE)
    3214           0 :         return CE_Failure;
    3215           2 :     geod_init(&g, dfSemiMajor,
    3216             :               dfInvFlattening != 0 ? 1.0 / dfInvFlattening : 0.0);
    3217             : 
    3218           2 :     std::array<double, 5> adfLon{};
    3219           2 :     std::array<double, 5> adfLat{};
    3220             : 
    3221           2 :     size_t ii = 0;
    3222          22 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3223             :     {
    3224         420 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    3225             :         {
    3226         400 :             gt.Apply(static_cast<double>(iCol + nXOff),
    3227         400 :                      static_cast<double>(iLine + nYOff), &adfLon[0],
    3228         400 :                      &adfLat[0]);
    3229         400 :             gt.Apply(static_cast<double>(iCol + nXOff + 1),
    3230         400 :                      static_cast<double>(iLine + nYOff), &adfLon[1],
    3231         400 :                      &adfLat[1]);
    3232         400 :             gt.Apply(static_cast<double>(iCol + nXOff + 1),
    3233         400 :                      static_cast<double>(iLine + nYOff + 1), &adfLon[2],
    3234         400 :                      &adfLat[2]);
    3235         400 :             gt.Apply(static_cast<double>(iCol + nXOff),
    3236         400 :                      static_cast<double>(iLine + nYOff + 1), &adfLon[3],
    3237         400 :                      &adfLat[3]);
    3238         400 :             adfLon[4] = adfLon[0];
    3239         400 :             adfLat[4] = adfLat[0];
    3240             : 
    3241         600 :             if (poTransform &&
    3242         200 :                 !poTransform->Transform(adfLon.size(), adfLon.data(),
    3243         400 :                                         adfLat.data(), nullptr))
    3244             :             {
    3245           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3246             :                          "Failed to reproject cell corners to geographic CRS");
    3247           0 :                 return CE_Failure;
    3248             :             }
    3249             : 
    3250         400 :             double dfArea = -1.0;
    3251         400 :             geod_polygonarea(&g, adfLat.data(), adfLon.data(),
    3252         400 :                              static_cast<int>(adfLat.size()), &dfArea, nullptr);
    3253         400 :             dfArea = std::fabs(dfArea);
    3254             : 
    3255         400 :             GDALCopyWords(&dfArea, GDT_Float64, 0,
    3256             :                           static_cast<GByte *>(pData) +
    3257         400 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    3258         400 :                               iCol * nPixelSpace,
    3259             :                           eBufType, nPixelSpace, 1);
    3260             :         }
    3261             :     }
    3262             : 
    3263           2 :     return CE_None;
    3264             : }  // AreaPixelFunc
    3265             : 
    3266             : static const char pszReclassifyPixelFuncMetadata[] =
    3267             :     "<PixelFunctionArgumentsList>"
    3268             :     "   <Argument name='mapping' "
    3269             :     "             description='Lookup table for mapping, in format "
    3270             :     "from=to,from=to' "
    3271             :     "             type='string'></Argument>"
    3272             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3273             :     "</PixelFunctionArgumentsList>";
    3274             : 
    3275          33 : static CPLErr ReclassifyPixelFunc(void **papoSources, int nSources, void *pData,
    3276             :                                   int nXSize, int nYSize, GDALDataType eSrcType,
    3277             :                                   GDALDataType eBufType, int nPixelSpace,
    3278             :                                   int nLineSpace, CSLConstList papszArgs)
    3279             : {
    3280          33 :     if (GDALDataTypeIsComplex(eSrcType))
    3281             :     {
    3282           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3283             :                  "reclassify cannot by applied to complex data types");
    3284           0 :         return CE_Failure;
    3285             :     }
    3286             : 
    3287          33 :     if (nSources != 1)
    3288             :     {
    3289           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3290             :                  "reclassify only be applied to a single source at a time");
    3291           0 :         return CE_Failure;
    3292             :     }
    3293          33 :     std::optional<double> noDataValue{};
    3294             : 
    3295          33 :     const char *pszNoData = CSLFetchNameValue(papszArgs, "NoData");
    3296          33 :     if (pszNoData != nullptr)
    3297             :     {
    3298          10 :         noDataValue = CPLAtof(pszNoData);
    3299             :     }
    3300             : 
    3301          33 :     const char *pszMappings = CSLFetchNameValue(papszArgs, "mapping");
    3302          33 :     if (pszMappings == nullptr)
    3303             :     {
    3304           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3305             :                  "reclassify must be called with 'mapping' argument");
    3306           0 :         return CE_Failure;
    3307             :     }
    3308             : 
    3309          66 :     gdal::Reclassifier oReclassifier;
    3310          33 :     if (auto eErr = oReclassifier.Init(pszMappings, noDataValue, eBufType);
    3311             :         eErr != CE_None)
    3312             :     {
    3313          14 :         return eErr;
    3314             :     }
    3315             : 
    3316             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    3317          38 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    3318          19 :     if (!padfResults)
    3319           0 :         return CE_Failure;
    3320             : 
    3321          19 :     size_t ii = 0;
    3322          19 :     bool bSuccess = false;
    3323         435 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3324             :     {
    3325       20805 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    3326             :         {
    3327       20389 :             double srcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    3328       40778 :             padfResults.get()[iCol] =
    3329       20389 :                 oReclassifier.Reclassify(srcVal, bSuccess);
    3330       20389 :             if (!bSuccess)
    3331             :             {
    3332           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3333             :                          "Encountered value %g with no specified mapping",
    3334             :                          srcVal);
    3335           2 :                 return CE_Failure;
    3336             :             }
    3337             :         }
    3338             : 
    3339         416 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    3340             :                       static_cast<GByte *>(pData) +
    3341         416 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    3342             :                       eBufType, nPixelSpace, nXSize);
    3343             :     }
    3344             : 
    3345          17 :     return CE_None;
    3346             : }  // ReclassifyPixelFunc
    3347             : 
    3348             : struct MeanKernel
    3349             : {
    3350             :     static constexpr const char *pszName = "mean";
    3351             : 
    3352             :     double dfMean = 0;
    3353             :     int nValidSources = 0;
    3354             : 
    3355         568 :     void Reset()
    3356             :     {
    3357         568 :         dfMean = 0;
    3358         568 :         nValidSources = 0;
    3359         568 :     }
    3360             : 
    3361          93 :     static CPLErr ProcessArguments(CSLConstList)
    3362             :     {
    3363          93 :         return CE_None;
    3364             :     }
    3365             : 
    3366        1375 :     void ProcessPixel(double dfVal)
    3367             :     {
    3368        1375 :         ++nValidSources;
    3369             : 
    3370        1375 :         if (CPL_UNLIKELY(std::isinf(dfVal)))
    3371             :         {
    3372         620 :             if (nValidSources == 1)
    3373             :             {
    3374         310 :                 dfMean = dfVal;
    3375             :             }
    3376         310 :             else if (dfVal == -dfMean)
    3377             :             {
    3378          62 :                 dfMean = std::numeric_limits<double>::quiet_NaN();
    3379             :             }
    3380             :         }
    3381         755 :         else if (CPL_UNLIKELY(std::isinf(dfMean)))
    3382             :         {
    3383         186 :             if (!std::isfinite(dfVal))
    3384             :             {
    3385          62 :                 dfMean = std::numeric_limits<double>::quiet_NaN();
    3386             :             }
    3387             :         }
    3388             :         else
    3389             :         {
    3390         569 :             const double delta = dfVal - dfMean;
    3391         569 :             if (CPL_UNLIKELY(std::isinf(delta)))
    3392           0 :                 dfMean += dfVal / nValidSources - dfMean / nValidSources;
    3393             :             else
    3394         569 :                 dfMean += delta / nValidSources;
    3395             :         }
    3396        1375 :     }
    3397             : 
    3398         566 :     bool HasValue() const
    3399             :     {
    3400         566 :         return nValidSources > 0;
    3401             :     }
    3402             : 
    3403         563 :     double GetValue() const
    3404             :     {
    3405         563 :         return dfMean;
    3406             :     }
    3407             : };
    3408             : 
    3409             : struct GeoMeanKernel
    3410             : {
    3411             :     static constexpr const char *pszName = "geometric_mean";
    3412             : 
    3413             :     double dfProduct = 1;
    3414             :     int nValidSources = 0;
    3415             : 
    3416           6 :     void Reset()
    3417             :     {
    3418           6 :         dfProduct = 1;
    3419           6 :         nValidSources = 0;
    3420           6 :     }
    3421             : 
    3422           3 :     static CPLErr ProcessArguments(CSLConstList)
    3423             :     {
    3424           3 :         return CE_None;
    3425             :     }
    3426             : 
    3427           3 :     void ProcessPixel(double dfVal)
    3428             :     {
    3429           3 :         dfProduct *= dfVal;
    3430           3 :         nValidSources++;
    3431           3 :     }
    3432             : 
    3433           4 :     bool HasValue() const
    3434             :     {
    3435           4 :         return nValidSources > 0;
    3436             :     }
    3437             : 
    3438           1 :     double GetValue() const
    3439             :     {
    3440           1 :         return std::pow(dfProduct, 1.0 / nValidSources);
    3441             :     }
    3442             : };
    3443             : 
    3444             : struct HarmonicMeanKernel
    3445             : {
    3446             :     static constexpr const char *pszName = "harmonic_mean";
    3447             : 
    3448             :     double dfDenom = 0;
    3449             :     int nValidSources = 0;
    3450             :     bool bValueIsZero = false;
    3451             :     bool bPropagateZero = false;
    3452             : 
    3453          10 :     void Reset()
    3454             :     {
    3455          10 :         dfDenom = 0;
    3456          10 :         nValidSources = 0;
    3457          10 :         bValueIsZero = false;
    3458          10 :     }
    3459             : 
    3460           7 :     void ProcessPixel(double dfVal)
    3461             :     {
    3462           7 :         if (dfVal == 0)
    3463             :         {
    3464           2 :             bValueIsZero = true;
    3465             :         }
    3466             :         else
    3467             :         {
    3468           5 :             dfDenom += 1 / dfVal;
    3469             :         }
    3470           7 :         nValidSources++;
    3471           7 :     }
    3472             : 
    3473           5 :     CPLErr ProcessArguments(CSLConstList papszArgs)
    3474             :     {
    3475           5 :         bPropagateZero =
    3476           5 :             CPLTestBool(CSLFetchNameValueDef(papszArgs, "propagateZero", "0"));
    3477           5 :         return CE_None;
    3478             :     }
    3479             : 
    3480           8 :     bool HasValue() const
    3481             :     {
    3482           8 :         return dfDenom > 0 && (bPropagateZero || !bValueIsZero);
    3483             :     }
    3484             : 
    3485           2 :     double GetValue() const
    3486             :     {
    3487           2 :         if (bPropagateZero && bValueIsZero)
    3488             :         {
    3489           1 :             return 0;
    3490             :         }
    3491           1 :         return static_cast<double>(nValidSources) / dfDenom;
    3492             :     }
    3493             : };
    3494             : 
    3495             : struct MedianKernel
    3496             : {
    3497             :     static constexpr const char *pszName = "median";
    3498             : 
    3499             :     mutable std::vector<double> values{};
    3500             : 
    3501           9 :     void Reset()
    3502             :     {
    3503           9 :         values.clear();
    3504           9 :     }
    3505             : 
    3506           5 :     static CPLErr ProcessArguments(CSLConstList)
    3507             :     {
    3508           5 :         return CE_None;
    3509             :     }
    3510             : 
    3511           9 :     void ProcessPixel(double dfVal)
    3512             :     {
    3513           9 :         if (!std::isnan(dfVal))
    3514             :         {
    3515           9 :             values.push_back(dfVal);
    3516             :         }
    3517           9 :     }
    3518             : 
    3519           7 :     bool HasValue() const
    3520             :     {
    3521           7 :         return !values.empty();
    3522             :     }
    3523             : 
    3524           3 :     double GetValue() const
    3525             :     {
    3526           3 :         std::sort(values.begin(), values.end());
    3527           3 :         if (values.size() % 2 == 0)
    3528             :         {
    3529             :             return 0.5 *
    3530           1 :                    (values[values.size() / 2 - 1] + values[values.size() / 2]);
    3531             :         }
    3532             : 
    3533           2 :         return values[values.size() / 2];
    3534             :     }
    3535             : };
    3536             : 
    3537             : struct QuantileKernel
    3538             : {
    3539             :     static constexpr const char *pszName = "quantile";
    3540             : 
    3541             :     mutable std::vector<double> values{};
    3542             :     double q = 0.5;
    3543             : 
    3544          14 :     void Reset()
    3545             :     {
    3546          14 :         values.clear();
    3547             :         // q intentionally preserved (set via ProcessArguments)
    3548          14 :     }
    3549             : 
    3550          15 :     CPLErr ProcessArguments(CSLConstList papszArgs)
    3551             :     {
    3552          15 :         const char *pszQ = CSLFetchNameValue(papszArgs, "q");
    3553          15 :         if (pszQ)
    3554             :         {
    3555             :             char *end;
    3556          14 :             const double dq = CPLStrtod(pszQ, &end);
    3557          14 :             while (isspace(*end))
    3558             :             {
    3559           0 :                 end++;
    3560             :             }
    3561          14 :             if (*end != '\0' || dq < 0.0 || dq > 1.0 || std::isnan(dq))
    3562             :             {
    3563           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3564             :                          "quantile: q must be between 0 and 1");
    3565           4 :                 return CE_Failure;
    3566             :             }
    3567          10 :             q = dq;
    3568             :         }
    3569             :         else
    3570             :         {
    3571           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    3572             :                      "quantile: q must be specified");
    3573           1 :             return CE_Failure;
    3574             :         }
    3575          10 :         return CE_None;
    3576             :     }
    3577             : 
    3578          15 :     void ProcessPixel(double dfVal)
    3579             :     {
    3580          15 :         if (!std::isnan(dfVal))
    3581             :         {
    3582          15 :             values.push_back(dfVal);
    3583             :         }
    3584          15 :     }
    3585             : 
    3586          12 :     bool HasValue() const
    3587             :     {
    3588          12 :         return !values.empty();
    3589             :     }
    3590             : 
    3591           7 :     double GetValue() const
    3592             :     {
    3593           7 :         if (values.empty())
    3594             :         {
    3595           0 :             return std::numeric_limits<double>::quiet_NaN();
    3596             :         }
    3597             : 
    3598           7 :         std::sort(values.begin(), values.end());
    3599           7 :         const double loc = q * static_cast<double>(values.size() - 1);
    3600             : 
    3601             :         // Use formula from NumPy docs with default linear interpolation
    3602             :         // g: fractional component of loc
    3603             :         // j: integral component of loc
    3604             :         double j;
    3605           7 :         const double g = std::modf(loc, &j);
    3606             : 
    3607           7 :         if (static_cast<size_t>(j) + 1 == values.size())
    3608             :         {
    3609           3 :             return values[static_cast<size_t>(j)];
    3610             :         }
    3611             : 
    3612           4 :         return (1 - g) * values[static_cast<size_t>(j)] +
    3613           4 :                g * values[static_cast<size_t>(j) + 1];
    3614             :     }
    3615             : };
    3616             : 
    3617             : struct ModeKernel
    3618             : {
    3619             :     static constexpr const char *pszName = "mode";
    3620             : 
    3621             :     std::map<double, size_t> counts{};
    3622             :     std::size_t nanCount{0};
    3623             :     double dfMax = std::numeric_limits<double>::quiet_NaN();
    3624             :     decltype(counts.begin()) oMax = counts.end();
    3625             : 
    3626           7 :     void Reset()
    3627             :     {
    3628           7 :         nanCount = 0;
    3629           7 :         counts.clear();
    3630           7 :         oMax = counts.end();
    3631           7 :     }
    3632             : 
    3633           4 :     static CPLErr ProcessArguments(CSLConstList)
    3634             :     {
    3635           4 :         return CE_None;
    3636             :     }
    3637             : 
    3638          11 :     void ProcessPixel(double dfVal)
    3639             :     {
    3640          11 :         if (std::isnan(dfVal))
    3641             :         {
    3642           2 :             nanCount += 1;
    3643           2 :             return;
    3644             :         }
    3645             : 
    3646             :         // if dfVal is NaN, try_emplace will return an entry for a different key!
    3647           9 :         auto [it, inserted] = counts.try_emplace(dfVal, 0);
    3648             : 
    3649           9 :         it->second += 1;
    3650             : 
    3651           9 :         if (oMax == counts.end() || it->second > oMax->second)
    3652             :         {
    3653           5 :             oMax = it;
    3654             :         }
    3655             :     }
    3656             : 
    3657           5 :     bool HasValue() const
    3658             :     {
    3659           5 :         return nanCount > 0 || oMax != counts.end();
    3660             :     }
    3661             : 
    3662           3 :     double GetValue() const
    3663             :     {
    3664           3 :         double ret = std::numeric_limits<double>::quiet_NaN();
    3665           3 :         if (oMax != counts.end())
    3666             :         {
    3667           3 :             const size_t nCount = oMax->second;
    3668           3 :             if (nCount > nanCount)
    3669           2 :                 ret = oMax->first;
    3670             :         }
    3671           3 :         return ret;
    3672             :     }
    3673             : };
    3674             : 
    3675             : static const char pszBasicPixelFuncMetadata[] =
    3676             :     "<PixelFunctionArgumentsList>"
    3677             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3678             :     "   <Argument name='propagateNoData' description='Whether the output value "
    3679             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    3680             :     "default='false' />"
    3681             :     "</PixelFunctionArgumentsList>";
    3682             : 
    3683             : static const char pszQuantilePixelFuncMetadata[] =
    3684             :     "<PixelFunctionArgumentsList>"
    3685             :     "   <Argument name='q' type='float' description='Quantile in [0,1]' />"
    3686             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3687             :     "   <Argument name='propagateNoData' type='boolean' default='false' />"
    3688             :     "</PixelFunctionArgumentsList>";
    3689             : 
    3690             : #if defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    3691         636 : inline __m128i packus_epi32(__m128i low, __m128i high)
    3692             : {
    3693             : #if __SSE4_1__
    3694             :     return _mm_packus_epi32(low, high);  // Pack uint32 to uint16
    3695             : #else
    3696        1272 :     low = _mm_add_epi32(low, _mm_set1_epi32(-32768));
    3697        1272 :     high = _mm_add_epi32(high, _mm_set1_epi32(-32768));
    3698        1908 :     return _mm_sub_epi16(_mm_packs_epi32(low, high), _mm_set1_epi16(-32768));
    3699             : #endif
    3700             : }
    3701             : #endif
    3702             : 
    3703             : #ifdef USE_SSE2
    3704             : 
    3705             : template <class T, class SSEWrapper>
    3706          45 : static void OptimizedMeanFloatSSE2(const void *const *papoSources, int nSources,
    3707             :                                    void *pData, int nXSize, int nYSize,
    3708             :                                    int nLineSpace)
    3709             : {
    3710          45 :     assert(nSources >= 1);
    3711          45 :     constexpr int VALUES_PER_REG =
    3712             :         static_cast<int>(sizeof(typename SSEWrapper::Vec) / sizeof(T));
    3713          45 :     const T invSources = static_cast<T>(1.0) / static_cast<T>(nSources);
    3714          45 :     const auto invSourcesSSE = SSEWrapper::Set1(invSources);
    3715          45 :     const auto signMaskSSE = SSEWrapper::Set1(static_cast<T>(-0.0));
    3716          45 :     const auto infSSE = SSEWrapper::Set1(std::numeric_limits<T>::infinity());
    3717         256 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3718             :     {
    3719         211 :         T *CPL_RESTRICT pDest =
    3720             :             reinterpret_cast<T *>(static_cast<GByte *>(pData) +
    3721         211 :                                   static_cast<GSpacing>(nLineSpace) * iLine);
    3722         211 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    3723         211 :         int iCol = 0;
    3724        1921 :         for (; iCol < nXSize - (2 * VALUES_PER_REG - 1);
    3725             :              iCol += 2 * VALUES_PER_REG)
    3726             :         {
    3727        1725 :             auto reg0 = SSEWrapper::LoadU(
    3728        1725 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    3729        1725 :                 iOffsetLine + iCol);
    3730        1725 :             auto reg1 = SSEWrapper::LoadU(
    3731        1725 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    3732        1725 :                 iOffsetLine + iCol + VALUES_PER_REG);
    3733        4900 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    3734             :             {
    3735        3175 :                 const auto inputVal0 = SSEWrapper::LoadU(
    3736        3175 :                     static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    3737        3175 :                     iOffsetLine + iCol);
    3738        3175 :                 const auto inputVal1 = SSEWrapper::LoadU(
    3739        3175 :                     static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    3740        3175 :                     iOffsetLine + iCol + VALUES_PER_REG);
    3741        3175 :                 reg0 = SSEWrapper::Add(reg0, inputVal0);
    3742        3175 :                 reg1 = SSEWrapper::Add(reg1, inputVal1);
    3743             :             }
    3744        1725 :             reg0 = SSEWrapper::Mul(reg0, invSourcesSSE);
    3745        1725 :             reg1 = SSEWrapper::Mul(reg1, invSourcesSSE);
    3746             : 
    3747             :             // Detect infinity that could happen when summing huge
    3748             :             // values
    3749        1725 :             if (SSEWrapper::MoveMask(SSEWrapper::Or(
    3750             :                     SSEWrapper::CmpEq(SSEWrapper::AndNot(signMaskSSE, reg0),
    3751             :                                       infSSE),
    3752             :                     SSEWrapper::CmpEq(SSEWrapper::AndNot(signMaskSSE, reg1),
    3753             :                                       infSSE))))
    3754             :             {
    3755          15 :                 break;
    3756             :             }
    3757             : 
    3758        1710 :             SSEWrapper::StoreU(pDest + iCol, reg0);
    3759        1710 :             SSEWrapper::StoreU(pDest + iCol + VALUES_PER_REG, reg1);
    3760             :         }
    3761             : 
    3762             :         // Use numerically stable mean computation
    3763        1090 :         for (; iCol < nXSize; ++iCol)
    3764             :         {
    3765         879 :             T mean = static_cast<const T * CPL_RESTRICT>(
    3766         879 :                 papoSources[0])[iOffsetLine + iCol];
    3767         879 :             if (nSources >= 2)
    3768             :             {
    3769         869 :                 T new_val = static_cast<const T * CPL_RESTRICT>(
    3770         869 :                     papoSources[1])[iOffsetLine + iCol];
    3771         869 :                 if (CPL_UNLIKELY(std::isinf(new_val)))
    3772             :                 {
    3773         268 :                     if (new_val == -mean)
    3774             :                     {
    3775          10 :                         pDest[iCol] = std::numeric_limits<T>::quiet_NaN();
    3776          10 :                         continue;
    3777             :                     }
    3778             :                 }
    3779         601 :                 else if (CPL_UNLIKELY(std::isinf(mean)))
    3780             :                 {
    3781         144 :                     if (!std::isfinite(new_val))
    3782             :                     {
    3783          10 :                         pDest[iCol] = std::numeric_limits<T>::quiet_NaN();
    3784          10 :                         continue;
    3785             :                     }
    3786             :                 }
    3787             :                 else
    3788             :                 {
    3789         457 :                     const T delta = new_val - mean;
    3790         457 :                     if (CPL_UNLIKELY(std::isinf(delta)))
    3791          10 :                         mean += new_val * static_cast<T>(0.5) -
    3792          10 :                                 mean * static_cast<T>(0.5);
    3793             :                     else
    3794         447 :                         mean += delta * static_cast<T>(0.5);
    3795             :                 }
    3796             : 
    3797        1481 :                 for (int iSrc = 2; iSrc < nSources; ++iSrc)
    3798             :                 {
    3799         652 :                     new_val = static_cast<const T * CPL_RESTRICT>(
    3800         652 :                         papoSources[iSrc])[iOffsetLine + iCol];
    3801         652 :                     if (CPL_UNLIKELY(std::isinf(new_val)))
    3802             :                     {
    3803         196 :                         if (new_val == -mean)
    3804             :                         {
    3805          10 :                             mean = std::numeric_limits<T>::quiet_NaN();
    3806          10 :                             break;
    3807             :                         }
    3808             :                     }
    3809         456 :                     else if (CPL_UNLIKELY(std::isinf(mean)))
    3810             :                     {
    3811          72 :                         if (!std::isfinite(new_val))
    3812             :                         {
    3813          10 :                             mean = std::numeric_limits<T>::quiet_NaN();
    3814          10 :                             break;
    3815             :                         }
    3816             :                     }
    3817             :                     else
    3818             :                     {
    3819         384 :                         const T delta = new_val - mean;
    3820         384 :                         if (CPL_UNLIKELY(std::isinf(delta)))
    3821          62 :                             mean += new_val / static_cast<T>(iSrc + 1) -
    3822          62 :                                     mean / static_cast<T>(iSrc + 1);
    3823             :                         else
    3824         322 :                             mean += delta / static_cast<T>(iSrc + 1);
    3825             :                     }
    3826             :                 }
    3827             :             }
    3828         859 :             pDest[iCol] = mean;
    3829             :         }
    3830             :     }
    3831          45 : }
    3832             : 
    3833             : // clang-format off
    3834             : namespace
    3835             : {
    3836             : #ifdef __AVX2__
    3837             : struct SSEWrapperFloat
    3838             : {
    3839             :     typedef __m256 Vec;
    3840             : 
    3841             :     static inline Vec Set1(float x) { return _mm256_set1_ps(x); }
    3842             :     static inline Vec LoadU(const float *x) { return _mm256_loadu_ps(x); }
    3843             :     static inline void StoreU(float *x, Vec y) { _mm256_storeu_ps(x, y); }
    3844             :     static inline Vec Add(Vec x, Vec y) { return _mm256_add_ps(x, y); }
    3845             :     static inline Vec Mul(Vec x, Vec y) { return _mm256_mul_ps(x, y); }
    3846             :     static inline Vec Or(Vec x, Vec y) { return _mm256_or_ps(x, y); }
    3847             :     static inline Vec AndNot(Vec x, Vec y) { return _mm256_andnot_ps(x, y); }
    3848             :     static inline Vec CmpEq(Vec x, Vec y) { return _mm256_cmp_ps(x, y, _CMP_EQ_OQ); }
    3849             :     static inline int MoveMask(Vec x) { return _mm256_movemask_ps(x); }
    3850             : };
    3851             : 
    3852             : struct SSEWrapperDouble
    3853             : {
    3854             :     typedef __m256d Vec;
    3855             : 
    3856             :     static inline Vec Set1(double x) { return _mm256_set1_pd(x); }
    3857             :     static inline Vec LoadU(const double *x) { return _mm256_loadu_pd(x); }
    3858             :     static inline void StoreU(double *x, Vec y) { _mm256_storeu_pd(x, y); }
    3859             :     static inline Vec Add(Vec x, Vec y) { return _mm256_add_pd(x, y); }
    3860             :     static inline Vec Mul(Vec x, Vec y) { return _mm256_mul_pd(x, y); }
    3861             :     static inline Vec Or(Vec x, Vec y) { return _mm256_or_pd(x, y); }
    3862             :     static inline Vec AndNot(Vec x, Vec y) { return _mm256_andnot_pd(x, y); }
    3863             :     static inline Vec CmpEq(Vec x, Vec y) { return _mm256_cmp_pd(x, y, _CMP_EQ_OQ); }
    3864             :     static inline int MoveMask(Vec x) { return _mm256_movemask_pd(x); }
    3865             : };
    3866             : 
    3867             : #else
    3868             : 
    3869             : struct SSEWrapperFloat
    3870             : {
    3871             :     typedef __m128 Vec;
    3872             : 
    3873         114 :     static inline Vec Set1(float x) { return _mm_set1_ps(x); }
    3874        3984 :     static inline Vec LoadU(const float *x) { return _mm_loadu_ps(x); }
    3875         666 :     static inline void StoreU(float *x, Vec y) { _mm_storeu_ps(x, y); }
    3876        2624 :     static inline Vec Add(Vec x, Vec y) { return _mm_add_ps(x, y); }
    3877        1360 :     static inline Vec Mul(Vec x, Vec y) { return _mm_mul_ps(x, y); }
    3878         680 :     static inline Vec Or(Vec x, Vec y) { return _mm_or_ps(x, y); }
    3879        1360 :     static inline Vec AndNot(Vec x, Vec y) { return _mm_andnot_ps(x, y); }
    3880        1360 :     static inline Vec CmpEq(Vec x, Vec y) { return _mm_cmpeq_ps(x, y); }
    3881         680 :     static inline int MoveMask(Vec x) { return _mm_movemask_ps(x); }
    3882             : };
    3883             : 
    3884             : struct SSEWrapperDouble
    3885             : {
    3886             :     typedef __m128d Vec;
    3887             : 
    3888         156 :     static inline Vec Set1(double x) { return _mm_set1_pd(x); }
    3889       15616 :     static inline Vec LoadU(const double *x) { return _mm_loadu_pd(x); }
    3890        2754 :     static inline void StoreU(double *x, Vec y) { _mm_storeu_pd(x, y); }
    3891       10076 :     static inline Vec Add(Vec x, Vec y) { return _mm_add_pd(x, y); }
    3892        5540 :     static inline Vec Mul(Vec x, Vec y) { return _mm_mul_pd(x, y); }
    3893        2770 :     static inline Vec Or(Vec x, Vec y) { return _mm_or_pd(x, y); }
    3894        5540 :     static inline Vec AndNot(Vec x, Vec y) { return _mm_andnot_pd(x, y); }
    3895        5540 :     static inline Vec CmpEq(Vec x, Vec y) { return _mm_cmpeq_pd(x, y); }
    3896        2770 :     static inline int MoveMask(Vec x) { return _mm_movemask_pd(x); }
    3897             : };
    3898             : #endif
    3899             : }  // namespace
    3900             : 
    3901             : // clang-format on
    3902             : 
    3903             : #endif  // USE_SSE2
    3904             : 
    3905             : template <typename Kernel>
    3906         126 : static CPLErr BasicPixelFunc(void **papoSources, int nSources, void *pData,
    3907             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    3908             :                              GDALDataType eBufType, int nPixelSpace,
    3909             :                              int nLineSpace, CSLConstList papszArgs)
    3910             : {
    3911             :     /* ---- Init ---- */
    3912         151 :     Kernel oKernel;
    3913             : 
    3914         126 :     if (GDALDataTypeIsComplex(eSrcType))
    3915             :     {
    3916           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3917             :                  "Complex data types not supported by %s", oKernel.pszName);
    3918           1 :         return CE_Failure;
    3919             :     }
    3920             : 
    3921         125 :     double dfNoData{0};
    3922         125 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    3923         125 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    3924           0 :         return CE_Failure;
    3925             : 
    3926         125 :     const bool bPropagateNoData = CPLTestBool(
    3927             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    3928             : 
    3929         125 :     if (oKernel.ProcessArguments(papszArgs) == CE_Failure)
    3930             :     {
    3931           5 :         return CE_Failure;
    3932             :     }
    3933             : 
    3934             : #if defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    3935             :     if constexpr (std::is_same_v<Kernel, MeanKernel>)
    3936             :     {
    3937          90 :         if (!bHasNoData && eSrcType == GDT_UInt8 && eBufType == GDT_UInt8 &&
    3938         183 :             nPixelSpace == 1 &&
    3939             :             // We use signed int16 to accumulate
    3940          11 :             nSources <= std::numeric_limits<int16_t>::max() /
    3941          11 :                             std::numeric_limits<uint8_t>::max())
    3942             :         {
    3943             :             using T = uint8_t;
    3944          10 :             constexpr int VALUES_PER_REG = 16;
    3945          10 :             if (nSources == 2)
    3946             :             {
    3947         207 :                 for (int iLine = 0; iLine < nYSize; ++iLine)
    3948             :                 {
    3949         203 :                     T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    3950             :                         static_cast<GByte *>(pData) +
    3951         203 :                         static_cast<GSpacing>(nLineSpace) * iLine);
    3952         203 :                     const size_t iOffsetLine =
    3953         203 :                         static_cast<size_t>(iLine) * nXSize;
    3954         203 :                     int iCol = 0;
    3955        5209 :                     for (; iCol < nXSize - (VALUES_PER_REG - 1);
    3956             :                          iCol += VALUES_PER_REG)
    3957             :                     {
    3958             :                         const __m128i inputVal0 =
    3959       10012 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    3960             :                                 static_cast<const T * CPL_RESTRICT>(
    3961             :                                     papoSources[0]) +
    3962        5006 :                                 iOffsetLine + iCol));
    3963             :                         const __m128i inputVal1 =
    3964        5006 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    3965        5006 :                                 static_cast<const T * CPL_RESTRICT>(
    3966             :                                     papoSources[1]) +
    3967        5006 :                                 iOffsetLine + iCol));
    3968        5006 :                         _mm_storeu_si128(
    3969        5006 :                             reinterpret_cast<__m128i *>(pDest + iCol),
    3970             :                             _mm_avg_epu8(inputVal0, inputVal1));
    3971             :                     }
    3972         235 :                     for (; iCol < nXSize; ++iCol)
    3973             :                     {
    3974          32 :                         uint32_t acc = 1 +
    3975          32 :                                        static_cast<const T * CPL_RESTRICT>(
    3976          32 :                                            papoSources[0])[iOffsetLine + iCol] +
    3977          32 :                                        static_cast<const T * CPL_RESTRICT>(
    3978          32 :                                            papoSources[1])[iOffsetLine + iCol];
    3979          32 :                         pDest[iCol] = static_cast<T>(acc / 2);
    3980             :                     }
    3981             :                 }
    3982             :             }
    3983             :             else
    3984             :             {
    3985           6 :                 libdivide::divider<uint16_t> fast_d(
    3986             :                     static_cast<uint16_t>(nSources));
    3987             :                 const auto halfConstant =
    3988           6 :                     _mm_set1_epi16(static_cast<int16_t>(nSources / 2));
    3989         211 :                 for (int iLine = 0; iLine < nYSize; ++iLine)
    3990             :                 {
    3991         205 :                     T *CPL_RESTRICT pDest =
    3992             :                         static_cast<GByte *>(pData) +
    3993         205 :                         static_cast<GSpacing>(nLineSpace) * iLine;
    3994         205 :                     const size_t iOffsetLine =
    3995         205 :                         static_cast<size_t>(iLine) * nXSize;
    3996         205 :                     int iCol = 0;
    3997        5220 :                     for (; iCol < nXSize - (VALUES_PER_REG - 1);
    3998             :                          iCol += VALUES_PER_REG)
    3999             :                     {
    4000        5015 :                         __m128i reg0 = halfConstant;
    4001        5015 :                         __m128i reg1 = halfConstant;
    4002       20435 :                         for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4003             :                         {
    4004       15420 :                             const __m128i inputVal = _mm_loadu_si128(
    4005             :                                 reinterpret_cast<const __m128i *>(
    4006       15420 :                                     static_cast<const T * CPL_RESTRICT>(
    4007       15420 :                                         papoSources[iSrc]) +
    4008       15420 :                                     iOffsetLine + iCol));
    4009       46260 :                             reg0 = _mm_add_epi16(
    4010             :                                 reg0, _mm_unpacklo_epi8(inputVal,
    4011             :                                                         _mm_setzero_si128()));
    4012       46260 :                             reg1 = _mm_add_epi16(
    4013             :                                 reg1, _mm_unpackhi_epi8(inputVal,
    4014             :                                                         _mm_setzero_si128()));
    4015             :                         }
    4016             :                         reg0 /= fast_d;
    4017             :                         reg1 /= fast_d;
    4018        5015 :                         _mm_storeu_si128(
    4019        5015 :                             reinterpret_cast<__m128i *>(pDest + iCol),
    4020             :                             _mm_packus_epi16(reg0, reg1));
    4021             :                     }
    4022         280 :                     for (; iCol < nXSize; ++iCol)
    4023             :                     {
    4024          75 :                         uint32_t acc = nSources / 2;
    4025        2175 :                         for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4026             :                         {
    4027        2100 :                             acc += static_cast<const T * CPL_RESTRICT>(
    4028        2100 :                                 papoSources[iSrc])[iOffsetLine + iCol];
    4029             :                         }
    4030          75 :                         pDest[iCol] = static_cast<T>(acc / nSources);
    4031             :                     }
    4032             :                 }
    4033             :             }
    4034          10 :             return CE_None;
    4035             :         }
    4036             : 
    4037          80 :         if (!bHasNoData && eSrcType == GDT_UInt8 && eBufType == GDT_UInt8 &&
    4038         163 :             nPixelSpace == 1 &&
    4039             :             // We use signed int32 to accumulate
    4040           1 :             nSources <= std::numeric_limits<int32_t>::max() /
    4041           1 :                             std::numeric_limits<uint8_t>::max())
    4042             :         {
    4043             :             using T = uint8_t;
    4044           1 :             constexpr int VALUES_PER_REG = 16;
    4045           1 :             libdivide::divider<uint32_t> fast_d(nSources);
    4046           1 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4047           2 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4048             :             {
    4049           1 :                 T *CPL_RESTRICT pDest =
    4050             :                     static_cast<GByte *>(pData) +
    4051           1 :                     static_cast<GSpacing>(nLineSpace) * iLine;
    4052           1 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4053           1 :                 int iCol = 0;
    4054           4 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4055             :                      iCol += VALUES_PER_REG)
    4056             :                 {
    4057           3 :                     __m128i reg0 = halfConstant;
    4058           3 :                     __m128i reg1 = halfConstant;
    4059           3 :                     __m128i reg2 = halfConstant;
    4060           3 :                     __m128i reg3 = halfConstant;
    4061       98307 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4062             :                     {
    4063             :                         const __m128i inputVal =
    4064       98304 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4065       98304 :                                 static_cast<const T * CPL_RESTRICT>(
    4066       98304 :                                     papoSources[iSrc]) +
    4067       98304 :                                 iOffsetLine + iCol));
    4068             :                         const __m128i low =
    4069      196608 :                             _mm_unpacklo_epi8(inputVal, _mm_setzero_si128());
    4070             :                         const __m128i high =
    4071      196608 :                             _mm_unpackhi_epi8(inputVal, _mm_setzero_si128());
    4072      294912 :                         reg0 = _mm_add_epi32(
    4073             :                             reg0, _mm_unpacklo_epi16(low, _mm_setzero_si128()));
    4074      294912 :                         reg1 = _mm_add_epi32(
    4075             :                             reg1, _mm_unpackhi_epi16(low, _mm_setzero_si128()));
    4076      294912 :                         reg2 = _mm_add_epi32(
    4077             :                             reg2,
    4078             :                             _mm_unpacklo_epi16(high, _mm_setzero_si128()));
    4079      294912 :                         reg3 = _mm_add_epi32(
    4080             :                             reg3,
    4081             :                             _mm_unpackhi_epi16(high, _mm_setzero_si128()));
    4082             :                     }
    4083             :                     reg0 /= fast_d;
    4084             :                     reg1 /= fast_d;
    4085             :                     reg2 /= fast_d;
    4086             :                     reg3 /= fast_d;
    4087           3 :                     _mm_storeu_si128(
    4088           3 :                         reinterpret_cast<__m128i *>(pDest + iCol),
    4089             :                         _mm_packus_epi16(packus_epi32(reg0, reg1),
    4090             :                                          packus_epi32(reg2, reg3)));
    4091             :                 }
    4092          16 :                 for (; iCol < nXSize; ++iCol)
    4093             :                 {
    4094          15 :                     uint32_t acc = nSources / 2;
    4095      491535 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4096             :                     {
    4097      491520 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4098      491520 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4099             :                     }
    4100          15 :                     pDest[iCol] = static_cast<T>(acc / nSources);
    4101             :                 }
    4102             :             }
    4103           1 :             return CE_None;
    4104             :         }
    4105             : 
    4106          79 :         if (!bHasNoData && eSrcType == GDT_UInt16 && eBufType == GDT_UInt16 &&
    4107         161 :             nPixelSpace == 2 &&
    4108           5 :             nSources <= std::numeric_limits<int32_t>::max() /
    4109           5 :                             std::numeric_limits<uint16_t>::max())
    4110             :         {
    4111           5 :             libdivide::divider<uint32_t> fast_d(nSources);
    4112             :             using T = uint16_t;
    4113           5 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4114           5 :             constexpr int VALUES_PER_REG = 8;
    4115          59 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4116             :             {
    4117          54 :                 T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    4118             :                     static_cast<GByte *>(pData) +
    4119          54 :                     static_cast<GSpacing>(nLineSpace) * iLine);
    4120          54 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4121          54 :                 int iCol = 0;
    4122         366 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4123             :                      iCol += VALUES_PER_REG)
    4124             :                 {
    4125         312 :                     __m128i reg0 = halfConstant;
    4126         312 :                     __m128i reg1 = halfConstant;
    4127       99534 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4128             :                     {
    4129             :                         const __m128i inputVal =
    4130       99222 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4131       99222 :                                 static_cast<const T * CPL_RESTRICT>(
    4132       99222 :                                     papoSources[iSrc]) +
    4133       99222 :                                 iOffsetLine + iCol));
    4134      297666 :                         reg0 = _mm_add_epi32(
    4135             :                             reg0,
    4136             :                             _mm_unpacklo_epi16(inputVal, _mm_setzero_si128()));
    4137      297666 :                         reg1 = _mm_add_epi32(
    4138             :                             reg1,
    4139             :                             _mm_unpackhi_epi16(inputVal, _mm_setzero_si128()));
    4140             :                     }
    4141             :                     reg0 /= fast_d;
    4142             :                     reg1 /= fast_d;
    4143         312 :                     _mm_storeu_si128(reinterpret_cast<__m128i *>(pDest + iCol),
    4144             :                                      packus_epi32(reg0, reg1));
    4145             :                 }
    4146         182 :                 for (; iCol < nXSize; ++iCol)
    4147             :                 {
    4148         128 :                     uint32_t acc = nSources / 2;
    4149      229846 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4150             :                     {
    4151      229718 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4152      229718 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4153             :                     }
    4154         128 :                     pDest[iCol] = static_cast<T>(acc / nSources);
    4155             :                 }
    4156             :             }
    4157           5 :             return CE_None;
    4158             :         }
    4159             : 
    4160          74 :         if (!bHasNoData && eSrcType == GDT_Int16 && eBufType == GDT_Int16 &&
    4161         151 :             nPixelSpace == 2 &&
    4162           7 :             nSources <= std::numeric_limits<int32_t>::max() /
    4163           7 :                             std::numeric_limits<uint16_t>::max())
    4164             :         {
    4165           7 :             libdivide::divider<uint32_t> fast_d(nSources);
    4166             :             using T = int16_t;
    4167           7 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4168           7 :             const auto shift = _mm_set1_epi16(std::numeric_limits<T>::min());
    4169           7 :             constexpr int VALUES_PER_REG = 8;
    4170          63 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4171             :             {
    4172          56 :                 T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    4173             :                     static_cast<GByte *>(pData) +
    4174          56 :                     static_cast<GSpacing>(nLineSpace) * iLine);
    4175          56 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4176          56 :                 int iCol = 0;
    4177         374 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4178             :                      iCol += VALUES_PER_REG)
    4179             :                 {
    4180         318 :                     __m128i reg0 = halfConstant;
    4181         318 :                     __m128i reg1 = halfConstant;
    4182       99555 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4183             :                     {
    4184             :                         // Shift input values by 32768 to get unsigned values
    4185      198474 :                         const __m128i inputVal = _mm_add_epi16(
    4186             :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4187       99237 :                                 static_cast<const T * CPL_RESTRICT>(
    4188       99237 :                                     papoSources[iSrc]) +
    4189       99237 :                                 iOffsetLine + iCol)),
    4190             :                             shift);
    4191      297711 :                         reg0 = _mm_add_epi32(
    4192             :                             reg0,
    4193             :                             _mm_unpacklo_epi16(inputVal, _mm_setzero_si128()));
    4194      297711 :                         reg1 = _mm_add_epi32(
    4195             :                             reg1,
    4196             :                             _mm_unpackhi_epi16(inputVal, _mm_setzero_si128()));
    4197             :                     }
    4198             :                     reg0 /= fast_d;
    4199             :                     reg1 /= fast_d;
    4200         318 :                     _mm_storeu_si128(
    4201         318 :                         reinterpret_cast<__m128i *>(pDest + iCol),
    4202             :                         _mm_add_epi16(packus_epi32(reg0, reg1), shift));
    4203             :                 }
    4204         198 :                 for (; iCol < nXSize; ++iCol)
    4205             :                 {
    4206         142 :                     int32_t acc = (-std::numeric_limits<T>::min()) * nSources +
    4207         142 :                                   nSources / 2;
    4208      229895 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4209             :                     {
    4210      229753 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4211      229753 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4212             :                     }
    4213         284 :                     pDest[iCol] = static_cast<T>(acc / nSources +
    4214         142 :                                                  std::numeric_limits<T>::min());
    4215             :                 }
    4216             :             }
    4217           7 :             return CE_None;
    4218             :         }
    4219             :     }
    4220             : #endif  // defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    4221             : 
    4222             : #if defined(USE_SSE2)
    4223             :     if constexpr (std::is_same_v<Kernel, MeanKernel>)
    4224             :     {
    4225          70 :         if (!bHasNoData && eSrcType == GDT_Float32 && eBufType == GDT_Float32 &&
    4226          19 :             nPixelSpace == 4 && nSources > 0)
    4227             :         {
    4228          19 :             OptimizedMeanFloatSSE2<float, SSEWrapperFloat>(
    4229             :                 papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    4230          19 :             return CE_None;
    4231             :         }
    4232             : 
    4233          51 :         if (!bHasNoData && eSrcType == GDT_Float64 && eBufType == GDT_Float64 &&
    4234          26 :             nPixelSpace == 8 && nSources > 0)
    4235             :         {
    4236          26 :             OptimizedMeanFloatSSE2<double, SSEWrapperDouble>(
    4237             :                 papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    4238          26 :             return CE_None;
    4239             :         }
    4240             :     }
    4241             : #endif  // USE_SSE2
    4242             : 
    4243             :     /* ---- Set pixels ---- */
    4244          52 :     size_t ii = 0;
    4245         104 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    4246             :     {
    4247         666 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    4248             :         {
    4249         614 :             oKernel.Reset();
    4250         614 :             bool bWriteNoData = false;
    4251             : 
    4252        2107 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4253             :             {
    4254        1505 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    4255             : 
    4256        1505 :                 if (bHasNoData && IsNoData(dfVal, dfNoData))
    4257             :                 {
    4258          85 :                     if (bPropagateNoData)
    4259             :                     {
    4260          12 :                         bWriteNoData = true;
    4261          12 :                         break;
    4262             :                     }
    4263             :                 }
    4264             :                 else
    4265             :                 {
    4266        1420 :                     oKernel.ProcessPixel(dfVal);
    4267             :                 }
    4268             :             }
    4269             : 
    4270         614 :             double dfPixVal{dfNoData};
    4271         614 :             if (!bWriteNoData && oKernel.HasValue())
    4272             :             {
    4273         579 :                 dfPixVal = oKernel.GetValue();
    4274             :             }
    4275             : 
    4276         614 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    4277             :                           static_cast<GByte *>(pData) +
    4278         614 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    4279         614 :                               iCol * nPixelSpace,
    4280             :                           eBufType, nPixelSpace, 1);
    4281             :         }
    4282             :     }
    4283             : 
    4284             :     /* ---- Return success ---- */
    4285          52 :     return CE_None;
    4286             : }  // BasicPixelFunc
    4287             : 
    4288             : /************************************************************************/
    4289             : /*                    GDALRegisterDefaultPixelFunc()                    */
    4290             : /************************************************************************/
    4291             : 
    4292             : /**
    4293             :  * This adds a default set of pixel functions to the global list of
    4294             :  * available pixel functions for derived bands:
    4295             :  *
    4296             :  * - "real": extract real part from a single raster band (just a copy if the
    4297             :  *           input is non-complex)
    4298             :  * - "imag": extract imaginary part from a single raster band (0 for
    4299             :  *           non-complex)
    4300             :  * - "complex": make a complex band merging two bands used as real and
    4301             :  *              imag values
    4302             :  * - "polar": make a complex band using input bands for amplitude and
    4303             :  *            phase values (b1 * exp( j * b2 ))
    4304             :  * - "mod": extract module from a single raster band (real or complex)
    4305             :  * - "phase": extract phase from a single raster band [-PI,PI] (0 or PI for
    4306             :               non-complex)
    4307             :  * - "conj": computes the complex conjugate of a single raster band (just a
    4308             :  *           copy if the input is non-complex)
    4309             :  * - "sum": sum 2 or more raster bands
    4310             :  * - "diff": computes the difference between 2 raster bands (b1 - b2)
    4311             :  * - "mul": multiply 2 or more raster bands
    4312             :  * - "div": divide one raster band by another (b1 / b2).
    4313             :  * - "min": minimum value of 2 or more raster bands
    4314             :  * - "max": maximum value of 2 or more raster bands
    4315             :  * - "norm_diff": computes the normalized difference between two raster bands:
    4316             :  *                ``(b1 - b2)/(b1 + b2)``.
    4317             :  * - "cmul": multiply the first band for the complex conjugate of the second
    4318             :  * - "inv": inverse (1./x).
    4319             :  * - "intensity": computes the intensity Re(x*conj(x)) of a single raster band
    4320             :  *                (real or complex)
    4321             :  * - "sqrt": perform the square root of a single raster band (real only)
    4322             :  * - "log10": compute the logarithm (base 10) of the abs of a single raster
    4323             :  *            band (real or complex): log10( abs( x ) )
    4324             :  * - "dB": perform conversion to dB of the abs of a single raster
    4325             :  *         band (real or complex): 20. * log10( abs( x ) ).
    4326             :  *         Note: the optional fact parameter can be set to 10. to get the
    4327             :  *         alternative formula: 10. * log10( abs( x ) )
    4328             :  * - "exp": computes the exponential of each element in the input band ``x``
    4329             :  *          (of real values): ``e ^ x``.
    4330             :  *          The function also accepts two optional parameters: ``base`` and
    4331             :  ``fact``
    4332             :  *          that allow to compute the generalized formula: ``base ^ ( fact *
    4333             :  x)``.
    4334             :  *          Note: this function is the recommended one to perform conversion
    4335             :  *          form logarithmic scale (dB): `` 10. ^ (x / 20.)``, in this case
    4336             :  *          ``base = 10.`` and ``fact = 1./20``
    4337             :  * - "dB2amp": perform scale conversion from logarithmic to linear
    4338             :  *             (amplitude) (i.e. 10 ^ ( x / 20 ) ) of a single raster
    4339             :  *             band (real only).
    4340             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    4341             :  with
    4342             :  *             ``base = 10.`` and ``fact = 0.05`` i.e. ``1./20``
    4343             :  * - "dB2pow": perform scale conversion from logarithmic to linear
    4344             :  *             (power) (i.e. 10 ^ ( x / 10 ) ) of a single raster
    4345             :  *             band (real only)
    4346             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    4347             :  with
    4348             :  *             ``base = 10.`` and ``fact = 0.1`` i.e. ``1./10``
    4349             :  * - "pow": raise a single raster band to a constant power
    4350             :  * - "interpolate_linear": interpolate values between two raster bands
    4351             :  *                         using linear interpolation
    4352             :  * - "interpolate_exp": interpolate values between two raster bands using
    4353             :  *                      exponential interpolation
    4354             :  * - "scale": Apply the RasterBand metadata values of "offset" and "scale"
    4355             :  * - "reclassify": Reclassify values matching ranges in a table
    4356             :  * - "nan": Convert incoming NoData values to IEEE 754 nan
    4357             :  *
    4358             :  * @see GDALAddDerivedBandPixelFunc
    4359             :  *
    4360             :  * @return CE_None
    4361             :  */
    4362        1576 : CPLErr GDALRegisterDefaultPixelFunc()
    4363             : {
    4364        1576 :     GDALAddDerivedBandPixelFunc("real", RealPixelFunc);
    4365        1576 :     GDALAddDerivedBandPixelFunc("imag", ImagPixelFunc);
    4366        1576 :     GDALAddDerivedBandPixelFunc("complex", ComplexPixelFunc);
    4367        1576 :     GDALAddDerivedBandPixelFuncWithArgs("polar", PolarPixelFunc,
    4368             :                                         pszPolarPixelFuncMetadata);
    4369        1576 :     GDALAddDerivedBandPixelFunc("mod", ModulePixelFunc);
    4370        1576 :     GDALAddDerivedBandPixelFunc("phase", PhasePixelFunc);
    4371        1576 :     GDALAddDerivedBandPixelFunc("conj", ConjPixelFunc);
    4372        1576 :     GDALAddDerivedBandPixelFuncWithArgs("sum", SumPixelFunc,
    4373             :                                         pszSumPixelFuncMetadata);
    4374        1576 :     GDALAddDerivedBandPixelFuncWithArgs("diff", DiffPixelFunc,
    4375             :                                         pszDiffPixelFuncMetadata);
    4376        1576 :     GDALAddDerivedBandPixelFuncWithArgs("mul", MulPixelFunc,
    4377             :                                         pszMulPixelFuncMetadata);
    4378        1576 :     GDALAddDerivedBandPixelFuncWithArgs("div", DivPixelFunc,
    4379             :                                         pszDivPixelFuncMetadata);
    4380        1576 :     GDALAddDerivedBandPixelFunc("cmul", CMulPixelFunc);
    4381        1576 :     GDALAddDerivedBandPixelFuncWithArgs("inv", InvPixelFunc,
    4382             :                                         pszInvPixelFuncMetadata);
    4383        1576 :     GDALAddDerivedBandPixelFunc("intensity", IntensityPixelFunc);
    4384        1576 :     GDALAddDerivedBandPixelFuncWithArgs("sqrt", SqrtPixelFunc,
    4385             :                                         pszSqrtPixelFuncMetadata);
    4386        1576 :     GDALAddDerivedBandPixelFuncWithArgs("log10", Log10PixelFunc,
    4387             :                                         pszLog10PixelFuncMetadata);
    4388        1576 :     GDALAddDerivedBandPixelFuncWithArgs("dB", DBPixelFunc,
    4389             :                                         pszDBPixelFuncMetadata);
    4390        1576 :     GDALAddDerivedBandPixelFuncWithArgs("exp", ExpPixelFunc,
    4391             :                                         pszExpPixelFuncMetadata);
    4392        1576 :     GDALAddDerivedBandPixelFunc("dB2amp",
    4393             :                                 dB2AmpPixelFunc);  // deprecated in v3.5
    4394        1576 :     GDALAddDerivedBandPixelFunc("dB2pow",
    4395             :                                 dB2PowPixelFunc);  // deprecated in v3.5
    4396        1576 :     GDALAddDerivedBandPixelFuncWithArgs("pow", PowPixelFunc,
    4397             :                                         pszPowPixelFuncMetadata);
    4398        1576 :     GDALAddDerivedBandPixelFuncWithArgs("interpolate_linear",
    4399             :                                         InterpolatePixelFunc<InterpolateLinear>,
    4400             :                                         pszInterpolatePixelFuncMetadata);
    4401        1576 :     GDALAddDerivedBandPixelFuncWithArgs(
    4402             :         "interpolate_exp", InterpolatePixelFunc<InterpolateExponential>,
    4403             :         pszInterpolatePixelFuncMetadata);
    4404        1576 :     GDALAddDerivedBandPixelFuncWithArgs("replace_nodata",
    4405             :                                         ReplaceNoDataPixelFunc,
    4406             :                                         pszReplaceNoDataPixelFuncMetadata);
    4407        1576 :     GDALAddDerivedBandPixelFuncWithArgs("scale", ScalePixelFunc,
    4408             :                                         pszScalePixelFuncMetadata);
    4409        1576 :     GDALAddDerivedBandPixelFuncWithArgs("norm_diff", NormDiffPixelFunc,
    4410             :                                         pszNormDiffPixelFuncMetadata);
    4411        1576 :     GDALAddDerivedBandPixelFuncWithArgs("min", MinPixelFunc<ReturnValue>,
    4412             :                                         pszMinMaxFuncMetadataNodata);
    4413        1576 :     GDALAddDerivedBandPixelFuncWithArgs("argmin", MinPixelFunc<ReturnIndex>,
    4414             :                                         pszArgMinMaxFuncMetadataNodata);
    4415        1576 :     GDALAddDerivedBandPixelFuncWithArgs("max", MaxPixelFunc<ReturnValue>,
    4416             :                                         pszMinMaxFuncMetadataNodata);
    4417        1576 :     GDALAddDerivedBandPixelFuncWithArgs("argmax", MaxPixelFunc<ReturnIndex>,
    4418             :                                         pszArgMinMaxFuncMetadataNodata);
    4419        1576 :     GDALAddDerivedBandPixelFuncWithArgs("expression", ExprPixelFunc,
    4420             :                                         pszExprPixelFuncMetadata);
    4421        1576 :     GDALAddDerivedBandPixelFuncWithArgs("reclassify", ReclassifyPixelFunc,
    4422             :                                         pszReclassifyPixelFuncMetadata);
    4423        1576 :     GDALAddDerivedBandPixelFuncWithArgs("round", RoundPixelFunc,
    4424             :                                         pszRoundPixelFuncMetadata);
    4425        1576 :     GDALAddDerivedBandPixelFuncWithArgs("mean", BasicPixelFunc<MeanKernel>,
    4426             :                                         pszBasicPixelFuncMetadata);
    4427        1576 :     GDALAddDerivedBandPixelFuncWithArgs("geometric_mean",
    4428             :                                         BasicPixelFunc<GeoMeanKernel>,
    4429             :                                         pszBasicPixelFuncMetadata);
    4430        1576 :     GDALAddDerivedBandPixelFuncWithArgs("harmonic_mean",
    4431             :                                         BasicPixelFunc<HarmonicMeanKernel>,
    4432             :                                         pszBasicPixelFuncMetadata);
    4433        1576 :     GDALAddDerivedBandPixelFuncWithArgs("median", BasicPixelFunc<MedianKernel>,
    4434             :                                         pszBasicPixelFuncMetadata);
    4435        1576 :     GDALAddDerivedBandPixelFuncWithArgs("quantile",
    4436             :                                         BasicPixelFunc<QuantileKernel>,
    4437             :                                         pszQuantilePixelFuncMetadata);
    4438        1576 :     GDALAddDerivedBandPixelFuncWithArgs("mode", BasicPixelFunc<ModeKernel>,
    4439             :                                         pszBasicPixelFuncMetadata);
    4440        1576 :     GDALAddDerivedBandPixelFuncWithArgs("area", AreaPixelFunc,
    4441             :                                         pszAreaPixelFuncMetadata);
    4442        1576 :     return CE_None;
    4443             : }

Generated by: LCOV version 1.14