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

Generated by: LCOV version 1.14