LCOV - code coverage report
Current view: top level - frmts/vrt - pixelfunctions.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1622 1711 94.8 %
Date: 2025-08-01 10:10:57 Functions: 212 213 99.5 %

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

Generated by: LCOV version 1.14