LCOV - code coverage report
Current view: top level - frmts/vrt - pixelfunctions.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1785 1901 93.9 %
Date: 2026-09-09 22:24:20 Functions: 225 226 99.6 %

          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    26199500 : inline double GetSrcVal(const void *pSource, GDALDataType eSrcType, T ii)
      57             : {
      58    26199500 :     switch (eSrcType)
      59             :     {
      60           0 :         case GDT_Unknown:
      61           0 :             return 0;
      62       62381 :         case GDT_UInt8:
      63       62381 :             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        9816 :         case GDT_Float32:
      84        9816 :             return static_cast<const float *>(pSource)[ii];
      85    26022100 :         case GDT_Float64:
      86    26022100 :             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       10318 : static bool IsNoData(double dfVal, double dfNoData)
     104             : {
     105       10318 :     return dfVal == dfNoData || (std::isnan(dfVal) && std::isnan(dfNoData));
     106             : }
     107             : 
     108        2201 : static CPLErr FetchDoubleArg(CSLConstList papszArgs, const char *pszName,
     109             :                              double *pdfX, double *pdfDefault = nullptr)
     110             : {
     111        2201 :     const char *pszVal = CSLFetchNameValue(papszArgs, pszName);
     112             : 
     113        2201 :     if (pszVal == nullptr)
     114             :     {
     115        1092 :         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        1092 :             *pdfX = *pdfDefault;
     124        1092 :             return CE_None;
     125             :         }
     126             :     }
     127             : 
     128        1109 :     char *pszEnd = nullptr;
     129        1109 :     *pdfX = std::strtod(pszVal, &pszEnd);
     130        1109 :     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        1109 :     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           8 : 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           8 :     if (nSources != 1)
     397           1 :         return CE_Failure;
     398             : 
     399           7 :     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           5 :         double dfNoData{0};
     428           5 :         const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
     429           8 :         if (bHasNoData &&
     430           3 :             FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
     431           0 :             return CE_Failure;
     432             : 
     433             :         /* ---- Set pixels ---- */
     434           5 :         size_t ii = 0;
     435          29 :         for (int iLine = 0; iLine < nYSize; ++iLine)
     436             :         {
     437         431 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
     438             :             {
     439         407 :                 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         413 :                                             ? dfNoData
     444         407 :                                             : std::fabs(dfVal);
     445             : 
     446         407 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
     447             :                               static_cast<GByte *>(pData) +
     448         407 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
     449         407 :                                   iCol * nPixelSpace,
     450             :                               eBufType, nPixelSpace, 1);
     451             :             }
     452             :         }
     453             :     }
     454             : 
     455             :     /* ---- Return success ---- */
     456           7 :     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        1927 : 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        1927 :     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        1926 :     double dfK = 0.0;
    1163        1926 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1164           0 :         return CE_Failure;
    1165             : 
    1166        1926 :     double dfNoData{0};
    1167        1926 :     bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1168        1926 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1169           0 :         return CE_Failure;
    1170             : 
    1171        1926 :     const bool bPropagateNoData = CPLTestBool(
    1172             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    1173             : 
    1174        1926 :     if (dfNoData == 0 && !bPropagateNoData)
    1175        1908 :         bHasNoData = false;
    1176             : 
    1177             :     /* ---- Set pixels ---- */
    1178        1926 :     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        1890 :         bool bGeneralCase = true;
    1214        1890 :         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        1890 :         if (bGeneralCase)
    1308             :         {
    1309         247 :             size_t ii = 0;
    1310         725 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    1311             :             {
    1312       31206 :                 for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1313             :                 {
    1314       30728 :                     double dfSum = dfK;
    1315       92169 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1316             :                     {
    1317             :                         const double dfVal =
    1318       61451 :                             GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1319             : 
    1320       61451 :                         if (bHasNoData && IsNoData(dfVal, dfNoData))
    1321             :                         {
    1322          23 :                             if (bPropagateNoData)
    1323             :                             {
    1324          10 :                                 dfSum = dfNoData;
    1325          10 :                                 break;
    1326             :                             }
    1327             :                         }
    1328             :                         else
    1329             :                         {
    1330       61428 :                             dfSum += dfVal;
    1331             :                         }
    1332             :                     }
    1333             : 
    1334       30728 :                     GDALCopyWords(&dfSum, GDT_Float64, 0,
    1335             :                                   static_cast<GByte *>(pData) +
    1336       30728 :                                       static_cast<GSpacing>(nLineSpace) *
    1337       30728 :                                           iLine +
    1338       30728 :                                       iCol * nPixelSpace,
    1339             :                                   eBufType, nPixelSpace, 1);
    1340             :                 }
    1341             :             }
    1342             :         }
    1343             :     }
    1344             : 
    1345             :     /* ---- Return success ---- */
    1346        1926 :     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           6 : 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           6 :     if (nSources != 2)
    1361           1 :         return CE_Failure;
    1362             : 
    1363           5 :     double dfNoData{0};
    1364           5 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1365           5 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1366           0 :         return CE_Failure;
    1367             : 
    1368           5 :     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           4 :         size_t ii = 0;
    1402          13 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1403             :         {
    1404          45 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1405             :             {
    1406          36 :                 const double dfA = GetSrcVal(papoSources[0], eSrcType, ii);
    1407          36 :                 const double dfB = GetSrcVal(papoSources[1], eSrcType, ii);
    1408             : 
    1409             :                 const double dfPixVal =
    1410          40 :                     bHasNoData &&
    1411           4 :                             (IsNoData(dfA, dfNoData) || IsNoData(dfB, dfNoData))
    1412          40 :                         ? dfNoData
    1413          36 :                         : dfA - dfB;
    1414             : 
    1415          36 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1416             :                               static_cast<GByte *>(pData) +
    1417          36 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1418          36 :                                   iCol * nPixelSpace,
    1419             :                               eBufType, nPixelSpace, 1);
    1420             :             }
    1421             :         }
    1422             :     }
    1423             : 
    1424             :     /* ---- Return success ---- */
    1425           5 :     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          15 : 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          15 :     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          14 :     double dfK = 1.0;
    1452          14 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1453           0 :         return CE_Failure;
    1454             : 
    1455          14 :     double dfNoData{0};
    1456          14 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1457          14 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1458           0 :         return CE_Failure;
    1459             : 
    1460          14 :     const bool bPropagateNoData = CPLTestBool(
    1461             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    1462             : 
    1463             :     /* ---- Set pixels ---- */
    1464          14 :     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          13 :         size_t ii = 0;
    1506          83 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1507             :         {
    1508        1290 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1509             :             {
    1510        1220 :                 double dfPixVal = dfK;  // Not complex.
    1511             : 
    1512        4051 :                 for (int iSrc = 0; iSrc < nSources; ++iSrc)
    1513             :                 {
    1514             :                     const double dfVal =
    1515        2837 :                         GetSrcVal(papoSources[iSrc], eSrcType, ii);
    1516             : 
    1517        2837 :                     if (bHasNoData && IsNoData(dfVal, dfNoData))
    1518             :                     {
    1519          18 :                         if (bPropagateNoData)
    1520             :                         {
    1521           6 :                             dfPixVal = dfNoData;
    1522           6 :                             break;
    1523             :                         }
    1524             :                     }
    1525             :                     else
    1526             :                     {
    1527        2819 :                         dfPixVal *= dfVal;
    1528             :                     }
    1529             :                 }
    1530             : 
    1531        1220 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1532             :                               static_cast<GByte *>(pData) +
    1533        1220 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1534        1220 :                                   iCol * nPixelSpace,
    1535             :                               eBufType, nPixelSpace, 1);
    1536             :             }
    1537             :         }
    1538             :     }
    1539             : 
    1540             :     /* ---- Return success ---- */
    1541          14 :     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           8 : 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           8 :     if (nSources != 2)
    1557           0 :         return CE_Failure;
    1558             : 
    1559           8 :     double dfNoData{0};
    1560           8 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1561           8 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1562           0 :         return CE_Failure;
    1563             : 
    1564             :     /* ---- Set pixels ---- */
    1565           8 :     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           7 :         size_t ii = 0;
    1608          19 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1609             :         {
    1610          51 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1611             :             {
    1612          39 :                 const double dfNum = GetSrcVal(papoSources[0], eSrcType, ii);
    1613          39 :                 const double dfDenom = GetSrcVal(papoSources[1], eSrcType, ii);
    1614             : 
    1615          39 :                 double dfPixVal = dfNoData;
    1616          43 :                 if (!bHasNoData || (!IsNoData(dfNum, dfNoData) &&
    1617           4 :                                     !IsNoData(dfDenom, dfNoData)))
    1618             :                 {
    1619             :                     // coverity[divide_by_zero]
    1620          35 :                     dfPixVal =
    1621             :                         dfDenom == 0
    1622          35 :                             ? 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          39 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1633             :                               static_cast<GByte *>(pData) +
    1634          39 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1635          39 :                                   iCol * nPixelSpace,
    1636             :                               eBufType, nPixelSpace, 1);
    1637             :             }
    1638             :         }
    1639             :     }
    1640             : 
    1641             :     /* ---- Return success ---- */
    1642           8 :     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          13 : 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          13 :     if (nSources != 1)
    1729           1 :         return CE_Failure;
    1730             : 
    1731          12 :     double dfK = 1.0;
    1732          12 :     if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    1733           0 :         return CE_Failure;
    1734             : 
    1735          12 :     double dfNoData{0};
    1736          12 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1737          12 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1738           0 :         return CE_Failure;
    1739             : 
    1740             :     /* ---- Set pixels ---- */
    1741          12 :     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          10 :         size_t ii = 0;
    1775          58 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1776             :         {
    1777         860 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1778             :             {
    1779             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1780             :                 // Not complex.
    1781         812 :                 const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1782         812 :                 double dfPixVal = dfNoData;
    1783             : 
    1784         812 :                 if (!bHasNoData || !IsNoData(dfVal, dfNoData))
    1785             :                 {
    1786         807 :                     dfPixVal =
    1787             :                         dfVal == 0
    1788         807 :                             ? std::numeric_limits<double>::infinity()
    1789         806 :                             : dfK /
    1790             : #ifdef __COVERITY__
    1791             :                                   (dfVal + std::numeric_limits<double>::min())
    1792             : #else
    1793             :                                   dfVal
    1794             : #endif
    1795             :                         ;
    1796             :                 }
    1797             : 
    1798         812 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1799             :                               static_cast<GByte *>(pData) +
    1800         812 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1801         812 :                                   iCol * nPixelSpace,
    1802             :                               eBufType, nPixelSpace, 1);
    1803             :             }
    1804             :         }
    1805             :     }
    1806             : 
    1807             :     /* ---- Return success ---- */
    1808          12 :     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           4 : 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           4 :     if (nSources != 1)
    1884           1 :         return CE_Failure;
    1885           3 :     if (GDALDataTypeIsComplex(eSrcType))
    1886           0 :         return CE_Failure;
    1887             : 
    1888           3 :     double dfNoData{0};
    1889           3 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1890           3 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1891           0 :         return CE_Failure;
    1892             : 
    1893             :     /* ---- Set pixels ---- */
    1894           3 :     size_t ii = 0;
    1895          25 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    1896             :     {
    1897         425 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1898             :         {
    1899             :             // Source raster pixels may be obtained with GetSrcVal macro.
    1900         403 :             double dfPixVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1901             : 
    1902         403 :             if (bHasNoData && IsNoData(dfPixVal, dfNoData))
    1903             :             {
    1904           2 :                 dfPixVal = dfNoData;
    1905             :             }
    1906             :             else
    1907             :             {
    1908         401 :                 dfPixVal = std::sqrt(dfPixVal);
    1909             :             }
    1910             : 
    1911         403 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1912             :                           static_cast<GByte *>(pData) +
    1913         403 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    1914         403 :                               iCol * nPixelSpace,
    1915             :                           eBufType, nPixelSpace, 1);
    1916             :         }
    1917             :     }
    1918             : 
    1919             :     /* ---- Return success ---- */
    1920           3 :     return CE_None;
    1921             : }  // SqrtPixelFunc
    1922             : 
    1923          14 : 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          14 :     if (nSources != 1)
    1931           2 :         return CE_Failure;
    1932             : 
    1933          12 :     double dfNoData{0};
    1934          12 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    1935          12 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    1936           0 :         return CE_Failure;
    1937             : 
    1938          12 :     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           7 :         size_t ii = 0;
    1977          90 :         for (int iLine = 0; iLine < nYSize; ++iLine)
    1978             :         {
    1979        1688 :             for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    1980             :             {
    1981             :                 // Source raster pixels may be obtained with GetSrcVal macro.
    1982        1605 :                 const double dfSrcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    1983             :                 const double dfPixVal =
    1984           4 :                     bHasNoData && IsNoData(dfSrcVal, dfNoData)
    1985        1609 :                         ? dfNoData
    1986        1601 :                         : fact * std::log10(std::abs(dfSrcVal));
    1987             : 
    1988        1605 :                 GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    1989             :                               static_cast<GByte *>(pData) +
    1990        1605 :                                   static_cast<GSpacing>(nLineSpace) * iLine +
    1991        1605 :                                   iCol * nPixelSpace,
    1992             :                               eBufType, nPixelSpace, 1);
    1993             :             }
    1994             :         }
    1995             :     }
    1996             : 
    1997             :     /* ---- Return success ---- */
    1998          12 :     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           6 : 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           6 :     return Log10PixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2012             :                                 eSrcType, eBufType, nPixelSpace, nLineSpace,
    2013           6 :                                 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           9 : 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           9 :     if (nSources != 1)
    2045           2 :         return CE_Failure;
    2046           7 :     if (GDALDataTypeIsComplex(eSrcType))
    2047           0 :         return CE_Failure;
    2048             : 
    2049           7 :     double dfNoData{0};
    2050           7 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2051           7 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2052           0 :         return CE_Failure;
    2053             : 
    2054             :     /* ---- Set pixels ---- */
    2055           7 :     size_t ii = 0;
    2056         109 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2057             :     {
    2058        2105 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2059             :         {
    2060             :             // Source raster pixels may be obtained with GetSrcVal macro.
    2061        2003 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2062           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2063        2005 :                                         ? dfNoData
    2064        2001 :                                         : pow(base, dfVal * fact);
    2065             : 
    2066        2003 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2067             :                           static_cast<GByte *>(pData) +
    2068        2003 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2069        2003 :                               iCol * nPixelSpace,
    2070             :                           eBufType, nPixelSpace, 1);
    2071             :         }
    2072             :     }
    2073             : 
    2074             :     /* ---- Return success ---- */
    2075           7 :     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           5 : 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           5 :     double dfBase = 2.7182818284590452353602874713526624;
    2092           5 :     double dfFact = 1.;
    2093             : 
    2094           5 :     if (FetchDoubleArg(papszArgs, "base", &dfBase, &dfBase) != CE_None)
    2095           0 :         return CE_Failure;
    2096             : 
    2097           5 :     if (FetchDoubleArg(papszArgs, "fact", &dfFact, &dfFact) != CE_None)
    2098           0 :         return CE_Failure;
    2099             : 
    2100           5 :     return ExpPixelFuncHelper(papoSources, nSources, pData, nXSize, nYSize,
    2101             :                               eSrcType, eBufType, nPixelSpace, nLineSpace,
    2102           5 :                               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           3 : 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           3 :     if (nSources != 1)
    2139           0 :         return CE_Failure;
    2140           3 :     if (GDALDataTypeIsComplex(eSrcType))
    2141           0 :         return CE_Failure;
    2142             : 
    2143             :     double power;
    2144           3 :     if (FetchDoubleArg(papszArgs, "power", &power) != CE_None)
    2145           0 :         return CE_Failure;
    2146             : 
    2147           3 :     double dfNoData{0};
    2148           3 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2149           3 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2150           0 :         return CE_Failure;
    2151             : 
    2152             :     /* ---- Set pixels ---- */
    2153           3 :     size_t ii = 0;
    2154          25 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2155             :     {
    2156         425 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2157             :         {
    2158         403 :             const double dfVal = GetSrcVal(papoSources[0], eSrcType, ii);
    2159             : 
    2160           2 :             const double dfPixVal = bHasNoData && IsNoData(dfVal, dfNoData)
    2161         405 :                                         ? dfNoData
    2162         401 :                                         : std::pow(dfVal, power);
    2163             : 
    2164         403 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    2165             :                           static_cast<GByte *>(pData) +
    2166         403 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2167         403 :                               iCol * nPixelSpace,
    2168             :                           eBufType, nPixelSpace, 1);
    2169             :         }
    2170             :     }
    2171             : 
    2172             :     /* ---- Return success ---- */
    2173           3 :     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          32 : 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          32 :     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          32 :     double dfNoData = std::numeric_limits<double>::quiet_NaN();
    2517          32 :     if (FetchDoubleArg(papszArgs, "NoData", &dfNoData, &dfNoData) != CE_None)
    2518           0 :         return CE_Failure;
    2519          32 :     const bool bPropagateNoData = CPLTestBool(
    2520             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2521             : 
    2522             :     /* ---- Set pixels ---- */
    2523          32 :     size_t ii = 0;
    2524         260 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2525             :     {
    2526       10278 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    2527             :         {
    2528       10050 :             double dfRes = std::numeric_limits<double>::quiet_NaN();
    2529       10050 :             double dfResSrc = std::numeric_limits<double>::quiet_NaN();
    2530             : 
    2531       33568 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    2532             :             {
    2533       25580 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    2534             : 
    2535       25580 :                 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       11155 :                 else if (Comparator::compare(dfVal, dfRes))
    2548             :                 {
    2549        6588 :                     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       10038 :                 if (std::isnan(dfRes))
    2565             :                 {
    2566        3211 :                     dfRes = dfNoData;
    2567             :                 }
    2568             : 
    2569       10038 :                 if (IsNoData(dfRes, dfNoData))
    2570             :                 {
    2571        5269 :                     if (!bPropagateNoData && !std::isnan(dfK))
    2572             :                     {
    2573           6 :                         dfRes = dfK;
    2574             :                     }
    2575             :                 }
    2576        4769 :                 else if (!std::isnan(dfK) && Comparator::compare(dfK, dfRes))
    2577             :                 {
    2578           6 :                     dfRes = dfK;
    2579             :                 }
    2580             :             }
    2581             : 
    2582       10050 :             GDALCopyWords(&dfRes, GDT_Float64, 0,
    2583             :                           static_cast<GByte *>(pData) +
    2584       10050 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    2585       10050 :                               iCol * nPixelSpace,
    2586             :                           eBufType, nPixelSpace, 1);
    2587             :         }
    2588             :     }
    2589             : 
    2590             :     /* ---- Return success ---- */
    2591          32 :     return CE_None;
    2592             : } /* MinOrMaxPixelFunc */
    2593             : 
    2594             : #ifdef USE_SSE2
    2595             : 
    2596             : template <class T, class SSEWrapper>
    2597          23 : static void OptimizedMinOrMaxSSE2(const void *const *papoSources, int nSources,
    2598             :                                   void *pData, int nXSize, int nYSize,
    2599             :                                   int nLineSpace)
    2600             : {
    2601          23 :     assert(nSources >= 1);
    2602          23 :     constexpr int VALUES_PER_REG =
    2603             :         static_cast<int>(sizeof(typename SSEWrapper::Vec) / sizeof(T));
    2604         585 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    2605             :     {
    2606         562 :         T *CPL_RESTRICT pDest =
    2607             :             reinterpret_cast<T *>(static_cast<GByte *>(pData) +
    2608         562 :                                   static_cast<GSpacing>(nLineSpace) * iLine);
    2609         562 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    2610         562 :         int iCol = 0;
    2611        3124 :         for (; iCol < nXSize - (2 * VALUES_PER_REG - 1);
    2612             :              iCol += 2 * VALUES_PER_REG)
    2613             :         {
    2614        2562 :             auto reg0 = SSEWrapper::LoadU(
    2615        2562 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    2616        2562 :                 iOffsetLine + iCol);
    2617        2562 :             auto reg1 = SSEWrapper::LoadU(
    2618        2562 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    2619        2562 :                 iOffsetLine + iCol + VALUES_PER_REG);
    2620        5186 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    2621             :             {
    2622        2624 :                 reg0 = SSEWrapper::MinOrMax(
    2623        2624 :                     reg0, SSEWrapper::LoadU(static_cast<const T * CPL_RESTRICT>(
    2624        2624 :                                                 papoSources[iSrc]) +
    2625        2624 :                                             iOffsetLine + iCol));
    2626        2624 :                 reg1 = SSEWrapper::MinOrMax(
    2627             :                     reg1,
    2628             :                     SSEWrapper::LoadU(
    2629        2624 :                         static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    2630        2624 :                         iOffsetLine + iCol + VALUES_PER_REG));
    2631             :             }
    2632        2562 :             SSEWrapper::StoreU(pDest + iCol, reg0);
    2633        2562 :             SSEWrapper::StoreU(pDest + iCol + VALUES_PER_REG, reg1);
    2634             :         }
    2635        4072 :         for (; iCol < nXSize; ++iCol)
    2636             :         {
    2637        3510 :             T v = static_cast<const T * CPL_RESTRICT>(
    2638        3510 :                 papoSources[0])[iOffsetLine + iCol];
    2639        7926 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    2640             :             {
    2641        4416 :                 v = SSEWrapper::MinOrMax(
    2642        4416 :                     v, static_cast<const T * CPL_RESTRICT>(
    2643        4416 :                            papoSources[iSrc])[iOffsetLine + iCol]);
    2644             :             }
    2645        3510 :             pDest[iCol] = v;
    2646             :         }
    2647             :     }
    2648          23 : }
    2649             : 
    2650             : /************************************************************************/
    2651             : /*                    NaNAwareMinOrMaxFloat/Double()                    */
    2652             : /************************************************************************/
    2653             : 
    2654        1216 : static inline __m128 NaNAwareMinOrMaxFloat(__m128 x, __m128 y, bool bMin)
    2655             : {
    2656        1216 :     const __m128 xIsNaN = _mm_cmpunord_ps(x, x);
    2657        1216 :     const __m128 yIsNaN = _mm_cmpunord_ps(y, y);
    2658             :     const __m128 xx =
    2659        3648 :         _mm_or_ps(_mm_andnot_ps(xIsNaN, x), _mm_and_ps(xIsNaN, y));
    2660             :     const __m128 yy =
    2661        2432 :         _mm_or_ps(_mm_andnot_ps(yIsNaN, y), _mm_and_ps(yIsNaN, x));
    2662        2432 :     return bMin ? _mm_min_ps(xx, yy) : _mm_max_ps(xx, yy);
    2663             : }
    2664             : 
    2665        2432 : static inline __m128d NaNAwareMinOrMaxDouble(__m128d x, __m128d y, bool bMin)
    2666             : {
    2667        2432 :     const __m128d xIsNaN = _mm_cmpunord_pd(x, x);
    2668        2432 :     const __m128d yIsNaN = _mm_cmpunord_pd(y, y);
    2669             :     const __m128d xx =
    2670        7296 :         _mm_or_pd(_mm_andnot_pd(xIsNaN, x), _mm_and_pd(xIsNaN, y));
    2671             :     const __m128d yy =
    2672        4864 :         _mm_or_pd(_mm_andnot_pd(yIsNaN, y), _mm_and_pd(yIsNaN, x));
    2673        4864 :     return bMin ? _mm_min_pd(xx, yy) : _mm_max_pd(xx, yy);
    2674             : }
    2675             : 
    2676         408 : template <class T> static inline T NaNAwareMinOrMax(T x, T y, bool bMin)
    2677             : {
    2678         408 :     if (std::isnan(x))
    2679           0 :         return y;
    2680         408 :     if (std::isnan(y))
    2681           0 :         return x;
    2682         408 :     return bMin ? std::min(x, y) : std::max(x, y);
    2683             : }
    2684             : 
    2685             : // clang-format off
    2686             : namespace
    2687             : {
    2688             : struct SSEWrapperMinByte
    2689             : {
    2690             :     using T = uint8_t;
    2691             :     typedef __m128i Vec;
    2692             : 
    2693         400 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2694         100 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2695         200 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epu8(x, y); }
    2696         904 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2697             : };
    2698             : 
    2699             : struct SSEWrapperMaxByte
    2700             : {
    2701             :     using T = uint8_t;
    2702             :     typedef __m128i Vec;
    2703             : 
    2704        1000 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2705         200 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2706         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epu8(x, y); }
    2707        2704 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2708             : };
    2709             : 
    2710             : struct SSEWrapperMinUInt16
    2711             : {
    2712             :     using T = uint16_t;
    2713             :     typedef __m128i Vec;
    2714             : 
    2715        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2716         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2717             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
    2718             :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epu16(x, y); }
    2719             : #else
    2720         300 :     static inline Vec MinOrMax(Vec x, Vec y) { return
    2721        1800 :         _mm_add_epi16(
    2722             :            _mm_min_epi16(
    2723             :              _mm_add_epi16(x, _mm_set1_epi16(-32768)),
    2724             :              _mm_add_epi16(y, _mm_set1_epi16(-32768))),
    2725         300 :            _mm_set1_epi16(-32768)); }
    2726             : #endif
    2727         100 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2728             : };
    2729             : 
    2730             : struct SSEWrapperMaxUInt16
    2731             : {
    2732             :     using T = uint16_t;
    2733             :     typedef __m128i Vec;
    2734             : 
    2735        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2736         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2737             : #if defined(__SSE4_1__) || defined(USE_NEON_OPTIMIZATIONS)
    2738             :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epu16(x, y); }
    2739             : #else
    2740         300 :     static inline Vec MinOrMax(Vec x, Vec y) { return
    2741        1800 :         _mm_add_epi16(
    2742             :            _mm_max_epi16(
    2743             :              _mm_add_epi16(x, _mm_set1_epi16(-32768)),
    2744             :              _mm_add_epi16(y, _mm_set1_epi16(-32768))),
    2745         300 :            _mm_set1_epi16(-32768)); }
    2746             : #endif
    2747         100 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2748             : };
    2749             : 
    2750             : struct SSEWrapperMinInt16
    2751             : {
    2752             :     using T = int16_t;
    2753             :     typedef __m128i Vec;
    2754             : 
    2755        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2756         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2757         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_min_epi16(x, y); }
    2758         100 :     static inline T MinOrMax(T x, T y) { return std::min(x, y); }
    2759             : };
    2760             : 
    2761             : struct SSEWrapperMaxInt16
    2762             : {
    2763             :     using T = int16_t;
    2764             :     typedef __m128i Vec;
    2765             : 
    2766        1200 :     static inline Vec LoadU(const T *x) { return _mm_loadu_si128(reinterpret_cast<const Vec*>(x)); }
    2767         300 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_si128(reinterpret_cast<Vec*>(x), y); }
    2768         600 :     static inline Vec MinOrMax(Vec x, Vec y) { return _mm_max_epi16(x, y); }
    2769         100 :     static inline T MinOrMax(T x, T y) { return std::max(x, y); }
    2770             : };
    2771             : 
    2772             : struct SSEWrapperMinFloat
    2773             : {
    2774             :     using T = float;
    2775             :     typedef __m128 Vec;
    2776             : 
    2777        2424 :     static inline Vec LoadU(const T *x) { return _mm_loadu_ps(x); }
    2778         604 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_ps(x, y); }
    2779         608 :     static inline Vec MinOrMax(Vec x, Vec y) { return NaNAwareMinOrMaxFloat(x, y, true); }
    2780         100 :     static inline T MinOrMax(T x, T y) { return NaNAwareMinOrMax(x, y, true); }
    2781             : };
    2782             : 
    2783             : struct SSEWrapperMaxFloat
    2784             : {
    2785             :     using T = float;
    2786             :     typedef __m128 Vec;
    2787             : 
    2788        2424 :     static inline Vec LoadU(const T *x) { return _mm_loadu_ps(x); }
    2789         604 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_ps(x, y); }
    2790         608 :     static inline Vec MinOrMax(Vec x, Vec y) { return NaNAwareMinOrMaxFloat(x, y, false); }
    2791         100 :     static inline T MinOrMax(T x, T y) { return NaNAwareMinOrMax(x, y, false); }
    2792             : };
    2793             : 
    2794             : struct SSEWrapperMinDouble
    2795             : {
    2796             :     using T = double;
    2797             :     typedef __m128d Vec;
    2798             : 
    2799        4848 :     static inline Vec LoadU(const T *x) { return _mm_loadu_pd(x); }
    2800        1208 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_pd(x, y); }
    2801        1216 :     static inline Vec MinOrMax(Vec x, Vec y) { return NaNAwareMinOrMaxDouble(x, y, true); }
    2802         104 :     static inline T MinOrMax(T x, T y) { return NaNAwareMinOrMax(x, y, true); }
    2803             : };
    2804             : 
    2805             : struct SSEWrapperMaxDouble
    2806             : {
    2807             :     using T = double;
    2808             :     typedef __m128d Vec;
    2809             : 
    2810        4848 :     static inline Vec LoadU(const T *x) { return _mm_loadu_pd(x); }
    2811        1208 :     static inline void StoreU(T *x, Vec y) { _mm_storeu_pd(x, y); }
    2812        1216 :     static inline Vec MinOrMax(Vec x, Vec y) { return NaNAwareMinOrMaxDouble(x, y, false); }
    2813         104 :     static inline T MinOrMax(T x, T y) { return NaNAwareMinOrMax(x, y, false); }
    2814             : };
    2815             : 
    2816             : }  // namespace
    2817             : 
    2818             : // clang-format on
    2819             : 
    2820             : #endif  // USE_SSE2
    2821             : 
    2822             : template <typename ReturnType>
    2823          30 : static CPLErr MinPixelFunc(void **papoSources, int nSources, void *pData,
    2824             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2825             :                            GDALDataType eBufType, int nPixelSpace,
    2826             :                            int nLineSpace, CSLConstList papszArgs)
    2827             : {
    2828             :     struct Comparator
    2829             :     {
    2830        2738 :         static bool compare(double x, double resVal)
    2831             :         {
    2832             :             // Written this way to deal with resVal being NaN
    2833        2738 :             return !(x >= resVal);
    2834             :         }
    2835             :     };
    2836             : 
    2837          30 :     double dfK = std::numeric_limits<double>::quiet_NaN();
    2838             :     if constexpr (std::is_same_v<ReturnType, ReturnValue>)
    2839             :     {
    2840          27 :         if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    2841           0 :             return CE_Failure;
    2842             : 
    2843             : #ifdef USE_SSE2
    2844          27 :         const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2845          46 :         if (std::isnan(dfK) && nSources > 0 && !bHasNoData &&
    2846          46 :             eSrcType == eBufType &&
    2847          11 :             nPixelSpace == GDALGetDataTypeSizeBytes(eSrcType))
    2848             :         {
    2849          11 :             if (eSrcType == GDT_UInt8)
    2850             :             {
    2851           3 :                 OptimizedMinOrMaxSSE2<uint8_t, SSEWrapperMinByte>(
    2852             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2853           3 :                 return CE_None;
    2854             :             }
    2855           8 :             else if (eSrcType == GDT_UInt16)
    2856             :             {
    2857           1 :                 OptimizedMinOrMaxSSE2<uint16_t, SSEWrapperMinUInt16>(
    2858             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2859           1 :                 return CE_None;
    2860             :             }
    2861           7 :             else if (eSrcType == GDT_Int16)
    2862             :             {
    2863           1 :                 OptimizedMinOrMaxSSE2<int16_t, SSEWrapperMinInt16>(
    2864             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2865           1 :                 return CE_None;
    2866             :             }
    2867           6 :             else if (eSrcType == GDT_Float32)
    2868             :             {
    2869           2 :                 OptimizedMinOrMaxSSE2<float, SSEWrapperMinFloat>(
    2870             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2871           2 :                 return CE_None;
    2872             :             }
    2873           4 :             else if (eSrcType == GDT_Float64)
    2874             :             {
    2875           4 :                 OptimizedMinOrMaxSSE2<double, SSEWrapperMinDouble>(
    2876             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2877           4 :                 return CE_None;
    2878             :             }
    2879             :         }
    2880             : #endif
    2881             :     }
    2882             : 
    2883          19 :     return MinOrMaxPixelFunc<Comparator, ReturnType>(
    2884             :         dfK, papoSources, nSources, pData, nXSize, nYSize, eSrcType, eBufType,
    2885          22 :         nPixelSpace, nLineSpace, papszArgs);
    2886             : }
    2887             : 
    2888             : template <typename ReturnType>
    2889          25 : static CPLErr MaxPixelFunc(void **papoSources, int nSources, void *pData,
    2890             :                            int nXSize, int nYSize, GDALDataType eSrcType,
    2891             :                            GDALDataType eBufType, int nPixelSpace,
    2892             :                            int nLineSpace, CSLConstList papszArgs)
    2893             : {
    2894             :     struct Comparator
    2895             :     {
    2896        8429 :         static bool compare(double x, double resVal)
    2897             :         {
    2898             :             // Written this way to deal with resVal being NaN
    2899        8429 :             return !(x <= resVal);
    2900             :         }
    2901             :     };
    2902             : 
    2903          25 :     double dfK = std::numeric_limits<double>::quiet_NaN();
    2904             :     if constexpr (std::is_same_v<ReturnType, ReturnValue>)
    2905             :     {
    2906          22 :         if (FetchDoubleArg(papszArgs, "k", &dfK, &dfK) != CE_None)
    2907           0 :             return CE_Failure;
    2908             : 
    2909             : #ifdef USE_SSE2
    2910          22 :         const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2911          38 :         if (std::isnan(dfK) && nSources > 0 && !bHasNoData &&
    2912          38 :             eSrcType == eBufType &&
    2913          12 :             nPixelSpace == GDALGetDataTypeSizeBytes(eSrcType))
    2914             :         {
    2915          12 :             if (eSrcType == GDT_UInt8)
    2916             :             {
    2917           4 :                 OptimizedMinOrMaxSSE2<uint8_t, SSEWrapperMaxByte>(
    2918             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2919           4 :                 return CE_None;
    2920             :             }
    2921           8 :             else if (eSrcType == GDT_UInt16)
    2922             :             {
    2923           1 :                 OptimizedMinOrMaxSSE2<uint16_t, SSEWrapperMaxUInt16>(
    2924             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2925           1 :                 return CE_None;
    2926             :             }
    2927           7 :             else if (eSrcType == GDT_Int16)
    2928             :             {
    2929           1 :                 OptimizedMinOrMaxSSE2<int16_t, SSEWrapperMaxInt16>(
    2930             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2931           1 :                 return CE_None;
    2932             :             }
    2933           6 :             else if (eSrcType == GDT_Float32)
    2934             :             {
    2935           2 :                 OptimizedMinOrMaxSSE2<float, SSEWrapperMaxFloat>(
    2936             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2937           2 :                 return CE_None;
    2938             :             }
    2939           4 :             else if (eSrcType == GDT_Float64)
    2940             :             {
    2941           4 :                 OptimizedMinOrMaxSSE2<double, SSEWrapperMaxDouble>(
    2942             :                     papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    2943           4 :                 return CE_None;
    2944             :             }
    2945             :         }
    2946             : #endif
    2947             :     }
    2948             : 
    2949          13 :     return MinOrMaxPixelFunc<Comparator, ReturnType>(
    2950             :         dfK, papoSources, nSources, pData, nXSize, nYSize, eSrcType, eBufType,
    2951          16 :         nPixelSpace, nLineSpace, papszArgs);
    2952             : }
    2953             : 
    2954             : static const char pszExprPixelFuncMetadata[] =
    2955             :     "<PixelFunctionArgumentsList>"
    2956             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    2957             :     "   <Argument name='propagateNoData' description='Whether the output value "
    2958             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    2959             :     "default='false' />"
    2960             :     "   <Argument name='expression' "
    2961             :     "             description='Expression to be evaluated' "
    2962             :     "             type='string'></Argument>"
    2963             :     "   <Argument name='dialect' "
    2964             :     "             description='Expression dialect' "
    2965             :     "             type='string-select'"
    2966             :     "             default='muparser'>"
    2967             :     "       <Value>exprtk</Value>"
    2968             :     "       <Value>muparser</Value>"
    2969             :     "   </Argument>"
    2970             :     "   <Argument type='builtin' value='source_names' />"
    2971             :     "   <Argument type='builtin' value='xoff' />"
    2972             :     "   <Argument type='builtin' value='yoff' />"
    2973             :     "   <Argument type='builtin' value='geotransform' />"
    2974             :     "</PixelFunctionArgumentsList>";
    2975             : 
    2976         367 : static CPLErr ExprPixelFunc(void **papoSources, int nSources, void *pData,
    2977             :                             int nXSize, int nYSize, GDALDataType eSrcType,
    2978             :                             GDALDataType eBufType, int nPixelSpace,
    2979             :                             int nLineSpace, CSLConstList papszArgs)
    2980             : {
    2981             :     /* ---- Init ---- */
    2982         367 :     if (GDALDataTypeIsComplex(eSrcType))
    2983             :     {
    2984           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2985             :                  "expression cannot by applied to complex data types");
    2986           0 :         return CE_Failure;
    2987             :     }
    2988             : 
    2989         367 :     double dfNoData{0};
    2990         367 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    2991         367 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    2992           0 :         return CE_Failure;
    2993             : 
    2994         367 :     const bool bPropagateNoData = CPLTestBool(
    2995             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    2996             : 
    2997         367 :     const char *pszExpression = CSLFetchNameValue(papszArgs, "expression");
    2998         367 :     if (!pszExpression)
    2999             :     {
    3000           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3001             :                  "Missing 'expression' pixel function argument");
    3002           1 :         return CE_Failure;
    3003             :     }
    3004             : 
    3005         366 :     const char *pszSourceNames = CSLFetchNameValue(papszArgs, "source_names");
    3006             :     const CPLStringList aosSourceNames(
    3007         732 :         CSLTokenizeString2(pszSourceNames, "|", 0));
    3008         366 :     if (aosSourceNames.size() != nSources)
    3009             :     {
    3010           7 :         CPLError(CE_Failure, CPLE_AppDefined,
    3011             :                  "The source_names variable passed to ExprPixelFunc() has %d "
    3012             :                  "values, whereas %d were expected. An invalid variable name "
    3013             :                  "has likely been used",
    3014             :                  aosSourceNames.size(), nSources);
    3015           7 :         return CE_Failure;
    3016             :     }
    3017             : 
    3018         718 :     std::vector<double> adfValuesForPixel(nSources);
    3019             : 
    3020         359 :     const char *pszDialect = CSLFetchNameValue(papszArgs, "dialect");
    3021         359 :     if (!pszDialect)
    3022             :     {
    3023         220 :         pszDialect = "muparser";
    3024             :     }
    3025             : 
    3026         718 :     auto poExpression = gdal::MathExpression::Create(pszExpression, pszDialect);
    3027             : 
    3028             :     // cppcheck-suppress knownConditionTrueFalse
    3029         359 :     if (!poExpression)
    3030             :     {
    3031           0 :         return CE_Failure;
    3032             :     }
    3033             : 
    3034         359 :     int nXOff = 0;
    3035         359 :     int nYOff = 0;
    3036         359 :     GDALGeoTransform gt;
    3037         359 :     double dfCenterX = 0;
    3038         359 :     double dfCenterY = 0;
    3039             : 
    3040         359 :     bool includeCenterCoords = false;
    3041         359 :     if (strstr(pszExpression, "_CENTER_X_") ||
    3042         357 :         strstr(pszExpression, "_CENTER_Y_"))
    3043             :     {
    3044           2 :         includeCenterCoords = true;
    3045             : 
    3046           2 :         const char *pszXOff = CSLFetchNameValue(papszArgs, "xoff");
    3047           2 :         nXOff = std::atoi(pszXOff);
    3048             : 
    3049           2 :         const char *pszYOff = CSLFetchNameValue(papszArgs, "yoff");
    3050           2 :         nYOff = std::atoi(pszYOff);
    3051             : 
    3052           2 :         const char *pszGT = CSLFetchNameValue(papszArgs, "geotransform");
    3053           2 :         if (pszGT == nullptr)
    3054             :         {
    3055           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    3056             :                      "To use _CENTER_X_ or _CENTER_Y_ in an expression, "
    3057             :                      "VRTDataset must have a <GeoTransform> element.");
    3058           1 :             return CE_Failure;
    3059             :         }
    3060             : 
    3061           1 :         if (!gt.Init(pszGT))
    3062             :         {
    3063           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3064             :                      "Invalid GeoTransform argument");
    3065           0 :             return CE_Failure;
    3066             :         }
    3067             :     }
    3068             : 
    3069             :     {
    3070         358 :         int iSource = 0;
    3071         939 :         for (const auto &osName : aosSourceNames)
    3072             :         {
    3073        1162 :             poExpression->RegisterVariable(osName,
    3074         581 :                                            &adfValuesForPixel[iSource++]);
    3075             :         }
    3076             :     }
    3077             : 
    3078         358 :     if (includeCenterCoords)
    3079             :     {
    3080           1 :         poExpression->RegisterVariable("_CENTER_X_", &dfCenterX);
    3081           1 :         poExpression->RegisterVariable("_CENTER_Y_", &dfCenterY);
    3082             :     }
    3083             : 
    3084         358 :     if (bHasNoData)
    3085             :     {
    3086          10 :         poExpression->RegisterVariable("NODATA", &dfNoData);
    3087             :     }
    3088             : 
    3089         358 :     if (strstr(pszExpression, "BANDS"))
    3090             :     {
    3091           2 :         poExpression->RegisterVector("BANDS", &adfValuesForPixel);
    3092             :     }
    3093             : 
    3094             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    3095         716 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    3096         358 :     if (!padfResults)
    3097           0 :         return CE_Failure;
    3098             : 
    3099             :     /* ---- Set pixels ---- */
    3100         358 :     size_t ii = 0;
    3101        5343 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3102             :     {
    3103    13007500 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    3104             :         {
    3105    13002500 :             double &dfResult = padfResults.get()[iCol];
    3106    13002500 :             bool resultIsNoData = false;
    3107             : 
    3108    39020600 :             for (int iSrc = 0; iSrc < nSources; iSrc++)
    3109             :             {
    3110             :                 // cppcheck-suppress unreadVariable
    3111    26018100 :                 double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    3112             : 
    3113    26018100 :                 if (bHasNoData && bPropagateNoData && IsNoData(dfVal, dfNoData))
    3114             :                 {
    3115           1 :                     resultIsNoData = true;
    3116             :                 }
    3117             : 
    3118    26018100 :                 adfValuesForPixel[iSrc] = dfVal;
    3119             :             }
    3120             : 
    3121    13002500 :             if (includeCenterCoords)
    3122             :             {
    3123             :                 // Add 0.5 to pixel / line to move from pixel corner to cell center
    3124         400 :                 gt.Apply(static_cast<double>(iCol + nXOff) + 0.5,
    3125         400 :                          static_cast<double>(iLine + nYOff) + 0.5, &dfCenterX,
    3126             :                          &dfCenterY);
    3127             :             }
    3128             : 
    3129    13002500 :             if (resultIsNoData)
    3130             :             {
    3131           1 :                 dfResult = dfNoData;
    3132             :             }
    3133             :             else
    3134             :             {
    3135    13002500 :                 if (auto eErr = poExpression->Evaluate(); eErr != CE_None)
    3136             :                 {
    3137           5 :                     return CE_Failure;
    3138             :                 }
    3139             : 
    3140    13002500 :                 dfResult = poExpression->Results()[0];
    3141             :             }
    3142             :         }
    3143             : 
    3144        4985 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    3145             :                       static_cast<GByte *>(pData) +
    3146        4985 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    3147             :                       eBufType, nPixelSpace, nXSize);
    3148             :     }
    3149             : 
    3150             :     /* ---- Return success ---- */
    3151         353 :     return CE_None;
    3152             : }  // ExprPixelFunc
    3153             : 
    3154             : static constexpr char pszAreaPixelFuncMetadata[] =
    3155             :     "<PixelFunctionArgumentsList>"
    3156             :     "   <Argument type='builtin' value='crs' />"
    3157             :     "   <Argument type='builtin' value='xoff' />"
    3158             :     "   <Argument type='builtin' value='yoff' />"
    3159             :     "   <Argument type='builtin' value='geotransform' />"
    3160             :     "</PixelFunctionArgumentsList>";
    3161             : 
    3162           4 : static CPLErr AreaPixelFunc(void ** /*papoSources*/, int nSources, void *pData,
    3163             :                             int nXSize, int nYSize, GDALDataType /* eSrcType */,
    3164             :                             GDALDataType eBufType, int nPixelSpace,
    3165             :                             int nLineSpace, CSLConstList papszArgs)
    3166             : {
    3167           4 :     if (nSources)
    3168             :     {
    3169           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3170             :                  "area: unexpected source band(s)");
    3171           1 :         return CE_Failure;
    3172             :     }
    3173             : 
    3174           3 :     const char *pszGT = CSLFetchNameValue(papszArgs, "geotransform");
    3175           3 :     if (pszGT == nullptr)
    3176             :     {
    3177           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3178             :                  "area: VRTDataset has no <GeoTransform>");
    3179           1 :         return CE_Failure;
    3180             :     }
    3181             : 
    3182           2 :     GDALGeoTransform gt;
    3183           2 :     if (!gt.Init(pszGT))
    3184             :     {
    3185           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3186             :                  "area: Invalid GeoTransform argument");
    3187           0 :         return CE_Failure;
    3188             :     }
    3189             : 
    3190           2 :     const char *pszXOff = CSLFetchNameValue(papszArgs, "xoff");
    3191           2 :     const int nXOff = std::atoi(pszXOff);
    3192             : 
    3193           2 :     const char *pszYOff = CSLFetchNameValue(papszArgs, "yoff");
    3194           2 :     const int nYOff = std::atoi(pszYOff);
    3195             : 
    3196           2 :     const char *pszCrsPtr = CSLFetchNameValue(papszArgs, "crs");
    3197             : 
    3198           2 :     if (!pszCrsPtr)
    3199             :     {
    3200           0 :         CPLError(CE_Failure, CPLE_AppDefined, "area: VRTDataset has no <SRS>");
    3201           0 :         return CE_Failure;
    3202             :     }
    3203             : 
    3204           2 :     std::uintptr_t nCrsPtr = 0;
    3205           2 :     if (auto [end, ec] =
    3206           2 :             std::from_chars(pszCrsPtr, pszCrsPtr + strlen(pszCrsPtr), nCrsPtr);
    3207             :         ec != std::errc())
    3208             :     {
    3209             :         // Since "crs" is populated by GDAL, this should never happen.
    3210           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read CRS");
    3211           0 :         return CE_Failure;
    3212             :     }
    3213             : 
    3214           2 :     const OGRSpatialReference *poCRS =
    3215             :         reinterpret_cast<const OGRSpatialReference *>(nCrsPtr);
    3216             : 
    3217           2 :     if (!poCRS)
    3218             :     {
    3219             :         // can't get here, but cppcheck doesn't know that
    3220           0 :         return CE_Failure;
    3221             :     }
    3222             : 
    3223           2 :     const OGRSpatialReference *poGeographicCRS = nullptr;
    3224           2 :     std::unique_ptr<OGRSpatialReference> poGeographicCRSHolder;
    3225           2 :     std::unique_ptr<OGRCoordinateTransformation> poTransform;
    3226             : 
    3227           2 :     if (!poCRS->IsGeographic())
    3228             :     {
    3229           1 :         poGeographicCRSHolder = std::make_unique<OGRSpatialReference>();
    3230           1 :         if (poGeographicCRSHolder->CopyGeogCSFrom(poCRS) != OGRERR_NONE)
    3231             :         {
    3232           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3233             :                      "Cannot reproject geometry to geographic CRS");
    3234           0 :             return CE_Failure;
    3235             :         }
    3236           1 :         poGeographicCRSHolder->SetAxisMappingStrategy(
    3237             :             OAMS_TRADITIONAL_GIS_ORDER);
    3238             : 
    3239           1 :         poTransform.reset(OGRCreateCoordinateTransformation(
    3240           1 :             poCRS, poGeographicCRSHolder.get()));
    3241             : 
    3242           1 :         if (!poTransform)
    3243             :         {
    3244           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3245             :                      "Cannot reproject geometry to geographic CRS");
    3246           0 :             return CE_Failure;
    3247             :         }
    3248             : 
    3249           1 :         poGeographicCRS = poGeographicCRSHolder.get();
    3250             :     }
    3251             :     else
    3252             :     {
    3253           1 :         poGeographicCRS = poCRS;
    3254             :     }
    3255             : 
    3256           2 :     geod_geodesic g{};
    3257           2 :     OGRErr eErr = OGRERR_NONE;
    3258           2 :     double dfSemiMajor = poGeographicCRS->GetSemiMajor(&eErr);
    3259           2 :     if (eErr != OGRERR_NONE)
    3260           0 :         return CE_Failure;
    3261           2 :     const double dfInvFlattening = poGeographicCRS->GetInvFlattening(&eErr);
    3262           2 :     if (eErr != OGRERR_NONE)
    3263           0 :         return CE_Failure;
    3264           2 :     geod_init(&g, dfSemiMajor,
    3265             :               dfInvFlattening != 0 ? 1.0 / dfInvFlattening : 0.0);
    3266             : 
    3267           2 :     std::array<double, 5> adfLon{};
    3268           2 :     std::array<double, 5> adfLat{};
    3269             : 
    3270          22 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3271             :     {
    3272         420 :         for (int iCol = 0; iCol < nXSize; ++iCol)
    3273             :         {
    3274         400 :             gt.Apply(static_cast<double>(iCol + nXOff),
    3275         400 :                      static_cast<double>(iLine + nYOff), &adfLon[0],
    3276         400 :                      &adfLat[0]);
    3277         400 :             gt.Apply(static_cast<double>(iCol + nXOff + 1),
    3278         400 :                      static_cast<double>(iLine + nYOff), &adfLon[1],
    3279         400 :                      &adfLat[1]);
    3280         400 :             gt.Apply(static_cast<double>(iCol + nXOff + 1),
    3281         400 :                      static_cast<double>(iLine + nYOff + 1), &adfLon[2],
    3282         400 :                      &adfLat[2]);
    3283         400 :             gt.Apply(static_cast<double>(iCol + nXOff),
    3284         400 :                      static_cast<double>(iLine + nYOff + 1), &adfLon[3],
    3285         400 :                      &adfLat[3]);
    3286         400 :             adfLon[4] = adfLon[0];
    3287         400 :             adfLat[4] = adfLat[0];
    3288             : 
    3289         600 :             if (poTransform &&
    3290         200 :                 !poTransform->Transform(adfLon.size(), adfLon.data(),
    3291         400 :                                         adfLat.data(), nullptr))
    3292             :             {
    3293           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3294             :                          "Failed to reproject cell corners to geographic CRS");
    3295           0 :                 return CE_Failure;
    3296             :             }
    3297             : 
    3298         400 :             double dfArea = -1.0;
    3299         400 :             geod_polygonarea(&g, adfLat.data(), adfLon.data(),
    3300         400 :                              static_cast<int>(adfLat.size()), &dfArea, nullptr);
    3301         400 :             dfArea = std::fabs(dfArea);
    3302             : 
    3303         400 :             GDALCopyWords(&dfArea, GDT_Float64, 0,
    3304             :                           static_cast<GByte *>(pData) +
    3305         400 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    3306         400 :                               iCol * nPixelSpace,
    3307             :                           eBufType, nPixelSpace, 1);
    3308             :         }
    3309             :     }
    3310             : 
    3311           2 :     return CE_None;
    3312             : }  // AreaPixelFunc
    3313             : 
    3314             : static const char pszReclassifyPixelFuncMetadata[] =
    3315             :     "<PixelFunctionArgumentsList>"
    3316             :     "   <Argument name='mapping' "
    3317             :     "             description='Lookup table for mapping, in format "
    3318             :     "from=to,from=to' "
    3319             :     "             type='string'></Argument>"
    3320             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3321             :     "</PixelFunctionArgumentsList>";
    3322             : 
    3323          34 : static CPLErr ReclassifyPixelFunc(void **papoSources, int nSources, void *pData,
    3324             :                                   int nXSize, int nYSize, GDALDataType eSrcType,
    3325             :                                   GDALDataType eBufType, int nPixelSpace,
    3326             :                                   int nLineSpace, CSLConstList papszArgs)
    3327             : {
    3328          34 :     if (GDALDataTypeIsComplex(eSrcType))
    3329             :     {
    3330           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3331             :                  "reclassify cannot by applied to complex data types");
    3332           0 :         return CE_Failure;
    3333             :     }
    3334             : 
    3335          34 :     if (nSources != 1)
    3336             :     {
    3337           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3338             :                  "reclassify only be applied to a single source at a time");
    3339           0 :         return CE_Failure;
    3340             :     }
    3341          34 :     std::optional<double> noDataValue{};
    3342             : 
    3343          34 :     const char *pszNoData = CSLFetchNameValue(papszArgs, "NoData");
    3344          34 :     if (pszNoData != nullptr)
    3345             :     {
    3346          10 :         noDataValue = CPLAtof(pszNoData);
    3347             :     }
    3348             : 
    3349          34 :     const char *pszMappings = CSLFetchNameValue(papszArgs, "mapping");
    3350          34 :     if (pszMappings == nullptr)
    3351             :     {
    3352           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3353             :                  "reclassify must be called with 'mapping' argument");
    3354           0 :         return CE_Failure;
    3355             :     }
    3356             : 
    3357          68 :     gdal::Reclassifier oReclassifier;
    3358          34 :     if (auto eErr = oReclassifier.Init(pszMappings, noDataValue, eBufType);
    3359             :         eErr != CE_None)
    3360             :     {
    3361          14 :         return eErr;
    3362             :     }
    3363             : 
    3364             :     std::unique_ptr<double, VSIFreeReleaser> padfResults(
    3365          40 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(nXSize, sizeof(double))));
    3366          20 :     if (!padfResults)
    3367           0 :         return CE_Failure;
    3368             : 
    3369          20 :     size_t ii = 0;
    3370          20 :     bool bSuccess = false;
    3371         436 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3372             :     {
    3373       20808 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    3374             :         {
    3375       20392 :             double srcVal = GetSrcVal(papoSources[0], eSrcType, ii);
    3376       40784 :             padfResults.get()[iCol] =
    3377       20392 :                 oReclassifier.Reclassify(srcVal, bSuccess);
    3378       20392 :             if (!bSuccess)
    3379             :             {
    3380           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3381             :                          "Encountered value %g with no specified mapping",
    3382             :                          srcVal);
    3383           3 :                 return CE_Failure;
    3384             :             }
    3385             :         }
    3386             : 
    3387         416 :         GDALCopyWords(padfResults.get(), GDT_Float64, sizeof(double),
    3388             :                       static_cast<GByte *>(pData) +
    3389         416 :                           static_cast<GSpacing>(nLineSpace) * iLine,
    3390             :                       eBufType, nPixelSpace, nXSize);
    3391             :     }
    3392             : 
    3393          17 :     return CE_None;
    3394             : }  // ReclassifyPixelFunc
    3395             : 
    3396             : struct MeanKernel
    3397             : {
    3398             :     static constexpr const char *pszName = "mean";
    3399             : 
    3400             :     double dfMean = 0;
    3401             :     int nValidSources = 0;
    3402             : 
    3403         568 :     void Reset()
    3404             :     {
    3405         568 :         dfMean = 0;
    3406         568 :         nValidSources = 0;
    3407         568 :     }
    3408             : 
    3409          94 :     static CPLErr ProcessArguments(CSLConstList)
    3410             :     {
    3411          94 :         return CE_None;
    3412             :     }
    3413             : 
    3414        1375 :     void ProcessPixel(double dfVal)
    3415             :     {
    3416        1375 :         ++nValidSources;
    3417             : 
    3418        1375 :         if (CPL_UNLIKELY(std::isinf(dfVal)))
    3419             :         {
    3420         620 :             if (nValidSources == 1)
    3421             :             {
    3422         310 :                 dfMean = dfVal;
    3423             :             }
    3424         310 :             else if (dfVal == -dfMean)
    3425             :             {
    3426          62 :                 dfMean = std::numeric_limits<double>::quiet_NaN();
    3427             :             }
    3428             :         }
    3429         755 :         else if (CPL_UNLIKELY(std::isinf(dfMean)))
    3430             :         {
    3431         186 :             if (!std::isfinite(dfVal))
    3432             :             {
    3433          62 :                 dfMean = std::numeric_limits<double>::quiet_NaN();
    3434             :             }
    3435             :         }
    3436             :         else
    3437             :         {
    3438         569 :             const double delta = dfVal - dfMean;
    3439         569 :             if (CPL_UNLIKELY(std::isinf(delta)))
    3440           0 :                 dfMean += dfVal / nValidSources - dfMean / nValidSources;
    3441             :             else
    3442         569 :                 dfMean += delta / nValidSources;
    3443             :         }
    3444        1375 :     }
    3445             : 
    3446         566 :     bool HasValue() const
    3447             :     {
    3448         566 :         return nValidSources > 0;
    3449             :     }
    3450             : 
    3451         563 :     double GetValue() const
    3452             :     {
    3453         563 :         return dfMean;
    3454             :     }
    3455             : };
    3456             : 
    3457             : struct GeoMeanKernel
    3458             : {
    3459             :     static constexpr const char *pszName = "geometric_mean";
    3460             : 
    3461             :     double dfProduct = 1;
    3462             :     int nValidSources = 0;
    3463             : 
    3464           6 :     void Reset()
    3465             :     {
    3466           6 :         dfProduct = 1;
    3467           6 :         nValidSources = 0;
    3468           6 :     }
    3469             : 
    3470           3 :     static CPLErr ProcessArguments(CSLConstList)
    3471             :     {
    3472           3 :         return CE_None;
    3473             :     }
    3474             : 
    3475           3 :     void ProcessPixel(double dfVal)
    3476             :     {
    3477           3 :         dfProduct *= dfVal;
    3478           3 :         nValidSources++;
    3479           3 :     }
    3480             : 
    3481           4 :     bool HasValue() const
    3482             :     {
    3483           4 :         return nValidSources > 0;
    3484             :     }
    3485             : 
    3486           1 :     double GetValue() const
    3487             :     {
    3488           1 :         return std::pow(dfProduct, 1.0 / nValidSources);
    3489             :     }
    3490             : };
    3491             : 
    3492             : struct HarmonicMeanKernel
    3493             : {
    3494             :     static constexpr const char *pszName = "harmonic_mean";
    3495             : 
    3496             :     double dfDenom = 0;
    3497             :     int nValidSources = 0;
    3498             :     bool bValueIsZero = false;
    3499             :     bool bPropagateZero = false;
    3500             : 
    3501          10 :     void Reset()
    3502             :     {
    3503          10 :         dfDenom = 0;
    3504          10 :         nValidSources = 0;
    3505          10 :         bValueIsZero = false;
    3506          10 :     }
    3507             : 
    3508           7 :     void ProcessPixel(double dfVal)
    3509             :     {
    3510           7 :         if (dfVal == 0)
    3511             :         {
    3512           2 :             bValueIsZero = true;
    3513             :         }
    3514             :         else
    3515             :         {
    3516           5 :             dfDenom += 1 / dfVal;
    3517             :         }
    3518           7 :         nValidSources++;
    3519           7 :     }
    3520             : 
    3521           5 :     CPLErr ProcessArguments(CSLConstList papszArgs)
    3522             :     {
    3523           5 :         bPropagateZero =
    3524           5 :             CPLTestBool(CSLFetchNameValueDef(papszArgs, "propagateZero", "0"));
    3525           5 :         return CE_None;
    3526             :     }
    3527             : 
    3528           8 :     bool HasValue() const
    3529             :     {
    3530           8 :         return dfDenom > 0 && (bPropagateZero || !bValueIsZero);
    3531             :     }
    3532             : 
    3533           2 :     double GetValue() const
    3534             :     {
    3535           2 :         if (bPropagateZero && bValueIsZero)
    3536             :         {
    3537           1 :             return 0;
    3538             :         }
    3539           1 :         return static_cast<double>(nValidSources) / dfDenom;
    3540             :     }
    3541             : };
    3542             : 
    3543             : struct MedianKernel
    3544             : {
    3545             :     static constexpr const char *pszName = "median";
    3546             : 
    3547             :     mutable std::vector<double> values{};
    3548             : 
    3549           9 :     void Reset()
    3550             :     {
    3551           9 :         values.clear();
    3552           9 :     }
    3553             : 
    3554           5 :     static CPLErr ProcessArguments(CSLConstList)
    3555             :     {
    3556           5 :         return CE_None;
    3557             :     }
    3558             : 
    3559           9 :     void ProcessPixel(double dfVal)
    3560             :     {
    3561           9 :         if (!std::isnan(dfVal))
    3562             :         {
    3563           9 :             values.push_back(dfVal);
    3564             :         }
    3565           9 :     }
    3566             : 
    3567           7 :     bool HasValue() const
    3568             :     {
    3569           7 :         return !values.empty();
    3570             :     }
    3571             : 
    3572           3 :     double GetValue() const
    3573             :     {
    3574           3 :         std::sort(values.begin(), values.end());
    3575           3 :         if (values.size() % 2 == 0)
    3576             :         {
    3577             :             return 0.5 *
    3578           1 :                    (values[values.size() / 2 - 1] + values[values.size() / 2]);
    3579             :         }
    3580             : 
    3581           2 :         return values[values.size() / 2];
    3582             :     }
    3583             : };
    3584             : 
    3585             : struct QuantileKernel
    3586             : {
    3587             :     static constexpr const char *pszName = "quantile";
    3588             : 
    3589             :     mutable std::vector<double> values{};
    3590             :     double q = 0.5;
    3591             : 
    3592          14 :     void Reset()
    3593             :     {
    3594          14 :         values.clear();
    3595             :         // q intentionally preserved (set via ProcessArguments)
    3596          14 :     }
    3597             : 
    3598          15 :     CPLErr ProcessArguments(CSLConstList papszArgs)
    3599             :     {
    3600          15 :         const char *pszQ = CSLFetchNameValue(papszArgs, "q");
    3601          15 :         if (pszQ)
    3602             :         {
    3603             :             char *end;
    3604          14 :             const double dq = CPLStrtod(pszQ, &end);
    3605          14 :             while (isspace(*end))
    3606             :             {
    3607           0 :                 end++;
    3608             :             }
    3609          14 :             if (*end != '\0' || dq < 0.0 || dq > 1.0 || std::isnan(dq))
    3610             :             {
    3611           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3612             :                          "quantile: q must be between 0 and 1");
    3613           4 :                 return CE_Failure;
    3614             :             }
    3615          10 :             q = dq;
    3616             :         }
    3617             :         else
    3618             :         {
    3619           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    3620             :                      "quantile: q must be specified");
    3621           1 :             return CE_Failure;
    3622             :         }
    3623          10 :         return CE_None;
    3624             :     }
    3625             : 
    3626          15 :     void ProcessPixel(double dfVal)
    3627             :     {
    3628          15 :         if (!std::isnan(dfVal))
    3629             :         {
    3630          15 :             values.push_back(dfVal);
    3631             :         }
    3632          15 :     }
    3633             : 
    3634          12 :     bool HasValue() const
    3635             :     {
    3636          12 :         return !values.empty();
    3637             :     }
    3638             : 
    3639           7 :     double GetValue() const
    3640             :     {
    3641           7 :         if (values.empty())
    3642             :         {
    3643           0 :             return std::numeric_limits<double>::quiet_NaN();
    3644             :         }
    3645             : 
    3646           7 :         std::sort(values.begin(), values.end());
    3647           7 :         const double loc = q * static_cast<double>(values.size() - 1);
    3648             : 
    3649             :         // Use formula from NumPy docs with default linear interpolation
    3650             :         // g: fractional component of loc
    3651             :         // j: integral component of loc
    3652             :         double j;
    3653           7 :         const double g = std::modf(loc, &j);
    3654             : 
    3655           7 :         if (static_cast<size_t>(j) + 1 == values.size())
    3656             :         {
    3657           3 :             return values[static_cast<size_t>(j)];
    3658             :         }
    3659             : 
    3660           4 :         return (1 - g) * values[static_cast<size_t>(j)] +
    3661           4 :                g * values[static_cast<size_t>(j) + 1];
    3662             :     }
    3663             : };
    3664             : 
    3665             : struct ModeKernel
    3666             : {
    3667             :     static constexpr const char *pszName = "mode";
    3668             : 
    3669             :     std::map<double, size_t> counts{};
    3670             :     std::size_t nanCount{0};
    3671             :     double dfMax = std::numeric_limits<double>::quiet_NaN();
    3672             :     decltype(counts.begin()) oMax = counts.end();
    3673             : 
    3674           7 :     void Reset()
    3675             :     {
    3676           7 :         nanCount = 0;
    3677           7 :         counts.clear();
    3678           7 :         oMax = counts.end();
    3679           7 :     }
    3680             : 
    3681           4 :     static CPLErr ProcessArguments(CSLConstList)
    3682             :     {
    3683           4 :         return CE_None;
    3684             :     }
    3685             : 
    3686          11 :     void ProcessPixel(double dfVal)
    3687             :     {
    3688          11 :         if (std::isnan(dfVal))
    3689             :         {
    3690           2 :             nanCount += 1;
    3691           2 :             return;
    3692             :         }
    3693             : 
    3694             :         // if dfVal is NaN, try_emplace will return an entry for a different key!
    3695           9 :         auto [it, inserted] = counts.try_emplace(dfVal, 0);
    3696             : 
    3697           9 :         it->second += 1;
    3698             : 
    3699           9 :         if (oMax == counts.end() || it->second > oMax->second)
    3700             :         {
    3701           5 :             oMax = it;
    3702             :         }
    3703             :     }
    3704             : 
    3705           5 :     bool HasValue() const
    3706             :     {
    3707           5 :         return nanCount > 0 || oMax != counts.end();
    3708             :     }
    3709             : 
    3710           3 :     double GetValue() const
    3711             :     {
    3712           3 :         double ret = std::numeric_limits<double>::quiet_NaN();
    3713           3 :         if (oMax != counts.end())
    3714             :         {
    3715           3 :             const size_t nCount = oMax->second;
    3716           3 :             if (nCount > nanCount)
    3717           2 :                 ret = oMax->first;
    3718             :         }
    3719           3 :         return ret;
    3720             :     }
    3721             : };
    3722             : 
    3723             : static const char pszBasicPixelFuncMetadata[] =
    3724             :     "<PixelFunctionArgumentsList>"
    3725             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3726             :     "   <Argument name='propagateNoData' description='Whether the output value "
    3727             :     "should be NoData as as soon as one source is NoData' type='boolean' "
    3728             :     "default='false' />"
    3729             :     "</PixelFunctionArgumentsList>";
    3730             : 
    3731             : static const char pszQuantilePixelFuncMetadata[] =
    3732             :     "<PixelFunctionArgumentsList>"
    3733             :     "   <Argument name='q' type='float' description='Quantile in [0,1]' />"
    3734             :     "   <Argument type='builtin' value='NoData' optional='true' />"
    3735             :     "   <Argument name='propagateNoData' type='boolean' default='false' />"
    3736             :     "</PixelFunctionArgumentsList>";
    3737             : 
    3738             : #if defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    3739         636 : inline __m128i packus_epi32(__m128i low, __m128i high)
    3740             : {
    3741             : #if __SSE4_1__
    3742             :     return _mm_packus_epi32(low, high);  // Pack uint32 to uint16
    3743             : #else
    3744        1272 :     low = _mm_add_epi32(low, _mm_set1_epi32(-32768));
    3745        1272 :     high = _mm_add_epi32(high, _mm_set1_epi32(-32768));
    3746        1908 :     return _mm_sub_epi16(_mm_packs_epi32(low, high), _mm_set1_epi16(-32768));
    3747             : #endif
    3748             : }
    3749             : #endif
    3750             : 
    3751             : #ifdef USE_SSE2
    3752             : 
    3753             : template <class T, class SSEWrapper>
    3754          46 : static void OptimizedMeanFloatSSE2(const void *const *papoSources, int nSources,
    3755             :                                    void *pData, int nXSize, int nYSize,
    3756             :                                    int nLineSpace)
    3757             : {
    3758          46 :     assert(nSources >= 1);
    3759          46 :     constexpr int VALUES_PER_REG =
    3760             :         static_cast<int>(sizeof(typename SSEWrapper::Vec) / sizeof(T));
    3761          46 :     const T invSources = static_cast<T>(1.0) / static_cast<T>(nSources);
    3762          46 :     const auto invSourcesSSE = SSEWrapper::Set1(invSources);
    3763          46 :     const auto signMaskSSE = SSEWrapper::Set1(static_cast<T>(-0.0));
    3764          46 :     const auto infSSE = SSEWrapper::Set1(std::numeric_limits<T>::infinity());
    3765         307 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    3766             :     {
    3767         261 :         T *CPL_RESTRICT pDest =
    3768             :             reinterpret_cast<T *>(static_cast<GByte *>(pData) +
    3769         261 :                                   static_cast<GSpacing>(nLineSpace) * iLine);
    3770         261 :         const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    3771         261 :         int iCol = 0;
    3772        2571 :         for (; iCol < nXSize - (2 * VALUES_PER_REG - 1);
    3773             :              iCol += 2 * VALUES_PER_REG)
    3774             :         {
    3775        2325 :             auto reg0 = SSEWrapper::LoadU(
    3776        2325 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    3777        2325 :                 iOffsetLine + iCol);
    3778        2325 :             auto reg1 = SSEWrapper::LoadU(
    3779        2325 :                 static_cast<const T * CPL_RESTRICT>(papoSources[0]) +
    3780        2325 :                 iOffsetLine + iCol + VALUES_PER_REG);
    3781        6700 :             for (int iSrc = 1; iSrc < nSources; ++iSrc)
    3782             :             {
    3783        4375 :                 const auto inputVal0 = SSEWrapper::LoadU(
    3784        4375 :                     static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    3785        4375 :                     iOffsetLine + iCol);
    3786        4375 :                 const auto inputVal1 = SSEWrapper::LoadU(
    3787        4375 :                     static_cast<const T * CPL_RESTRICT>(papoSources[iSrc]) +
    3788        4375 :                     iOffsetLine + iCol + VALUES_PER_REG);
    3789        4375 :                 reg0 = SSEWrapper::Add(reg0, inputVal0);
    3790        4375 :                 reg1 = SSEWrapper::Add(reg1, inputVal1);
    3791             :             }
    3792        2325 :             reg0 = SSEWrapper::Mul(reg0, invSourcesSSE);
    3793        2325 :             reg1 = SSEWrapper::Mul(reg1, invSourcesSSE);
    3794             : 
    3795             :             // Detect infinity that could happen when summing huge
    3796             :             // values
    3797        2325 :             if (SSEWrapper::MoveMask(SSEWrapper::Or(
    3798             :                     SSEWrapper::CmpEq(SSEWrapper::AndNot(signMaskSSE, reg0),
    3799             :                                       infSSE),
    3800             :                     SSEWrapper::CmpEq(SSEWrapper::AndNot(signMaskSSE, reg1),
    3801             :                                       infSSE))))
    3802             :             {
    3803          15 :                 break;
    3804             :             }
    3805             : 
    3806        2310 :             SSEWrapper::StoreU(pDest + iCol, reg0);
    3807        2310 :             SSEWrapper::StoreU(pDest + iCol + VALUES_PER_REG, reg1);
    3808             :         }
    3809             : 
    3810             :         // Use numerically stable mean computation
    3811        1240 :         for (; iCol < nXSize; ++iCol)
    3812             :         {
    3813         979 :             T mean = static_cast<const T * CPL_RESTRICT>(
    3814         979 :                 papoSources[0])[iOffsetLine + iCol];
    3815         979 :             if (nSources >= 2)
    3816             :             {
    3817         969 :                 T new_val = static_cast<const T * CPL_RESTRICT>(
    3818         969 :                     papoSources[1])[iOffsetLine + iCol];
    3819         969 :                 if (CPL_UNLIKELY(std::isinf(new_val)))
    3820             :                 {
    3821         268 :                     if (new_val == -mean)
    3822             :                     {
    3823          10 :                         pDest[iCol] = std::numeric_limits<T>::quiet_NaN();
    3824          10 :                         continue;
    3825             :                     }
    3826             :                 }
    3827         701 :                 else if (CPL_UNLIKELY(std::isinf(mean)))
    3828             :                 {
    3829         144 :                     if (!std::isfinite(new_val))
    3830             :                     {
    3831          10 :                         pDest[iCol] = std::numeric_limits<T>::quiet_NaN();
    3832          10 :                         continue;
    3833             :                     }
    3834             :                 }
    3835             :                 else
    3836             :                 {
    3837         557 :                     const T delta = new_val - mean;
    3838         557 :                     if (CPL_UNLIKELY(std::isinf(delta)))
    3839          10 :                         mean += new_val * static_cast<T>(0.5) -
    3840          10 :                                 mean * static_cast<T>(0.5);
    3841             :                     else
    3842         547 :                         mean += delta * static_cast<T>(0.5);
    3843             :                 }
    3844             : 
    3845        1681 :                 for (int iSrc = 2; iSrc < nSources; ++iSrc)
    3846             :                 {
    3847         752 :                     new_val = static_cast<const T * CPL_RESTRICT>(
    3848         752 :                         papoSources[iSrc])[iOffsetLine + iCol];
    3849         752 :                     if (CPL_UNLIKELY(std::isinf(new_val)))
    3850             :                     {
    3851         196 :                         if (new_val == -mean)
    3852             :                         {
    3853          10 :                             mean = std::numeric_limits<T>::quiet_NaN();
    3854          10 :                             break;
    3855             :                         }
    3856             :                     }
    3857         556 :                     else if (CPL_UNLIKELY(std::isinf(mean)))
    3858             :                     {
    3859          72 :                         if (!std::isfinite(new_val))
    3860             :                         {
    3861          10 :                             mean = std::numeric_limits<T>::quiet_NaN();
    3862          10 :                             break;
    3863             :                         }
    3864             :                     }
    3865             :                     else
    3866             :                     {
    3867         484 :                         const T delta = new_val - mean;
    3868         484 :                         if (CPL_UNLIKELY(std::isinf(delta)))
    3869          62 :                             mean += new_val / static_cast<T>(iSrc + 1) -
    3870          62 :                                     mean / static_cast<T>(iSrc + 1);
    3871             :                         else
    3872         422 :                             mean += delta / static_cast<T>(iSrc + 1);
    3873             :                     }
    3874             :                 }
    3875             :             }
    3876         959 :             pDest[iCol] = mean;
    3877             :         }
    3878             :     }
    3879          46 : }
    3880             : 
    3881             : // clang-format off
    3882             : namespace
    3883             : {
    3884             : #ifdef __AVX2__
    3885             : struct SSEWrapperFloat
    3886             : {
    3887             :     typedef __m256 Vec;
    3888             : 
    3889             :     static inline Vec Set1(float x) { return _mm256_set1_ps(x); }
    3890             :     static inline Vec LoadU(const float *x) { return _mm256_loadu_ps(x); }
    3891             :     static inline void StoreU(float *x, Vec y) { _mm256_storeu_ps(x, y); }
    3892             :     static inline Vec Add(Vec x, Vec y) { return _mm256_add_ps(x, y); }
    3893             :     static inline Vec Mul(Vec x, Vec y) { return _mm256_mul_ps(x, y); }
    3894             :     static inline Vec Or(Vec x, Vec y) { return _mm256_or_ps(x, y); }
    3895             :     static inline Vec AndNot(Vec x, Vec y) { return _mm256_andnot_ps(x, y); }
    3896             :     static inline Vec CmpEq(Vec x, Vec y) { return _mm256_cmp_ps(x, y, _CMP_EQ_OQ); }
    3897             :     static inline int MoveMask(Vec x) { return _mm256_movemask_ps(x); }
    3898             : };
    3899             : 
    3900             : struct SSEWrapperDouble
    3901             : {
    3902             :     typedef __m256d Vec;
    3903             : 
    3904             :     static inline Vec Set1(double x) { return _mm256_set1_pd(x); }
    3905             :     static inline Vec LoadU(const double *x) { return _mm256_loadu_pd(x); }
    3906             :     static inline void StoreU(double *x, Vec y) { _mm256_storeu_pd(x, y); }
    3907             :     static inline Vec Add(Vec x, Vec y) { return _mm256_add_pd(x, y); }
    3908             :     static inline Vec Mul(Vec x, Vec y) { return _mm256_mul_pd(x, y); }
    3909             :     static inline Vec Or(Vec x, Vec y) { return _mm256_or_pd(x, y); }
    3910             :     static inline Vec AndNot(Vec x, Vec y) { return _mm256_andnot_pd(x, y); }
    3911             :     static inline Vec CmpEq(Vec x, Vec y) { return _mm256_cmp_pd(x, y, _CMP_EQ_OQ); }
    3912             :     static inline int MoveMask(Vec x) { return _mm256_movemask_pd(x); }
    3913             : };
    3914             : 
    3915             : #else
    3916             : 
    3917             : struct SSEWrapperFloat
    3918             : {
    3919             :     typedef __m128 Vec;
    3920             : 
    3921         114 :     static inline Vec Set1(float x) { return _mm_set1_ps(x); }
    3922        3984 :     static inline Vec LoadU(const float *x) { return _mm_loadu_ps(x); }
    3923         666 :     static inline void StoreU(float *x, Vec y) { _mm_storeu_ps(x, y); }
    3924        2624 :     static inline Vec Add(Vec x, Vec y) { return _mm_add_ps(x, y); }
    3925        1360 :     static inline Vec Mul(Vec x, Vec y) { return _mm_mul_ps(x, y); }
    3926         680 :     static inline Vec Or(Vec x, Vec y) { return _mm_or_ps(x, y); }
    3927        1360 :     static inline Vec AndNot(Vec x, Vec y) { return _mm_andnot_ps(x, y); }
    3928        1360 :     static inline Vec CmpEq(Vec x, Vec y) { return _mm_cmpeq_ps(x, y); }
    3929         680 :     static inline int MoveMask(Vec x) { return _mm_movemask_ps(x); }
    3930             : };
    3931             : 
    3932             : struct SSEWrapperDouble
    3933             : {
    3934             :     typedef __m128d Vec;
    3935             : 
    3936         162 :     static inline Vec Set1(double x) { return _mm_set1_pd(x); }
    3937       22816 :     static inline Vec LoadU(const double *x) { return _mm_loadu_pd(x); }
    3938        3954 :     static inline void StoreU(double *x, Vec y) { _mm_storeu_pd(x, y); }
    3939       14876 :     static inline Vec Add(Vec x, Vec y) { return _mm_add_pd(x, y); }
    3940        7940 :     static inline Vec Mul(Vec x, Vec y) { return _mm_mul_pd(x, y); }
    3941        3970 :     static inline Vec Or(Vec x, Vec y) { return _mm_or_pd(x, y); }
    3942        7940 :     static inline Vec AndNot(Vec x, Vec y) { return _mm_andnot_pd(x, y); }
    3943        7940 :     static inline Vec CmpEq(Vec x, Vec y) { return _mm_cmpeq_pd(x, y); }
    3944        3970 :     static inline int MoveMask(Vec x) { return _mm_movemask_pd(x); }
    3945             : };
    3946             : #endif
    3947             : }  // namespace
    3948             : 
    3949             : // clang-format on
    3950             : 
    3951             : #endif  // USE_SSE2
    3952             : 
    3953             : template <typename Kernel>
    3954         127 : static CPLErr BasicPixelFunc(void **papoSources, int nSources, void *pData,
    3955             :                              int nXSize, int nYSize, GDALDataType eSrcType,
    3956             :                              GDALDataType eBufType, int nPixelSpace,
    3957             :                              int nLineSpace, CSLConstList papszArgs)
    3958             : {
    3959             :     /* ---- Init ---- */
    3960         152 :     Kernel oKernel;
    3961             : 
    3962         127 :     if (GDALDataTypeIsComplex(eSrcType))
    3963             :     {
    3964           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3965             :                  "Complex data types not supported by %s", oKernel.pszName);
    3966           1 :         return CE_Failure;
    3967             :     }
    3968             : 
    3969         126 :     double dfNoData{0};
    3970         126 :     const bool bHasNoData = CSLFindName(papszArgs, "NoData") != -1;
    3971         126 :     if (bHasNoData && FetchDoubleArg(papszArgs, "NoData", &dfNoData) != CE_None)
    3972           0 :         return CE_Failure;
    3973             : 
    3974         126 :     const bool bPropagateNoData = CPLTestBool(
    3975             :         CSLFetchNameValueDef(papszArgs, "propagateNoData", "false"));
    3976             : 
    3977         126 :     if (oKernel.ProcessArguments(papszArgs) == CE_Failure)
    3978             :     {
    3979           5 :         return CE_Failure;
    3980             :     }
    3981             : 
    3982             : #if defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    3983             :     if constexpr (std::is_same_v<Kernel, MeanKernel>)
    3984             :     {
    3985          91 :         if (!bHasNoData && eSrcType == GDT_UInt8 && eBufType == GDT_UInt8 &&
    3986         185 :             nPixelSpace == 1 &&
    3987             :             // We use signed int16 to accumulate
    3988          11 :             nSources <= std::numeric_limits<int16_t>::max() /
    3989          11 :                             std::numeric_limits<uint8_t>::max())
    3990             :         {
    3991             :             using T = uint8_t;
    3992          10 :             constexpr int VALUES_PER_REG = 16;
    3993          10 :             if (nSources == 2)
    3994             :             {
    3995         207 :                 for (int iLine = 0; iLine < nYSize; ++iLine)
    3996             :                 {
    3997         203 :                     T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    3998             :                         static_cast<GByte *>(pData) +
    3999         203 :                         static_cast<GSpacing>(nLineSpace) * iLine);
    4000         203 :                     const size_t iOffsetLine =
    4001         203 :                         static_cast<size_t>(iLine) * nXSize;
    4002         203 :                     int iCol = 0;
    4003        5209 :                     for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4004             :                          iCol += VALUES_PER_REG)
    4005             :                     {
    4006             :                         const __m128i inputVal0 =
    4007       10012 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4008             :                                 static_cast<const T * CPL_RESTRICT>(
    4009             :                                     papoSources[0]) +
    4010        5006 :                                 iOffsetLine + iCol));
    4011             :                         const __m128i inputVal1 =
    4012        5006 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4013        5006 :                                 static_cast<const T * CPL_RESTRICT>(
    4014             :                                     papoSources[1]) +
    4015        5006 :                                 iOffsetLine + iCol));
    4016        5006 :                         _mm_storeu_si128(
    4017        5006 :                             reinterpret_cast<__m128i *>(pDest + iCol),
    4018             :                             _mm_avg_epu8(inputVal0, inputVal1));
    4019             :                     }
    4020         235 :                     for (; iCol < nXSize; ++iCol)
    4021             :                     {
    4022          32 :                         uint32_t acc = 1 +
    4023          32 :                                        static_cast<const T * CPL_RESTRICT>(
    4024          32 :                                            papoSources[0])[iOffsetLine + iCol] +
    4025          32 :                                        static_cast<const T * CPL_RESTRICT>(
    4026          32 :                                            papoSources[1])[iOffsetLine + iCol];
    4027          32 :                         pDest[iCol] = static_cast<T>(acc / 2);
    4028             :                     }
    4029             :                 }
    4030             :             }
    4031             :             else
    4032             :             {
    4033           6 :                 libdivide::divider<uint16_t> fast_d(
    4034             :                     static_cast<uint16_t>(nSources));
    4035             :                 const auto halfConstant =
    4036           6 :                     _mm_set1_epi16(static_cast<int16_t>(nSources / 2));
    4037         211 :                 for (int iLine = 0; iLine < nYSize; ++iLine)
    4038             :                 {
    4039         205 :                     T *CPL_RESTRICT pDest =
    4040             :                         static_cast<GByte *>(pData) +
    4041         205 :                         static_cast<GSpacing>(nLineSpace) * iLine;
    4042         205 :                     const size_t iOffsetLine =
    4043         205 :                         static_cast<size_t>(iLine) * nXSize;
    4044         205 :                     int iCol = 0;
    4045        5220 :                     for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4046             :                          iCol += VALUES_PER_REG)
    4047             :                     {
    4048        5015 :                         __m128i reg0 = halfConstant;
    4049        5015 :                         __m128i reg1 = halfConstant;
    4050       20435 :                         for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4051             :                         {
    4052       15420 :                             const __m128i inputVal = _mm_loadu_si128(
    4053             :                                 reinterpret_cast<const __m128i *>(
    4054       15420 :                                     static_cast<const T * CPL_RESTRICT>(
    4055       15420 :                                         papoSources[iSrc]) +
    4056       15420 :                                     iOffsetLine + iCol));
    4057       46260 :                             reg0 = _mm_add_epi16(
    4058             :                                 reg0, _mm_unpacklo_epi8(inputVal,
    4059             :                                                         _mm_setzero_si128()));
    4060       46260 :                             reg1 = _mm_add_epi16(
    4061             :                                 reg1, _mm_unpackhi_epi8(inputVal,
    4062             :                                                         _mm_setzero_si128()));
    4063             :                         }
    4064             :                         reg0 /= fast_d;
    4065             :                         reg1 /= fast_d;
    4066        5015 :                         _mm_storeu_si128(
    4067        5015 :                             reinterpret_cast<__m128i *>(pDest + iCol),
    4068             :                             _mm_packus_epi16(reg0, reg1));
    4069             :                     }
    4070         280 :                     for (; iCol < nXSize; ++iCol)
    4071             :                     {
    4072          75 :                         uint32_t acc = nSources / 2;
    4073        2175 :                         for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4074             :                         {
    4075        2100 :                             acc += static_cast<const T * CPL_RESTRICT>(
    4076        2100 :                                 papoSources[iSrc])[iOffsetLine + iCol];
    4077             :                         }
    4078          75 :                         pDest[iCol] = static_cast<T>(acc / nSources);
    4079             :                     }
    4080             :                 }
    4081             :             }
    4082          10 :             return CE_None;
    4083             :         }
    4084             : 
    4085          81 :         if (!bHasNoData && eSrcType == GDT_UInt8 && eBufType == GDT_UInt8 &&
    4086         165 :             nPixelSpace == 1 &&
    4087             :             // We use signed int32 to accumulate
    4088           1 :             nSources <= std::numeric_limits<int32_t>::max() /
    4089           1 :                             std::numeric_limits<uint8_t>::max())
    4090             :         {
    4091             :             using T = uint8_t;
    4092           1 :             constexpr int VALUES_PER_REG = 16;
    4093           1 :             libdivide::divider<uint32_t> fast_d(nSources);
    4094           1 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4095           2 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4096             :             {
    4097           1 :                 T *CPL_RESTRICT pDest =
    4098             :                     static_cast<GByte *>(pData) +
    4099           1 :                     static_cast<GSpacing>(nLineSpace) * iLine;
    4100           1 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4101           1 :                 int iCol = 0;
    4102           4 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4103             :                      iCol += VALUES_PER_REG)
    4104             :                 {
    4105           3 :                     __m128i reg0 = halfConstant;
    4106           3 :                     __m128i reg1 = halfConstant;
    4107           3 :                     __m128i reg2 = halfConstant;
    4108           3 :                     __m128i reg3 = halfConstant;
    4109       98307 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4110             :                     {
    4111             :                         const __m128i inputVal =
    4112       98304 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4113       98304 :                                 static_cast<const T * CPL_RESTRICT>(
    4114       98304 :                                     papoSources[iSrc]) +
    4115       98304 :                                 iOffsetLine + iCol));
    4116             :                         const __m128i low =
    4117      196608 :                             _mm_unpacklo_epi8(inputVal, _mm_setzero_si128());
    4118             :                         const __m128i high =
    4119      196608 :                             _mm_unpackhi_epi8(inputVal, _mm_setzero_si128());
    4120      294912 :                         reg0 = _mm_add_epi32(
    4121             :                             reg0, _mm_unpacklo_epi16(low, _mm_setzero_si128()));
    4122      294912 :                         reg1 = _mm_add_epi32(
    4123             :                             reg1, _mm_unpackhi_epi16(low, _mm_setzero_si128()));
    4124      294912 :                         reg2 = _mm_add_epi32(
    4125             :                             reg2,
    4126             :                             _mm_unpacklo_epi16(high, _mm_setzero_si128()));
    4127      294912 :                         reg3 = _mm_add_epi32(
    4128             :                             reg3,
    4129             :                             _mm_unpackhi_epi16(high, _mm_setzero_si128()));
    4130             :                     }
    4131             :                     reg0 /= fast_d;
    4132             :                     reg1 /= fast_d;
    4133             :                     reg2 /= fast_d;
    4134             :                     reg3 /= fast_d;
    4135           3 :                     _mm_storeu_si128(
    4136           3 :                         reinterpret_cast<__m128i *>(pDest + iCol),
    4137             :                         _mm_packus_epi16(packus_epi32(reg0, reg1),
    4138             :                                          packus_epi32(reg2, reg3)));
    4139             :                 }
    4140          16 :                 for (; iCol < nXSize; ++iCol)
    4141             :                 {
    4142          15 :                     uint32_t acc = nSources / 2;
    4143      491535 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4144             :                     {
    4145      491520 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4146      491520 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4147             :                     }
    4148          15 :                     pDest[iCol] = static_cast<T>(acc / nSources);
    4149             :                 }
    4150             :             }
    4151           1 :             return CE_None;
    4152             :         }
    4153             : 
    4154          80 :         if (!bHasNoData && eSrcType == GDT_UInt16 && eBufType == GDT_UInt16 &&
    4155         163 :             nPixelSpace == 2 &&
    4156           5 :             nSources <= std::numeric_limits<int32_t>::max() /
    4157           5 :                             std::numeric_limits<uint16_t>::max())
    4158             :         {
    4159           5 :             libdivide::divider<uint32_t> fast_d(nSources);
    4160             :             using T = uint16_t;
    4161           5 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4162           5 :             constexpr int VALUES_PER_REG = 8;
    4163          59 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4164             :             {
    4165          54 :                 T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    4166             :                     static_cast<GByte *>(pData) +
    4167          54 :                     static_cast<GSpacing>(nLineSpace) * iLine);
    4168          54 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4169          54 :                 int iCol = 0;
    4170         366 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4171             :                      iCol += VALUES_PER_REG)
    4172             :                 {
    4173         312 :                     __m128i reg0 = halfConstant;
    4174         312 :                     __m128i reg1 = halfConstant;
    4175       99534 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4176             :                     {
    4177             :                         const __m128i inputVal =
    4178       99222 :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4179       99222 :                                 static_cast<const T * CPL_RESTRICT>(
    4180       99222 :                                     papoSources[iSrc]) +
    4181       99222 :                                 iOffsetLine + iCol));
    4182      297666 :                         reg0 = _mm_add_epi32(
    4183             :                             reg0,
    4184             :                             _mm_unpacklo_epi16(inputVal, _mm_setzero_si128()));
    4185      297666 :                         reg1 = _mm_add_epi32(
    4186             :                             reg1,
    4187             :                             _mm_unpackhi_epi16(inputVal, _mm_setzero_si128()));
    4188             :                     }
    4189             :                     reg0 /= fast_d;
    4190             :                     reg1 /= fast_d;
    4191         312 :                     _mm_storeu_si128(reinterpret_cast<__m128i *>(pDest + iCol),
    4192             :                                      packus_epi32(reg0, reg1));
    4193             :                 }
    4194         182 :                 for (; iCol < nXSize; ++iCol)
    4195             :                 {
    4196         128 :                     uint32_t acc = nSources / 2;
    4197      229846 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4198             :                     {
    4199      229718 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4200      229718 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4201             :                     }
    4202         128 :                     pDest[iCol] = static_cast<T>(acc / nSources);
    4203             :                 }
    4204             :             }
    4205           5 :             return CE_None;
    4206             :         }
    4207             : 
    4208          75 :         if (!bHasNoData && eSrcType == GDT_Int16 && eBufType == GDT_Int16 &&
    4209         153 :             nPixelSpace == 2 &&
    4210           7 :             nSources <= std::numeric_limits<int32_t>::max() /
    4211           7 :                             std::numeric_limits<uint16_t>::max())
    4212             :         {
    4213           7 :             libdivide::divider<uint32_t> fast_d(nSources);
    4214             :             using T = int16_t;
    4215           7 :             const auto halfConstant = _mm_set1_epi32(nSources / 2);
    4216           7 :             const auto shift = _mm_set1_epi16(std::numeric_limits<T>::min());
    4217           7 :             constexpr int VALUES_PER_REG = 8;
    4218          63 :             for (int iLine = 0; iLine < nYSize; ++iLine)
    4219             :             {
    4220          56 :                 T *CPL_RESTRICT pDest = reinterpret_cast<T *>(
    4221             :                     static_cast<GByte *>(pData) +
    4222          56 :                     static_cast<GSpacing>(nLineSpace) * iLine);
    4223          56 :                 const size_t iOffsetLine = static_cast<size_t>(iLine) * nXSize;
    4224          56 :                 int iCol = 0;
    4225         374 :                 for (; iCol < nXSize - (VALUES_PER_REG - 1);
    4226             :                      iCol += VALUES_PER_REG)
    4227             :                 {
    4228         318 :                     __m128i reg0 = halfConstant;
    4229         318 :                     __m128i reg1 = halfConstant;
    4230       99555 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4231             :                     {
    4232             :                         // Shift input values by 32768 to get unsigned values
    4233      198474 :                         const __m128i inputVal = _mm_add_epi16(
    4234             :                             _mm_loadu_si128(reinterpret_cast<const __m128i *>(
    4235       99237 :                                 static_cast<const T * CPL_RESTRICT>(
    4236       99237 :                                     papoSources[iSrc]) +
    4237       99237 :                                 iOffsetLine + iCol)),
    4238             :                             shift);
    4239      297711 :                         reg0 = _mm_add_epi32(
    4240             :                             reg0,
    4241             :                             _mm_unpacklo_epi16(inputVal, _mm_setzero_si128()));
    4242      297711 :                         reg1 = _mm_add_epi32(
    4243             :                             reg1,
    4244             :                             _mm_unpackhi_epi16(inputVal, _mm_setzero_si128()));
    4245             :                     }
    4246             :                     reg0 /= fast_d;
    4247             :                     reg1 /= fast_d;
    4248         318 :                     _mm_storeu_si128(
    4249         318 :                         reinterpret_cast<__m128i *>(pDest + iCol),
    4250             :                         _mm_add_epi16(packus_epi32(reg0, reg1), shift));
    4251             :                 }
    4252         198 :                 for (; iCol < nXSize; ++iCol)
    4253             :                 {
    4254         142 :                     int32_t acc = (-std::numeric_limits<T>::min()) * nSources +
    4255         142 :                                   nSources / 2;
    4256      229895 :                     for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4257             :                     {
    4258      229753 :                         acc += static_cast<const T * CPL_RESTRICT>(
    4259      229753 :                             papoSources[iSrc])[iOffsetLine + iCol];
    4260             :                     }
    4261         284 :                     pDest[iCol] = static_cast<T>(acc / nSources +
    4262         142 :                                                  std::numeric_limits<T>::min());
    4263             :                 }
    4264             :             }
    4265           7 :             return CE_None;
    4266             :         }
    4267             :     }
    4268             : #endif  // defined(USE_SSE2) && !defined(USE_NEON_OPTIMIZATIONS)
    4269             : 
    4270             : #if defined(USE_SSE2)
    4271             :     if constexpr (std::is_same_v<Kernel, MeanKernel>)
    4272             :     {
    4273          71 :         if (!bHasNoData && eSrcType == GDT_Float32 && eBufType == GDT_Float32 &&
    4274          19 :             nPixelSpace == 4 && nSources > 0)
    4275             :         {
    4276          19 :             OptimizedMeanFloatSSE2<float, SSEWrapperFloat>(
    4277             :                 papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    4278          19 :             return CE_None;
    4279             :         }
    4280             : 
    4281          52 :         if (!bHasNoData && eSrcType == GDT_Float64 && eBufType == GDT_Float64 &&
    4282          27 :             nPixelSpace == 8 && nSources > 0)
    4283             :         {
    4284          27 :             OptimizedMeanFloatSSE2<double, SSEWrapperDouble>(
    4285             :                 papoSources, nSources, pData, nXSize, nYSize, nLineSpace);
    4286          27 :             return CE_None;
    4287             :         }
    4288             :     }
    4289             : #endif  // USE_SSE2
    4290             : 
    4291             :     /* ---- Set pixels ---- */
    4292          52 :     size_t ii = 0;
    4293         104 :     for (int iLine = 0; iLine < nYSize; ++iLine)
    4294             :     {
    4295         666 :         for (int iCol = 0; iCol < nXSize; ++iCol, ++ii)
    4296             :         {
    4297         614 :             oKernel.Reset();
    4298         614 :             bool bWriteNoData = false;
    4299             : 
    4300        2107 :             for (int iSrc = 0; iSrc < nSources; ++iSrc)
    4301             :             {
    4302        1505 :                 const double dfVal = GetSrcVal(papoSources[iSrc], eSrcType, ii);
    4303             : 
    4304        1505 :                 if (bHasNoData && IsNoData(dfVal, dfNoData))
    4305             :                 {
    4306          85 :                     if (bPropagateNoData)
    4307             :                     {
    4308          12 :                         bWriteNoData = true;
    4309          12 :                         break;
    4310             :                     }
    4311             :                 }
    4312             :                 else
    4313             :                 {
    4314        1420 :                     oKernel.ProcessPixel(dfVal);
    4315             :                 }
    4316             :             }
    4317             : 
    4318         614 :             double dfPixVal{dfNoData};
    4319         614 :             if (!bWriteNoData && oKernel.HasValue())
    4320             :             {
    4321         579 :                 dfPixVal = oKernel.GetValue();
    4322             :             }
    4323             : 
    4324         614 :             GDALCopyWords(&dfPixVal, GDT_Float64, 0,
    4325             :                           static_cast<GByte *>(pData) +
    4326         614 :                               static_cast<GSpacing>(nLineSpace) * iLine +
    4327         614 :                               iCol * nPixelSpace,
    4328             :                           eBufType, nPixelSpace, 1);
    4329             :         }
    4330             :     }
    4331             : 
    4332             :     /* ---- Return success ---- */
    4333          52 :     return CE_None;
    4334             : }  // BasicPixelFunc
    4335             : 
    4336             : /************************************************************************/
    4337             : /*                    GDALRegisterDefaultPixelFunc()                    */
    4338             : /************************************************************************/
    4339             : 
    4340             : /**
    4341             :  * This adds a default set of pixel functions to the global list of
    4342             :  * available pixel functions for derived bands:
    4343             :  *
    4344             :  * - "real": extract real part from a single raster band (just a copy if the
    4345             :  *           input is non-complex)
    4346             :  * - "imag": extract imaginary part from a single raster band (0 for
    4347             :  *           non-complex)
    4348             :  * - "complex": make a complex band merging two bands used as real and
    4349             :  *              imag values
    4350             :  * - "polar": make a complex band using input bands for amplitude and
    4351             :  *            phase values (b1 * exp( j * b2 ))
    4352             :  * - "mod": extract module from a single raster band (real or complex)
    4353             :  * - "phase": extract phase from a single raster band [-PI,PI] (0 or PI for
    4354             :               non-complex)
    4355             :  * - "conj": computes the complex conjugate of a single raster band (just a
    4356             :  *           copy if the input is non-complex)
    4357             :  * - "sum": sum 2 or more raster bands
    4358             :  * - "diff": computes the difference between 2 raster bands (b1 - b2)
    4359             :  * - "mul": multiply 2 or more raster bands
    4360             :  * - "div": divide one raster band by another (b1 / b2).
    4361             :  * - "min": minimum value of 2 or more raster bands
    4362             :  * - "max": maximum value of 2 or more raster bands
    4363             :  * - "norm_diff": computes the normalized difference between two raster bands:
    4364             :  *                ``(b1 - b2)/(b1 + b2)``.
    4365             :  * - "cmul": multiply the first band for the complex conjugate of the second
    4366             :  * - "inv": inverse (1./x).
    4367             :  * - "intensity": computes the intensity Re(x*conj(x)) of a single raster band
    4368             :  *                (real or complex)
    4369             :  * - "sqrt": perform the square root of a single raster band (real only)
    4370             :  * - "log10": compute the logarithm (base 10) of the abs of a single raster
    4371             :  *            band (real or complex): log10( abs( x ) )
    4372             :  * - "dB": perform conversion to dB of the abs of a single raster
    4373             :  *         band (real or complex): 20. * log10( abs( x ) ).
    4374             :  *         Note: the optional fact parameter can be set to 10. to get the
    4375             :  *         alternative formula: 10. * log10( abs( x ) )
    4376             :  * - "exp": computes the exponential of each element in the input band ``x``
    4377             :  *          (of real values): ``e ^ x``.
    4378             :  *          The function also accepts two optional parameters: ``base`` and
    4379             :  ``fact``
    4380             :  *          that allow to compute the generalized formula: ``base ^ ( fact *
    4381             :  x)``.
    4382             :  *          Note: this function is the recommended one to perform conversion
    4383             :  *          form logarithmic scale (dB): `` 10. ^ (x / 20.)``, in this case
    4384             :  *          ``base = 10.`` and ``fact = 1./20``
    4385             :  * - "dB2amp": perform scale conversion from logarithmic to linear
    4386             :  *             (amplitude) (i.e. 10 ^ ( x / 20 ) ) of a single raster
    4387             :  *             band (real only).
    4388             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    4389             :  with
    4390             :  *             ``base = 10.`` and ``fact = 0.05`` i.e. ``1./20``
    4391             :  * - "dB2pow": perform scale conversion from logarithmic to linear
    4392             :  *             (power) (i.e. 10 ^ ( x / 10 ) ) of a single raster
    4393             :  *             band (real only)
    4394             :  *             Deprecated in GDAL v3.5. Please use the ``exp`` pixel function
    4395             :  with
    4396             :  *             ``base = 10.`` and ``fact = 0.1`` i.e. ``1./10``
    4397             :  * - "pow": raise a single raster band to a constant power
    4398             :  * - "interpolate_linear": interpolate values between two raster bands
    4399             :  *                         using linear interpolation
    4400             :  * - "interpolate_exp": interpolate values between two raster bands using
    4401             :  *                      exponential interpolation
    4402             :  * - "scale": Apply the RasterBand metadata values of "offset" and "scale"
    4403             :  * - "reclassify": Reclassify values matching ranges in a table
    4404             :  * - "nan": Convert incoming NoData values to IEEE 754 nan
    4405             :  *
    4406             :  * @see GDALAddDerivedBandPixelFunc
    4407             :  *
    4408             :  * @return CE_None
    4409             :  */
    4410        1599 : CPLErr GDALRegisterDefaultPixelFunc()
    4411             : {
    4412        1599 :     GDALAddDerivedBandPixelFunc("real", RealPixelFunc);
    4413        1599 :     GDALAddDerivedBandPixelFunc("imag", ImagPixelFunc);
    4414        1599 :     GDALAddDerivedBandPixelFunc("complex", ComplexPixelFunc);
    4415        1599 :     GDALAddDerivedBandPixelFuncWithArgs("polar", PolarPixelFunc,
    4416             :                                         pszPolarPixelFuncMetadata);
    4417        1599 :     GDALAddDerivedBandPixelFuncWithArgs("mod", ModulePixelFunc,
    4418             :                                         pszModulePixelFuncMetadata);
    4419        1599 :     GDALAddDerivedBandPixelFuncWithArgs("abs", ModulePixelFunc,
    4420             :                                         pszModulePixelFuncMetadata);
    4421        1599 :     GDALAddDerivedBandPixelFunc("phase", PhasePixelFunc);
    4422        1599 :     GDALAddDerivedBandPixelFunc("conj", ConjPixelFunc);
    4423        1599 :     GDALAddDerivedBandPixelFuncWithArgs("sum", SumPixelFunc,
    4424             :                                         pszSumPixelFuncMetadata);
    4425        1599 :     GDALAddDerivedBandPixelFuncWithArgs("diff", DiffPixelFunc,
    4426             :                                         pszDiffPixelFuncMetadata);
    4427        1599 :     GDALAddDerivedBandPixelFuncWithArgs("mul", MulPixelFunc,
    4428             :                                         pszMulPixelFuncMetadata);
    4429        1599 :     GDALAddDerivedBandPixelFuncWithArgs("div", DivPixelFunc,
    4430             :                                         pszDivPixelFuncMetadata);
    4431        1599 :     GDALAddDerivedBandPixelFunc("cmul", CMulPixelFunc);
    4432        1599 :     GDALAddDerivedBandPixelFuncWithArgs("inv", InvPixelFunc,
    4433             :                                         pszInvPixelFuncMetadata);
    4434        1599 :     GDALAddDerivedBandPixelFunc("intensity", IntensityPixelFunc);
    4435        1599 :     GDALAddDerivedBandPixelFuncWithArgs("sqrt", SqrtPixelFunc,
    4436             :                                         pszSqrtPixelFuncMetadata);
    4437        1599 :     GDALAddDerivedBandPixelFuncWithArgs("log10", Log10PixelFunc,
    4438             :                                         pszLog10PixelFuncMetadata);
    4439        1599 :     GDALAddDerivedBandPixelFuncWithArgs("dB", DBPixelFunc,
    4440             :                                         pszDBPixelFuncMetadata);
    4441        1599 :     GDALAddDerivedBandPixelFuncWithArgs("exp", ExpPixelFunc,
    4442             :                                         pszExpPixelFuncMetadata);
    4443        1599 :     GDALAddDerivedBandPixelFunc("dB2amp",
    4444             :                                 dB2AmpPixelFunc);  // deprecated in v3.5
    4445        1599 :     GDALAddDerivedBandPixelFunc("dB2pow",
    4446             :                                 dB2PowPixelFunc);  // deprecated in v3.5
    4447        1599 :     GDALAddDerivedBandPixelFuncWithArgs("pow", PowPixelFunc,
    4448             :                                         pszPowPixelFuncMetadata);
    4449        1599 :     GDALAddDerivedBandPixelFuncWithArgs("interpolate_linear",
    4450             :                                         InterpolatePixelFunc<InterpolateLinear>,
    4451             :                                         pszInterpolatePixelFuncMetadata);
    4452        1599 :     GDALAddDerivedBandPixelFuncWithArgs(
    4453             :         "interpolate_exp", InterpolatePixelFunc<InterpolateExponential>,
    4454             :         pszInterpolatePixelFuncMetadata);
    4455        1599 :     GDALAddDerivedBandPixelFuncWithArgs("replace_nodata",
    4456             :                                         ReplaceNoDataPixelFunc,
    4457             :                                         pszReplaceNoDataPixelFuncMetadata);
    4458        1599 :     GDALAddDerivedBandPixelFuncWithArgs("scale", ScalePixelFunc,
    4459             :                                         pszScalePixelFuncMetadata);
    4460        1599 :     GDALAddDerivedBandPixelFuncWithArgs("norm_diff", NormDiffPixelFunc,
    4461             :                                         pszNormDiffPixelFuncMetadata);
    4462        1599 :     GDALAddDerivedBandPixelFuncWithArgs("min", MinPixelFunc<ReturnValue>,
    4463             :                                         pszMinMaxFuncMetadataNodata);
    4464        1599 :     GDALAddDerivedBandPixelFuncWithArgs("argmin", MinPixelFunc<ReturnIndex>,
    4465             :                                         pszArgMinMaxFuncMetadataNodata);
    4466        1599 :     GDALAddDerivedBandPixelFuncWithArgs("max", MaxPixelFunc<ReturnValue>,
    4467             :                                         pszMinMaxFuncMetadataNodata);
    4468        1599 :     GDALAddDerivedBandPixelFuncWithArgs("argmax", MaxPixelFunc<ReturnIndex>,
    4469             :                                         pszArgMinMaxFuncMetadataNodata);
    4470        1599 :     GDALAddDerivedBandPixelFuncWithArgs("expression", ExprPixelFunc,
    4471             :                                         pszExprPixelFuncMetadata);
    4472        1599 :     GDALAddDerivedBandPixelFuncWithArgs("reclassify", ReclassifyPixelFunc,
    4473             :                                         pszReclassifyPixelFuncMetadata);
    4474        1599 :     GDALAddDerivedBandPixelFuncWithArgs("round", RoundPixelFunc,
    4475             :                                         pszRoundPixelFuncMetadata);
    4476        1599 :     GDALAddDerivedBandPixelFuncWithArgs("mean", BasicPixelFunc<MeanKernel>,
    4477             :                                         pszBasicPixelFuncMetadata);
    4478        1599 :     GDALAddDerivedBandPixelFuncWithArgs("geometric_mean",
    4479             :                                         BasicPixelFunc<GeoMeanKernel>,
    4480             :                                         pszBasicPixelFuncMetadata);
    4481        1599 :     GDALAddDerivedBandPixelFuncWithArgs("harmonic_mean",
    4482             :                                         BasicPixelFunc<HarmonicMeanKernel>,
    4483             :                                         pszBasicPixelFuncMetadata);
    4484        1599 :     GDALAddDerivedBandPixelFuncWithArgs("median", BasicPixelFunc<MedianKernel>,
    4485             :                                         pszBasicPixelFuncMetadata);
    4486        1599 :     GDALAddDerivedBandPixelFuncWithArgs("quantile",
    4487             :                                         BasicPixelFunc<QuantileKernel>,
    4488             :                                         pszQuantilePixelFuncMetadata);
    4489        1599 :     GDALAddDerivedBandPixelFuncWithArgs("mode", BasicPixelFunc<ModeKernel>,
    4490             :                                         pszBasicPixelFuncMetadata);
    4491        1599 :     GDALAddDerivedBandPixelFuncWithArgs("area", AreaPixelFunc,
    4492             :                                         pszAreaPixelFuncMetadata);
    4493        1599 :     return CE_None;
    4494             : }

Generated by: LCOV version 1.14