LCOV - code coverage report
Current view: top level - apps - gdaldem_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1496 1855 80.6 %
Date: 2024-05-03 15:49:35 Functions: 76 99 76.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL DEM Utilities
       4             :  * Purpose:
       5             :  * Authors:  Matthew Perry, perrygeo at gmail.com
       6             :  *           Even Rouault, even dot rouault at spatialys.com
       7             :  *           Howard Butler, hobu.inc at gmail.com
       8             :  *           Chris Yesson, chris dot yesson at ioz dot ac dot uk
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2006, 2009 Matthew Perry
      12             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      13             :  * Portions derived from GRASS 4.1 (public domain) See
      14             :  * http://trac.osgeo.org/gdal/ticket/2975 for more information regarding
      15             :  * history of this code
      16             :  *
      17             :  * Permission is hereby granted, free of charge, to any person obtaining a
      18             :  * copy of this software and associated documentation files (the "Software"),
      19             :  * to deal in the Software without restriction, including without limitation
      20             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      21             :  * and/or sell copies of the Software, and to permit persons to whom the
      22             :  * Software is furnished to do so, subject to the following conditions:
      23             :  *
      24             :  * The above copyright notice and this permission notice shall be included
      25             :  * in all copies or substantial portions of the Software.
      26             :  *
      27             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      28             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      29             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      30             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      31             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      32             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      33             :  * DEALINGS IN THE SOFTWARE.
      34             :  ****************************************************************************
      35             :  *
      36             :  * Slope and aspect calculations based on original method for GRASS GIS 4.1
      37             :  * by Michael Shapiro, U.S.Army Construction Engineering Research Laboratory
      38             :  *    Olga Waupotitsch, U.S.Army Construction Engineering Research Laboratory
      39             :  *    Marjorie Larson, U.S.Army Construction Engineering Research Laboratory
      40             :  * as found in GRASS's r.slope.aspect module.
      41             :  *
      42             :  * Horn's formula is used to find the first order derivatives in x and y
      43             :  *directions for slope and aspect calculations: Horn, B. K. P. (1981). "Hill
      44             :  *Shading and the Reflectance Map", Proceedings of the IEEE, 69(1):14-47.
      45             :  *
      46             :  * Other reference :
      47             :  * Burrough, P.A. and McDonell, R.A., 1998. Principles of Geographical
      48             :  *Information Systems. p. 190.
      49             :  *
      50             :  * Shaded relief based on original method for GRASS GIS 4.1 by Jim Westervelt,
      51             :  * U.S. Army Construction Engineering Research Laboratory
      52             :  * as found in GRASS's r.shaded.relief (formerly shade.rel.sh) module.
      53             :  * ref: "r.mapcalc: An Algebra for GIS and Image Processing",
      54             :  * by Michael Shapiro and Jim Westervelt, U.S. Army Construction Engineering
      55             :  * Research Laboratory (March/1991)
      56             :  *
      57             :  * Color table of named colors and lookup code derived from
      58             :  *src/libes/gis/named_colr.c of GRASS 4.1
      59             :  *
      60             :  * TRI -
      61             :  * For bathymetric use cases, implements
      62             :  * Terrain Ruggedness Index is as described in Wilson et al. (2007)
      63             :  * this is based on the method of Valentine et al. (2004)
      64             :  *
      65             :  * For terrestrial use cases, implements
      66             :  * Riley, S.J., De Gloria, S.D., Elliot, R. (1999): A Terrain Ruggedness
      67             :  * that Quantifies Topographic Heterogeneity. Intermountain Journal of Science,
      68             :  *Vol.5, No.1-4, pp.23-27
      69             :  *
      70             :  *
      71             :  * TPI - Topographic Position Index follows the description in
      72             :  * Wilson et al. (2007), following Weiss (2001).  The radius is fixed
      73             :  * at 1 cell width/height
      74             :  *
      75             :  * Roughness - follows the definition in Wilson et al. (2007), which follows
      76             :  * Dartnell (2000).
      77             :  *
      78             :  * References for TRI/TPI/Roughness:
      79             :  * Dartnell, P. 2000. Applying Remote Sensing Techniques to map Seafloor
      80             :  *  Geology/Habitat Relationships. Masters Thesis, San Francisco State
      81             :  *  University, pp. 108.
      82             :  * Valentine, P. C., S. J. Fuller, L. A. Scully. 2004. Terrain Ruggedness
      83             :  *  Analysis and Distribution of Boulder Ridges in the Stellwagen Bank National
      84             :  *  Marine Sanctuary Region (poster). Galway, Ireland: 5th International
      85             :  *  Symposium on Marine Geological and Biological Habitat Mapping (GeoHAB),
      86             :  *  May 2004.
      87             :  * Weiss, A. D. 2001. Topographic Positions and Landforms Analysis (poster),
      88             :  *  ESRI International User Conference, July 2001. San Diego, CA: ESRI.
      89             :  * Wilson, M. F. J.; O'Connell, B.; Brown, C.; Guinan, J. C. & Grehan, A. J.
      90             :  *  Multiscale terrain analysis of multibeam bathymetry data for habitat mapping
      91             :  *  on the continental slope Marine Geodesy, 2007, 30, 3-35
      92             :  ****************************************************************************/
      93             : 
      94             : // Include before others for mingw for VSIStatBufL
      95             : #include "cpl_conv.h"
      96             : 
      97             : #include "cpl_port.h"
      98             : #include "gdal_utils.h"
      99             : #include "gdal_utils_priv.h"
     100             : #include "commonutils.h"
     101             : 
     102             : #include <cfloat>
     103             : #include <cmath>
     104             : #include <cstdio>
     105             : #include <cstdlib>
     106             : #include <cstring>
     107             : #if HAVE_SYS_STAT_H
     108             : #include <sys/stat.h>
     109             : #endif
     110             : 
     111             : #include <algorithm>
     112             : #include <limits>
     113             : 
     114             : #include "cpl_error.h"
     115             : #include "cpl_progress.h"
     116             : #include "cpl_string.h"
     117             : #include "cpl_vsi.h"
     118             : #include "gdal.h"
     119             : #include "gdal_priv.h"
     120             : 
     121             : #if defined(__SSE2__) || defined(_M_X64)
     122             : #define HAVE_16_SSE_REG
     123             : #define HAVE_SSE2
     124             : #include "emmintrin.h"
     125             : #endif
     126             : 
     127             : static const double kdfDegreesToRadians = M_PI / 180.0;
     128             : static const double kdfRadiansToDegrees = 180.0 / M_PI;
     129             : 
     130             : typedef enum
     131             : {
     132             :     COLOR_SELECTION_INTERPOLATE,
     133             :     COLOR_SELECTION_NEAREST_ENTRY,
     134             :     COLOR_SELECTION_EXACT_ENTRY
     135             : } ColorSelectionMode;
     136             : 
     137             : namespace gdal::GDALDEM
     138             : {
     139             : enum class GradientAlg
     140             : {
     141             :     HORN,
     142             :     ZEVENBERGEN_THORNE,
     143             : };
     144             : 
     145             : enum class TRIAlg
     146             : {
     147             :     WILSON,
     148             :     RILEY,
     149             : };
     150             : }  // namespace gdal::GDALDEM
     151             : 
     152             : using namespace gdal::GDALDEM;
     153             : 
     154             : struct GDALDEMProcessingOptions
     155             : {
     156             :     /*! output format. Use the short format name. */
     157             :     char *pszFormat = nullptr;
     158             : 
     159             :     /*! the progress function to use */
     160             :     GDALProgressFunc pfnProgress = nullptr;
     161             : 
     162             :     /*! pointer to the progress data variable */
     163             :     void *pProgressData = nullptr;
     164             : 
     165             :     double z = 1.0;
     166             :     double scale = 1.0;
     167             :     double az = 315.0;
     168             :     double alt = 45.0;
     169             :     int slopeFormat = 1;  // 0 = 'percent' or 1 = 'degrees'
     170             :     bool bAddAlpha = false;
     171             :     bool bZeroForFlat = false;
     172             :     bool bAngleAsAzimuth = true;
     173             :     ColorSelectionMode eColorSelectionMode = COLOR_SELECTION_INTERPOLATE;
     174             :     bool bComputeAtEdges = false;
     175             :     bool bGradientAlgSpecified = false;
     176             :     GradientAlg eGradientAlg = GradientAlg::HORN;
     177             :     bool bTRIAlgSpecified = false;
     178             :     TRIAlg eTRIAlg = TRIAlg::RILEY;
     179             :     bool bCombined = false;
     180             :     bool bIgor = false;
     181             :     bool bMultiDirectional = false;
     182             :     char **papszCreateOptions = nullptr;
     183             :     int nBand = 1;
     184             : };
     185             : 
     186             : /************************************************************************/
     187             : /*                          ComputeVal()                                */
     188             : /************************************************************************/
     189             : 
     190             : template <class T> struct GDALGeneric3x3ProcessingAlg
     191             : {
     192             :     typedef float (*type)(const T *pafWindow, float fDstNoDataValue,
     193             :                           void *pData);
     194             : };
     195             : 
     196             : template <class T> struct GDALGeneric3x3ProcessingAlg_multisample
     197             : {
     198             :     typedef int (*type)(const T *pafThreeLineWin, int nLine1Off, int nLine2Off,
     199             :                         int nLine3Off, int nXSize, void *pData,
     200             :                         float *pafOutputBuf);
     201             : };
     202             : 
     203             : template <class T>
     204             : static float ComputeVal(bool bSrcHasNoData, T fSrcNoDataValue,
     205             :                         bool bIsSrcNoDataNan, T *afWin, float fDstNoDataValue,
     206             :                         typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
     207             :                         void *pData, bool bComputeAtEdges);
     208             : 
     209             : template <>
     210       42973 : float ComputeVal(bool bSrcHasNoData, float fSrcNoDataValue,
     211             :                  bool bIsSrcNoDataNan, float *afWin, float fDstNoDataValue,
     212             :                  GDALGeneric3x3ProcessingAlg<float>::type pfnAlg, void *pData,
     213             :                  bool bComputeAtEdges)
     214             : {
     215       85946 :     if (bSrcHasNoData &&
     216       42973 :         ((!bIsSrcNoDataNan && ARE_REAL_EQUAL(afWin[4], fSrcNoDataValue)) ||
     217           0 :          (bIsSrcNoDataNan && CPLIsNan(afWin[4]))))
     218             :     {
     219           8 :         return fDstNoDataValue;
     220             :     }
     221       42965 :     else if (bSrcHasNoData)
     222             :     {
     223      429641 :         for (int k = 0; k < 9; k++)
     224             :         {
     225      386677 :             if ((!bIsSrcNoDataNan &&
     226      773354 :                  ARE_REAL_EQUAL(afWin[k], fSrcNoDataValue)) ||
     227           0 :                 (bIsSrcNoDataNan && CPLIsNan(afWin[k])))
     228             :             {
     229           9 :                 if (bComputeAtEdges)
     230           8 :                     afWin[k] = afWin[4];
     231             :                 else
     232           1 :                     return fDstNoDataValue;
     233             :             }
     234             :         }
     235             :     }
     236             : 
     237       42964 :     return pfnAlg(afWin, fDstNoDataValue, pData);
     238             : }
     239             : 
     240             : template <>
     241      274825 : float ComputeVal(bool bSrcHasNoData, GInt32 fSrcNoDataValue,
     242             :                  bool /* bIsSrcNoDataNan */, GInt32 *afWin,
     243             :                  float fDstNoDataValue,
     244             :                  GDALGeneric3x3ProcessingAlg<GInt32>::type pfnAlg, void *pData,
     245             :                  bool bComputeAtEdges)
     246             : {
     247      274825 :     if (bSrcHasNoData && afWin[4] == fSrcNoDataValue)
     248             :     {
     249         584 :         return fDstNoDataValue;
     250             :     }
     251      274241 :     else if (bSrcHasNoData)
     252             :     {
     253      444161 :         for (int k = 0; k < 9; k++)
     254             :         {
     255      399745 :             if (afWin[k] == fSrcNoDataValue)
     256             :             {
     257           9 :                 if (bComputeAtEdges)
     258           8 :                     afWin[k] = afWin[4];
     259             :                 else
     260           1 :                     return fDstNoDataValue;
     261             :             }
     262             :         }
     263             :     }
     264             : 
     265      274240 :     return pfnAlg(afWin, fDstNoDataValue, pData);
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                           INTERPOL()                                 */
     270             : /************************************************************************/
     271             : 
     272             : template <class T>
     273             : static T INTERPOL(T a, T b, int bSrcHasNodata, T fSrcNoDataValue);
     274             : 
     275             : template <>
     276        1464 : float INTERPOL(float a, float b, int bSrcHasNoData, float fSrcNoDataValue)
     277             : {
     278        1464 :     return ((bSrcHasNoData && (ARE_REAL_EQUAL(a, fSrcNoDataValue) ||
     279        1440 :                                ARE_REAL_EQUAL(b, fSrcNoDataValue)))
     280        1488 :                 ? fSrcNoDataValue
     281        1464 :                 : 2 * (a) - (b));
     282             : }
     283             : 
     284             : template <>
     285       10104 : GInt32 INTERPOL(GInt32 a, GInt32 b, int bSrcHasNoData, GInt32 fSrcNoDataValue)
     286             : {
     287       10104 :     if (bSrcHasNoData && ((a == fSrcNoDataValue) || (b == fSrcNoDataValue)))
     288          24 :         return fSrcNoDataValue;
     289       10080 :     int nVal = 2 * a - b;
     290       10080 :     if (bSrcHasNoData && fSrcNoDataValue == nVal)
     291           0 :         return nVal + 1;
     292       10080 :     return nVal;
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                  GDALGeneric3x3Processing()                          */
     297             : /************************************************************************/
     298             : 
     299             : template <class T>
     300          37 : static CPLErr GDALGeneric3x3Processing(
     301             :     GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand,
     302             :     typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
     303             :     typename GDALGeneric3x3ProcessingAlg_multisample<T>::type
     304             :         pfnAlg_multisample,
     305             :     void *pData, bool bComputeAtEdges, GDALProgressFunc pfnProgress,
     306             :     void *pProgressData)
     307             : {
     308          37 :     if (pfnProgress == nullptr)
     309          31 :         pfnProgress = GDALDummyProgress;
     310             : 
     311             :     /* -------------------------------------------------------------------- */
     312             :     /*      Initialize progress counter.                                    */
     313             :     /* -------------------------------------------------------------------- */
     314          37 :     if (!pfnProgress(0.0, nullptr, pProgressData))
     315             :     {
     316           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     317           0 :         return CE_Failure;
     318             :     }
     319             : 
     320          37 :     const int nXSize = GDALGetRasterBandXSize(hSrcBand);
     321          37 :     const int nYSize = GDALGetRasterBandYSize(hSrcBand);
     322             : 
     323             :     // 1 line destination buffer.
     324             :     float *pafOutputBuf =
     325          37 :         static_cast<float *>(VSI_MALLOC2_VERBOSE(sizeof(float), nXSize));
     326             :     // 3 line rotating source buffer.
     327             :     T *pafThreeLineWin =
     328          37 :         static_cast<T *>(VSI_MALLOC2_VERBOSE(3 * sizeof(T), nXSize + 1));
     329          37 :     if (pafOutputBuf == nullptr || pafThreeLineWin == nullptr)
     330             :     {
     331           0 :         VSIFree(pafOutputBuf);
     332           0 :         VSIFree(pafThreeLineWin);
     333           0 :         return CE_Failure;
     334             :     }
     335             : 
     336             :     GDALDataType eReadDT;
     337          37 :     int bSrcHasNoData = FALSE;
     338             :     const double dfNoDataValue =
     339          37 :         GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData);
     340             : 
     341          37 :     int bIsSrcNoDataNan = FALSE;
     342          37 :     T fSrcNoDataValue = 0;
     343             :     if (std::numeric_limits<T>::is_integer)
     344             :     {
     345          33 :         eReadDT = GDT_Int32;
     346          33 :         if (bSrcHasNoData)
     347             :         {
     348          31 :             GDALDataType eSrcDT = GDALGetRasterDataType(hSrcBand);
     349          31 :             CPLAssert(eSrcDT == GDT_Byte || eSrcDT == GDT_UInt16 ||
     350             :                       eSrcDT == GDT_Int16);
     351          31 :             const int nMinVal = (eSrcDT == GDT_Byte)     ? 0
     352             :                                 : (eSrcDT == GDT_UInt16) ? 0
     353             :                                                          : -32768;
     354          31 :             const int nMaxVal = (eSrcDT == GDT_Byte)     ? 255
     355             :                                 : (eSrcDT == GDT_UInt16) ? 65535
     356             :                                                          : 32767;
     357             : 
     358          31 :             if (fabs(dfNoDataValue - floor(dfNoDataValue + 0.5)) < 1e-2 &&
     359          31 :                 dfNoDataValue >= nMinVal && dfNoDataValue <= nMaxVal)
     360             :             {
     361          31 :                 fSrcNoDataValue = static_cast<T>(floor(dfNoDataValue + 0.5));
     362             :             }
     363             :             else
     364             :             {
     365           0 :                 bSrcHasNoData = FALSE;
     366             :             }
     367             :         }
     368             :     }
     369             :     else
     370             :     {
     371           4 :         eReadDT = GDT_Float32;
     372           4 :         fSrcNoDataValue = static_cast<T>(dfNoDataValue);
     373           4 :         bIsSrcNoDataNan = bSrcHasNoData && CPLIsNan(dfNoDataValue);
     374             :     }
     375             : 
     376          37 :     int bDstHasNoData = FALSE;
     377          37 :     float fDstNoDataValue =
     378          37 :         static_cast<float>(GDALGetRasterNoDataValue(hDstBand, &bDstHasNoData));
     379          37 :     if (!bDstHasNoData)
     380           0 :         fDstNoDataValue = 0.0;
     381             : 
     382          37 :     int nLine1Off = 0;
     383          37 :     int nLine2Off = nXSize;
     384          37 :     int nLine3Off = 2 * nXSize;
     385             : 
     386             :     // Move a 3x3 pafWindow over each cell
     387             :     // (where the cell in question is #4)
     388             :     //
     389             :     //      0 1 2
     390             :     //      3 4 5
     391             :     //      6 7 8
     392             : 
     393             :     /* Preload the first 2 lines */
     394             : 
     395          37 :     bool abLineHasNoDataValue[3] = {CPL_TO_BOOL(bSrcHasNoData),
     396          37 :                                     CPL_TO_BOOL(bSrcHasNoData),
     397          37 :                                     CPL_TO_BOOL(bSrcHasNoData)};
     398             : 
     399             :     // Create an extra scope for VC12 to ignore i.
     400             :     {
     401         111 :         for (int i = 0; i < 2 && i < nYSize; i++)
     402             :         {
     403         148 :             if (GDALRasterIO(hSrcBand, GF_Read, 0, i, nXSize, 1,
     404          74 :                              pafThreeLineWin + i * nXSize, nXSize, 1, eReadDT,
     405          74 :                              0, 0) != CE_None)
     406             :             {
     407           0 :                 CPLFree(pafOutputBuf);
     408           0 :                 CPLFree(pafThreeLineWin);
     409             : 
     410           0 :                 return CE_Failure;
     411             :             }
     412          66 :             if (std::numeric_limits<T>::is_integer && bSrcHasNoData)
     413             :             {
     414          62 :                 abLineHasNoDataValue[i] = false;
     415        4902 :                 for (int iX = 0; iX < nXSize; iX++)
     416             :                 {
     417        4862 :                     if (pafThreeLineWin[i * nXSize + iX] == fSrcNoDataValue)
     418             :                     {
     419          22 :                         abLineHasNoDataValue[i] = true;
     420          22 :                         break;
     421             :                     }
     422             :                 }
     423             :             }
     424             :         }
     425             :     }  // End extra scope for VC12
     426             : 
     427          37 :     CPLErr eErr = CE_None;
     428          37 :     if (bComputeAtEdges && nXSize >= 2 && nYSize >= 2)
     429             :     {
     430         862 :         for (int j = 0; j < nXSize; j++)
     431             :         {
     432         853 :             int jmin = (j == 0) ? j : j - 1;
     433         853 :             int jmax = (j == nXSize - 1) ? j : j + 1;
     434             : 
     435        2559 :             T afWin[9] = {
     436         853 :                 INTERPOL(pafThreeLineWin[jmin], pafThreeLineWin[nXSize + jmin],
     437             :                          bSrcHasNoData, fSrcNoDataValue),
     438         853 :                 INTERPOL(pafThreeLineWin[j], pafThreeLineWin[nXSize + j],
     439             :                          bSrcHasNoData, fSrcNoDataValue),
     440         853 :                 INTERPOL(pafThreeLineWin[jmax], pafThreeLineWin[nXSize + jmax],
     441             :                          bSrcHasNoData, fSrcNoDataValue),
     442         853 :                 pafThreeLineWin[jmin],
     443         853 :                 pafThreeLineWin[j],
     444         853 :                 pafThreeLineWin[jmax],
     445         853 :                 pafThreeLineWin[nXSize + jmin],
     446         853 :                 pafThreeLineWin[nXSize + j],
     447         853 :                 pafThreeLineWin[nXSize + jmax]};
     448        1706 :             pafOutputBuf[j] =
     449         853 :                 ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
     450         853 :                            CPL_TO_BOOL(bIsSrcNoDataNan), afWin, fDstNoDataValue,
     451             :                            pfnAlg, pData, bComputeAtEdges);
     452             :         }
     453           9 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, 0, nXSize, 1, pafOutputBuf,
     454           9 :                             nXSize, 1, GDT_Float32, 0, 0);
     455             :     }
     456             :     else
     457             :     {
     458             :         // Exclude the edges
     459        2139 :         for (int j = 0; j < nXSize; j++)
     460             :         {
     461        2111 :             pafOutputBuf[j] = fDstNoDataValue;
     462             :         }
     463          28 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, 0, nXSize, 1, pafOutputBuf,
     464             :                             nXSize, 1, GDT_Float32, 0, 0);
     465             : 
     466          28 :         if (eErr == CE_None && nYSize > 1)
     467             :         {
     468          28 :             eErr = GDALRasterIO(hDstBand, GF_Write, 0, nYSize - 1, nXSize, 1,
     469             :                                 pafOutputBuf, nXSize, 1, GDT_Float32, 0, 0);
     470             :         }
     471             :     }
     472          37 :     if (eErr != CE_None)
     473             :     {
     474           0 :         CPLFree(pafOutputBuf);
     475           0 :         CPLFree(pafThreeLineWin);
     476             : 
     477           0 :         return eErr;
     478             :     }
     479             : 
     480          37 :     int i = 1;  // Used after for.
     481        2927 :     for (; i < nYSize - 1; i++)
     482             :     {
     483             :         /* Read third line of the line buffer */
     484             :         eErr =
     485        5780 :             GDALRasterIO(hSrcBand, GF_Read, 0, i + 1, nXSize, 1,
     486        2890 :                          pafThreeLineWin + nLine3Off, nXSize, 1, eReadDT, 0, 0);
     487        2890 :         if (eErr != CE_None)
     488             :         {
     489           0 :             CPLFree(pafOutputBuf);
     490           0 :             CPLFree(pafThreeLineWin);
     491             : 
     492           0 :             return eErr;
     493             :         }
     494             : 
     495             :         // In case none of the 3 lines have nodata values, then no need to
     496             :         // check it in ComputeVal()
     497        2890 :         bool bOneOfThreeLinesHasNoData = CPL_TO_BOOL(bSrcHasNoData);
     498        2650 :         if (std::numeric_limits<T>::is_integer && bSrcHasNoData)
     499             :         {
     500        2454 :             bool bLastLineHasNoDataValue = false;
     501        2454 :             int iX = 0;
     502       73854 :             for (; iX + 3 < nXSize; iX += 4)
     503             :             {
     504       71472 :                 if (pafThreeLineWin[nLine3Off + iX] == fSrcNoDataValue ||
     505       71400 :                     pafThreeLineWin[nLine3Off + iX + 1] == fSrcNoDataValue ||
     506       71400 :                     pafThreeLineWin[nLine3Off + iX + 2] == fSrcNoDataValue ||
     507       71400 :                     pafThreeLineWin[nLine3Off + iX + 3] == fSrcNoDataValue)
     508             :                 {
     509          72 :                     bLastLineHasNoDataValue = true;
     510          72 :                     break;
     511             :                 }
     512             :             }
     513        2454 :             if (!bLastLineHasNoDataValue)
     514             :             {
     515        4768 :                 for (; iX < nXSize; iX++)
     516             :                 {
     517        2386 :                     if (pafThreeLineWin[nLine3Off + iX] == fSrcNoDataValue)
     518             :                     {
     519           6 :                         bLastLineHasNoDataValue = true;
     520             :                     }
     521             :                 }
     522             :             }
     523        2454 :             abLineHasNoDataValue[nLine3Off / nXSize] = bLastLineHasNoDataValue;
     524             : 
     525        4834 :             bOneOfThreeLinesHasNoData = abLineHasNoDataValue[0] ||
     526        4834 :                                         abLineHasNoDataValue[1] ||
     527             :                                         abLineHasNoDataValue[2];
     528             :         }
     529             : 
     530        2890 :         if (bComputeAtEdges && nXSize >= 2)
     531             :         {
     532         835 :             int j = 0;
     533        3340 :             T afWin[9] = {INTERPOL(pafThreeLineWin[nLine1Off + j],
     534         835 :                                    pafThreeLineWin[nLine1Off + j + 1],
     535             :                                    bSrcHasNoData, fSrcNoDataValue),
     536         835 :                           pafThreeLineWin[nLine1Off + j],
     537         835 :                           pafThreeLineWin[nLine1Off + j + 1],
     538         955 :                           INTERPOL(pafThreeLineWin[nLine2Off + j],
     539         835 :                                    pafThreeLineWin[nLine2Off + j + 1],
     540             :                                    bSrcHasNoData, fSrcNoDataValue),
     541         835 :                           pafThreeLineWin[nLine2Off + j],
     542         835 :                           pafThreeLineWin[nLine2Off + j + 1],
     543         955 :                           INTERPOL(pafThreeLineWin[nLine3Off + j],
     544         835 :                                    pafThreeLineWin[nLine3Off + j + 1],
     545             :                                    bSrcHasNoData, fSrcNoDataValue),
     546         835 :                           pafThreeLineWin[nLine3Off + j],
     547         835 :                           pafThreeLineWin[nLine3Off + j + 1]};
     548             : 
     549        1670 :             pafOutputBuf[j] =
     550         835 :                 ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
     551         835 :                            fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
     552         835 :                            fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
     553             :         }
     554             :         else
     555             :         {
     556             :             // Exclude the edges
     557        2055 :             pafOutputBuf[0] = fDstNoDataValue;
     558             :         }
     559             : 
     560        2890 :         int j = 1;
     561        2890 :         if (pfnAlg_multisample && !bOneOfThreeLinesHasNoData)
     562             :         {
     563         672 :             j = pfnAlg_multisample(pafThreeLineWin, nLine1Off, nLine2Off,
     564             :                                    nLine3Off, nXSize, pData, pafOutputBuf);
     565             :         }
     566             : 
     567      260188 :         for (; j < nXSize - 1; j++)
     568             :         {
     569      257298 :             T afWin[9] = {pafThreeLineWin[nLine1Off + j - 1],
     570      257298 :                           pafThreeLineWin[nLine1Off + j],
     571      257298 :                           pafThreeLineWin[nLine1Off + j + 1],
     572      257298 :                           pafThreeLineWin[nLine2Off + j - 1],
     573      257298 :                           pafThreeLineWin[nLine2Off + j],
     574      257298 :                           pafThreeLineWin[nLine2Off + j + 1],
     575      257298 :                           pafThreeLineWin[nLine3Off + j - 1],
     576      257298 :                           pafThreeLineWin[nLine3Off + j],
     577      257298 :                           pafThreeLineWin[nLine3Off + j + 1]};
     578             : 
     579      514596 :             pafOutputBuf[j] =
     580      257298 :                 ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
     581      257298 :                            fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
     582             :                            fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
     583             :         }
     584             : 
     585        2890 :         if (bComputeAtEdges && nXSize >= 2)
     586             :         {
     587         835 :             j = nXSize - 1;
     588             : 
     589        3340 :             T afWin[9] = {pafThreeLineWin[nLine1Off + j - 1],
     590         835 :                           pafThreeLineWin[nLine1Off + j],
     591         955 :                           INTERPOL(pafThreeLineWin[nLine1Off + j],
     592         835 :                                    pafThreeLineWin[nLine1Off + j - 1],
     593             :                                    bSrcHasNoData, fSrcNoDataValue),
     594         835 :                           pafThreeLineWin[nLine2Off + j - 1],
     595         835 :                           pafThreeLineWin[nLine2Off + j],
     596         955 :                           INTERPOL(pafThreeLineWin[nLine2Off + j],
     597         835 :                                    pafThreeLineWin[nLine2Off + j - 1],
     598             :                                    bSrcHasNoData, fSrcNoDataValue),
     599         835 :                           pafThreeLineWin[nLine3Off + j - 1],
     600         835 :                           pafThreeLineWin[nLine3Off + j],
     601         955 :                           INTERPOL(pafThreeLineWin[nLine3Off + j],
     602         835 :                                    pafThreeLineWin[nLine3Off + j - 1],
     603             :                                    bSrcHasNoData, fSrcNoDataValue)};
     604             : 
     605        1670 :             pafOutputBuf[j] =
     606         835 :                 ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
     607         835 :                            fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
     608         835 :                            fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
     609             :         }
     610             :         else
     611             :         {
     612             :             // Exclude the edges
     613        2055 :             if (nXSize > 1)
     614        2055 :                 pafOutputBuf[nXSize - 1] = fDstNoDataValue;
     615             :         }
     616             : 
     617             :         /* -----------------------------------------
     618             :          * Write Line to Raster
     619             :          */
     620        2890 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, i, nXSize, 1, pafOutputBuf,
     621             :                             nXSize, 1, GDT_Float32, 0, 0);
     622        2890 :         if (eErr != CE_None)
     623             :         {
     624           0 :             CPLFree(pafOutputBuf);
     625           0 :             CPLFree(pafThreeLineWin);
     626             : 
     627           0 :             return eErr;
     628             :         }
     629             : 
     630        2890 :         if (!pfnProgress(1.0 * (i + 1) / nYSize, nullptr, pProgressData))
     631             :         {
     632           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     633           0 :             eErr = CE_Failure;
     634             : 
     635           0 :             CPLFree(pafOutputBuf);
     636           0 :             CPLFree(pafThreeLineWin);
     637             : 
     638           0 :             return eErr;
     639             :         }
     640             : 
     641        2890 :         const int nTemp = nLine1Off;
     642        2890 :         nLine1Off = nLine2Off;
     643        2890 :         nLine2Off = nLine3Off;
     644        2890 :         nLine3Off = nTemp;
     645             :     }
     646             : 
     647          37 :     if (bComputeAtEdges && nXSize >= 2 && nYSize >= 2)
     648             :     {
     649         862 :         for (int j = 0; j < nXSize; j++)
     650             :         {
     651         853 :             int jmin = (j == 0) ? j : j - 1;
     652         853 :             int jmax = (j == nXSize - 1) ? j : j + 1;
     653             : 
     654        3412 :             T afWin[9] = {
     655         853 :                 pafThreeLineWin[nLine1Off + jmin],
     656         853 :                 pafThreeLineWin[nLine1Off + j],
     657         853 :                 pafThreeLineWin[nLine1Off + jmax],
     658         853 :                 pafThreeLineWin[nLine2Off + jmin],
     659         853 :                 pafThreeLineWin[nLine2Off + j],
     660         853 :                 pafThreeLineWin[nLine2Off + jmax],
     661         977 :                 INTERPOL(pafThreeLineWin[nLine2Off + jmin],
     662         853 :                          pafThreeLineWin[nLine1Off + jmin], bSrcHasNoData,
     663             :                          fSrcNoDataValue),
     664         977 :                 INTERPOL(pafThreeLineWin[nLine2Off + j],
     665         853 :                          pafThreeLineWin[nLine1Off + j], bSrcHasNoData,
     666             :                          fSrcNoDataValue),
     667         977 :                 INTERPOL(pafThreeLineWin[nLine2Off + jmax],
     668         853 :                          pafThreeLineWin[nLine1Off + jmax], bSrcHasNoData,
     669             :                          fSrcNoDataValue),
     670             :             };
     671             : 
     672        1706 :             pafOutputBuf[j] =
     673         853 :                 ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
     674         853 :                            CPL_TO_BOOL(bIsSrcNoDataNan), afWin, fDstNoDataValue,
     675             :                            pfnAlg, pData, bComputeAtEdges);
     676             :         }
     677           9 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, i, nXSize, 1, pafOutputBuf,
     678             :                             nXSize, 1, GDT_Float32, 0, 0);
     679           9 :         if (eErr != CE_None)
     680             :         {
     681           0 :             CPLFree(pafOutputBuf);
     682           0 :             CPLFree(pafThreeLineWin);
     683             : 
     684           0 :             return eErr;
     685             :         }
     686             :     }
     687             : 
     688          37 :     pfnProgress(1.0, nullptr, pProgressData);
     689          37 :     eErr = CE_None;
     690             : 
     691          37 :     CPLFree(pafOutputBuf);
     692          37 :     CPLFree(pafThreeLineWin);
     693             : 
     694          37 :     return eErr;
     695             : }
     696             : 
     697             : /************************************************************************/
     698             : /*                            GradientAlg                               */
     699             : /************************************************************************/
     700             : 
     701             : template <class T, GradientAlg alg> struct Gradient
     702             : {
     703             :     static void inline calc(const T *afWin, double inv_ewres, double inv_nsres,
     704             :                             double &x, double &y);
     705             : };
     706             : 
     707             : template <class T> struct Gradient<T, GradientAlg::HORN>
     708             : {
     709       42963 :     static void calc(const T *afWin, double inv_ewres, double inv_nsres,
     710             :                      double &x, double &y)
     711             :     {
     712       42963 :         x = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
     713       42963 :              (afWin[2] + afWin[5] + afWin[5] + afWin[8])) *
     714             :             inv_ewres;
     715             : 
     716       42963 :         y = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
     717       42963 :              (afWin[0] + afWin[1] + afWin[1] + afWin[2])) *
     718             :             inv_nsres;
     719       42963 :     }
     720             : };
     721             : 
     722             : template <class T> struct Gradient<T, GradientAlg::ZEVENBERGEN_THORNE>
     723             : {
     724       42963 :     static void calc(const T *afWin, double inv_ewres, double inv_nsres,
     725             :                      double &x, double &y)
     726             :     {
     727       42963 :         x = (afWin[3] - afWin[5]) * inv_ewres;
     728       42963 :         y = (afWin[7] - afWin[1]) * inv_nsres;
     729       42963 :     }
     730             : };
     731             : 
     732             : /************************************************************************/
     733             : /*                         GDALHillshade()                              */
     734             : /************************************************************************/
     735             : 
     736             : typedef struct
     737             : {
     738             :     double inv_nsres;
     739             :     double inv_ewres;
     740             :     double sin_altRadians;
     741             :     double cos_alt_mul_z;
     742             :     double azRadians;
     743             :     double cos_az_mul_cos_alt_mul_z;
     744             :     double sin_az_mul_cos_alt_mul_z;
     745             :     double square_z;
     746             :     double sin_altRadians_mul_254;
     747             :     double cos_az_mul_cos_alt_mul_z_mul_254;
     748             :     double sin_az_mul_cos_alt_mul_z_mul_254;
     749             : 
     750             :     double square_z_mul_square_inv_res;
     751             :     double cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res;
     752             :     double sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res;
     753             :     double z_scaled;
     754             : } GDALHillshadeAlgData;
     755             : 
     756             : /* Unoptimized formulas are :
     757             :     x = psData->z*((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
     758             :         (afWin[2] + afWin[5] + afWin[5] + afWin[8])) /
     759             :         (8.0 * psData->ewres * psData->scale);
     760             : 
     761             :     y = psData->z*((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
     762             :         (afWin[0] + afWin[1] + afWin[1] + afWin[2])) /
     763             :         (8.0 * psData->nsres * psData->scale);
     764             : 
     765             :     slope = atan(sqrt(x*x + y*y));
     766             : 
     767             :     aspect = atan2(y,x);
     768             : 
     769             :     cang = sin(alt) * cos(slope) +
     770             :            cos(alt) * sin(slope) *
     771             :            cos(az - M_PI/2 - aspect);
     772             : 
     773             : We can avoid a lot of trigonometric computations:
     774             : 
     775             :     since cos(atan(x)) = 1 / sqrt(1+x^2)
     776             :       ==> cos(slope) = 1 / sqrt(1+ x*x+y*y)
     777             : 
     778             :       and sin(atan(x)) = x / sqrt(1+x^2)
     779             :       ==> sin(slope) = sqrt(x*x + y*y) / sqrt(1+ x*x+y*y)
     780             : 
     781             :       and cos(az - M_PI/2 - aspect)
     782             :         = cos(-az + M_PI/2 + aspect)
     783             :         = cos(M_PI/2 - (az - aspect))
     784             :         = sin(az - aspect)
     785             :         = -sin(aspect-az)
     786             : 
     787             : ==> cang = (sin(alt) - cos(alt) * sqrt(x*x + y*y)  * sin(aspect-as)) /
     788             :            sqrt(1+ x*x+y*y)
     789             : 
     790             :     But:
     791             :     sin(aspect - az) = sin(aspect)*cos(az) - cos(aspect)*sin(az))
     792             : 
     793             : and as sin(aspect)=sin(atan2(y,x)) = y / sqrt(xx_plus_yy)
     794             :    and cos(aspect)=cos(atan2(y,x)) = x / sqrt(xx_plus_yy)
     795             : 
     796             :     sin(aspect - az) = (y * cos(az) - x * sin(az)) / sqrt(xx_plus_yy)
     797             : 
     798             : so we get a final formula with just one transcendental function
     799             : (reciprocal of square root):
     800             : 
     801             :     cang = (psData->sin_altRadians -
     802             :            (y * psData->cos_az_mul_cos_alt_mul_z -
     803             :             x * psData->sin_az_mul_cos_alt_mul_z)) /
     804             :            sqrt(1 + psData->square_z * xx_plus_yy);
     805             : */
     806             : 
     807             : #ifdef HAVE_SSE2
     808      166017 : inline double ApproxADivByInvSqrtB(double a, double b)
     809             : {
     810      166017 :     __m128d regB = _mm_load_sd(&b);
     811      332034 :     __m128d regB_half = _mm_mul_sd(regB, _mm_set1_pd(0.5));
     812             :     // Compute rough approximation of 1 / sqrt(b) with _mm_rsqrt_ss
     813             :     regB =
     814      664068 :         _mm_cvtss_sd(regB, _mm_rsqrt_ss(_mm_cvtsd_ss(_mm_setzero_ps(), regB)));
     815             :     // And perform one step of Newton-Raphson approximation to improve it
     816             :     // approx_inv_sqrt_x = approx_inv_sqrt_x*(1.5 -
     817             :     //                            0.5*x*approx_inv_sqrt_x*approx_inv_sqrt_x);
     818      830085 :     regB = _mm_mul_sd(
     819             :         regB, _mm_sub_sd(_mm_set1_pd(1.5),
     820             :                          _mm_mul_sd(regB_half, _mm_mul_sd(regB, regB))));
     821             :     double dOut;
     822             :     _mm_store_sd(&dOut, regB);
     823      166017 :     return a * dOut;
     824             : }
     825             : #else
     826             : inline double ApproxADivByInvSqrtB(double a, double b)
     827             : {
     828             :     return a / sqrt(b);
     829             : }
     830             : #endif
     831             : 
     832       58564 : static double NormalizeAngle(double angle, double normalizer)
     833             : {
     834       58564 :     angle = std::fmod(angle, normalizer);
     835       58564 :     if (angle < 0)
     836       41924 :         angle = normalizer + angle;
     837             : 
     838       58564 :     return angle;
     839             : }
     840             : 
     841       29282 : static double DifferenceBetweenAngles(double angle1, double angle2,
     842             :                                       double normalizer)
     843             : {
     844             :     double diff =
     845       29282 :         NormalizeAngle(angle1, normalizer) - NormalizeAngle(angle2, normalizer);
     846       29282 :     diff = std::abs(diff);
     847       29282 :     if (diff > normalizer / 2)
     848        7060 :         diff = normalizer - diff;
     849       29282 :     return diff;
     850             : }
     851             : 
     852             : template <class T, GradientAlg alg>
     853       29282 : static float GDALHillshadeIgorAlg(const T *afWin, float /*fDstNoDataValue*/,
     854             :                                   void *pData)
     855             : {
     856       29282 :     GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
     857             : 
     858             :     double slopeDegrees;
     859             :     if (alg == GradientAlg::HORN)
     860             :     {
     861       14641 :         const double dx = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
     862       14641 :                            (afWin[2] + afWin[5] + afWin[5] + afWin[8])) *
     863       14641 :                           psData->inv_ewres;
     864             : 
     865       14641 :         const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
     866       14641 :                            (afWin[0] + afWin[1] + afWin[1] + afWin[2])) *
     867       14641 :                           psData->inv_nsres;
     868             : 
     869       14641 :         const double key = (dx * dx + dy * dy);
     870       14641 :         slopeDegrees = atan(sqrt(key) * psData->z_scaled) * kdfRadiansToDegrees;
     871             :     }
     872             :     else  // ZEVENBERGEN_THORNE
     873             :     {
     874       14641 :         const double dx = (afWin[3] - afWin[5]) * psData->inv_ewres;
     875       14641 :         const double dy = (afWin[7] - afWin[1]) * psData->inv_nsres;
     876       14641 :         const double key = dx * dx + dy * dy;
     877             : 
     878       14641 :         slopeDegrees = atan(sqrt(key) * psData->z_scaled) * kdfRadiansToDegrees;
     879             :     }
     880             : 
     881             :     double aspect;
     882             :     if (alg == GradientAlg::HORN)
     883             :     {
     884       14641 :         const double dx = ((afWin[2] + afWin[5] + afWin[5] + afWin[8]) -
     885       14641 :                            (afWin[0] + afWin[3] + afWin[3] + afWin[6]));
     886             : 
     887       14641 :         const double dy2 = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
     888       14641 :                             (afWin[0] + afWin[1] + afWin[1] + afWin[2]));
     889             : 
     890       14641 :         aspect = atan2(dy2, -dx);
     891             :     }
     892             :     else  // ZEVENBERGEN_THORNE
     893             :     {
     894       14641 :         const double dx = afWin[5] - afWin[3];
     895       14641 :         const double dy = afWin[7] - afWin[1];
     896       14641 :         aspect = atan2(dy, -dx);
     897             :     }
     898             : 
     899       29282 :     double slopeStrength = slopeDegrees / 90;
     900             : 
     901       29282 :     double aspectDiff = DifferenceBetweenAngles(
     902       29282 :         aspect, M_PI * 3 / 2 - psData->azRadians, M_PI * 2);
     903             : 
     904       29282 :     double aspectStrength = 1 - aspectDiff / M_PI;
     905             : 
     906       29282 :     double shadowness = 1.0 - slopeStrength * aspectStrength;
     907             : 
     908       29282 :     return static_cast<float>(255.0 * shadowness);
     909             : }
     910             : 
     911             : template <class T, GradientAlg alg>
     912       14161 : static float GDALHillshadeAlg(const T *afWin, float /*fDstNoDataValue*/,
     913             :                               void *pData)
     914             : {
     915       14161 :     GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
     916             : 
     917             :     // First Slope ...
     918             :     double x, y;
     919       14161 :     Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
     920             : 
     921       14161 :     const double xx_plus_yy = x * x + y * y;
     922             : 
     923             :     // ... then the shade value
     924             :     const double cang_mul_254 =
     925       28322 :         ApproxADivByInvSqrtB(psData->sin_altRadians_mul_254 -
     926       14161 :                                  (y * psData->cos_az_mul_cos_alt_mul_z_mul_254 -
     927       14161 :                                   x * psData->sin_az_mul_cos_alt_mul_z_mul_254),
     928       14161 :                              1 + psData->square_z * xx_plus_yy);
     929             : 
     930       14161 :     const double cang = cang_mul_254 <= 0.0 ? 1.0 : 1.0 + cang_mul_254;
     931             : 
     932       14161 :     return static_cast<float>(cang);
     933             : }
     934             : 
     935             : template <class T>
     936       88708 : static float GDALHillshadeAlg_same_res(const T *afWin,
     937             :                                        float /*fDstNoDataValue*/, void *pData)
     938             : {
     939       88708 :     GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
     940             : 
     941             :     // First Slope ...
     942             :     /*x = (afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
     943             :         (afWin[2] + afWin[5] + afWin[5] + afWin[8]);
     944             : 
     945             :     y = (afWin[0] + afWin[1] + afWin[1] + afWin[2]) -
     946             :         (afWin[6] + afWin[7] + afWin[7] + afWin[8]);*/
     947             : 
     948       88708 :     T accX = afWin[0] - afWin[8];
     949       88708 :     const T six_minus_two = afWin[6] - afWin[2];
     950       88708 :     T accY = accX;
     951       88708 :     const T three_minus_five = afWin[3] - afWin[5];
     952       88708 :     const T one_minus_seven = afWin[1] - afWin[7];
     953       88708 :     accX += three_minus_five;
     954       88708 :     accY += one_minus_seven;
     955       88708 :     accX += three_minus_five;
     956       88708 :     accY += one_minus_seven;
     957       88708 :     accX += six_minus_two;
     958       88708 :     accY -= six_minus_two;
     959       88708 :     const double x = accX;
     960       88708 :     const double y = accY;
     961             : 
     962       88708 :     const double xx_plus_yy = x * x + y * y;
     963             : 
     964             :     // ... then the shade value
     965      177416 :     const double cang_mul_254 = ApproxADivByInvSqrtB(
     966       88708 :         psData->sin_altRadians_mul_254 +
     967       88708 :             (x * psData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res +
     968       88708 :              y * psData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res),
     969       88708 :         1 + psData->square_z_mul_square_inv_res * xx_plus_yy);
     970             : 
     971       88708 :     const double cang = cang_mul_254 <= 0.0 ? 1.0 : 1.0 + cang_mul_254;
     972             : 
     973       88708 :     return static_cast<float>(cang);
     974             : }
     975             : 
     976             : #ifdef HAVE_16_SSE_REG
     977             : template <class T>
     978             : static int
     979         672 : GDALHillshadeAlg_same_res_multisample(const T *pafThreeLineWin, int nLine1Off,
     980             :                                       int nLine2Off, int nLine3Off, int nXSize,
     981             :                                       void *pData, float *pafOutputBuf)
     982             : {
     983             :     // Only valid for T == int
     984             : 
     985         672 :     GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
     986             :     const __m128d reg_fact_x =
     987         672 :         _mm_load1_pd(&(psData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res));
     988             :     const __m128d reg_fact_y =
     989         672 :         _mm_load1_pd(&(psData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res));
     990             :     const __m128d reg_constant_num =
     991         672 :         _mm_load1_pd(&(psData->sin_altRadians_mul_254));
     992             :     const __m128d reg_constant_denom =
     993        1344 :         _mm_load1_pd(&(psData->square_z_mul_square_inv_res));
     994         672 :     const __m128d reg_half = _mm_set1_pd(0.5);
     995         672 :     const __m128d reg_one = _mm_add_pd(reg_half, reg_half);
     996         672 :     const __m128 reg_one_float = _mm_set1_ps(1);
     997             : 
     998         672 :     int j = 1;  // Used after for.
     999       19180 :     for (; j < nXSize - 4; j += 4)
    1000             :     {
    1001       18508 :         const T *firstLine = pafThreeLineWin + nLine1Off + j - 1;
    1002       18508 :         const T *secondLine = pafThreeLineWin + nLine2Off + j - 1;
    1003       18508 :         const T *thirdLine = pafThreeLineWin + nLine3Off + j - 1;
    1004             : 
    1005             :         __m128i firstLine0 =
    1006       18508 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine));
    1007             :         __m128i firstLine1 =
    1008       18508 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine + 1));
    1009             :         __m128i firstLine2 =
    1010       37016 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine + 2));
    1011             :         __m128i thirdLine0 =
    1012       18508 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine));
    1013             :         __m128i thirdLine1 =
    1014       18508 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine + 1));
    1015             :         __m128i thirdLine2 =
    1016       37016 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine + 2));
    1017       18508 :         __m128i accX = _mm_sub_epi32(firstLine0, thirdLine2);
    1018       18508 :         const __m128i six_minus_two = _mm_sub_epi32(thirdLine0, firstLine2);
    1019       18508 :         __m128i accY = accX;
    1020       55524 :         const __m128i three_minus_five = _mm_sub_epi32(
    1021             :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(secondLine)),
    1022       18508 :             _mm_loadu_si128(reinterpret_cast<__m128i const *>(secondLine + 2)));
    1023       18508 :         const __m128i one_minus_seven = _mm_sub_epi32(firstLine1, thirdLine1);
    1024       18508 :         accX = _mm_add_epi32(accX, three_minus_five);
    1025       18508 :         accY = _mm_add_epi32(accY, one_minus_seven);
    1026       18508 :         accX = _mm_add_epi32(accX, three_minus_five);
    1027       18508 :         accY = _mm_add_epi32(accY, one_minus_seven);
    1028       18508 :         accX = _mm_add_epi32(accX, six_minus_two);
    1029       18508 :         accY = _mm_sub_epi32(accY, six_minus_two);
    1030             : 
    1031       18508 :         __m128d reg_x0 = _mm_cvtepi32_pd(accX);
    1032       37016 :         __m128d reg_x1 = _mm_cvtepi32_pd(_mm_srli_si128(accX, 8));
    1033       18508 :         __m128d reg_y0 = _mm_cvtepi32_pd(accY);
    1034       37016 :         __m128d reg_y1 = _mm_cvtepi32_pd(_mm_srli_si128(accY, 8));
    1035             :         __m128d reg_xx_plus_yy0 =
    1036       55524 :             _mm_add_pd(_mm_mul_pd(reg_x0, reg_x0), _mm_mul_pd(reg_y0, reg_y0));
    1037             :         __m128d reg_xx_plus_yy1 =
    1038       55524 :             _mm_add_pd(_mm_mul_pd(reg_x1, reg_x1), _mm_mul_pd(reg_y1, reg_y1));
    1039             : 
    1040       74032 :         __m128d reg_numerator0 = _mm_add_pd(
    1041             :             reg_constant_num, _mm_add_pd(_mm_mul_pd(reg_fact_x, reg_x0),
    1042             :                                          _mm_mul_pd(reg_fact_y, reg_y0)));
    1043       74032 :         __m128d reg_numerator1 = _mm_add_pd(
    1044             :             reg_constant_num, _mm_add_pd(_mm_mul_pd(reg_fact_x, reg_x1),
    1045             :                                          _mm_mul_pd(reg_fact_y, reg_y1)));
    1046       37016 :         __m128d reg_denominator0 = _mm_add_pd(
    1047             :             reg_one, _mm_mul_pd(reg_constant_denom, reg_xx_plus_yy0));
    1048       18508 :         __m128d reg_denominator1 = _mm_add_pd(
    1049             :             reg_one, _mm_mul_pd(reg_constant_denom, reg_xx_plus_yy1));
    1050             : 
    1051       18508 :         __m128d regB0 = reg_denominator0;
    1052       18508 :         __m128d regB1 = reg_denominator1;
    1053       18508 :         __m128d regB0_half = _mm_mul_pd(regB0, reg_half);
    1054       18508 :         __m128d regB1_half = _mm_mul_pd(regB1, reg_half);
    1055             :         // Compute rough approximation of 1 / sqrt(b) with _mm_rsqrt_ps
    1056       55524 :         regB0 = _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(regB0)));
    1057       55524 :         regB1 = _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(regB1)));
    1058             :         // And perform one step of Newton-Raphson approximation to improve it
    1059             :         // approx_inv_sqrt_x = approx_inv_sqrt_x*(1.5 -
    1060             :         //                            0.5*x*approx_inv_sqrt_x*approx_inv_sqrt_x);
    1061       18508 :         const __m128d reg_one_and_a_half = _mm_add_pd(reg_one, reg_half);
    1062       74032 :         regB0 = _mm_mul_pd(
    1063             :             regB0,
    1064             :             _mm_sub_pd(reg_one_and_a_half,
    1065             :                        _mm_mul_pd(regB0_half, _mm_mul_pd(regB0, regB0))));
    1066       74032 :         regB1 = _mm_mul_pd(
    1067             :             regB1,
    1068             :             _mm_sub_pd(reg_one_and_a_half,
    1069             :                        _mm_mul_pd(regB1_half, _mm_mul_pd(regB1, regB1))));
    1070       18508 :         reg_numerator0 = _mm_mul_pd(reg_numerator0, regB0);
    1071       18508 :         reg_numerator1 = _mm_mul_pd(reg_numerator1, regB1);
    1072             : 
    1073      111048 :         __m128 res = _mm_castsi128_ps(
    1074             :             _mm_unpacklo_epi64(_mm_castps_si128(_mm_cvtpd_ps(reg_numerator0)),
    1075             :                                _mm_castps_si128(_mm_cvtpd_ps(reg_numerator1))));
    1076       18508 :         res = _mm_add_ps(res, reg_one_float);
    1077       18508 :         res = _mm_max_ps(res, reg_one_float);
    1078             : 
    1079       18508 :         _mm_storeu_ps(pafOutputBuf + j, res);
    1080             :     }
    1081         672 :     return j;
    1082             : }
    1083             : #endif
    1084             : 
    1085             : static const double INV_SQUARE_OF_HALF_PI = 1.0 / ((M_PI * M_PI) / 4);
    1086             : 
    1087             : template <class T, GradientAlg alg>
    1088       42483 : static float GDALHillshadeCombinedAlg(const T *afWin, float /*fDstNoDataValue*/,
    1089             :                                       void *pData)
    1090             : {
    1091       42483 :     GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
    1092             : 
    1093             :     // First Slope ...
    1094             :     double x, y;
    1095       42483 :     Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
    1096             : 
    1097       42483 :     const double xx_plus_yy = x * x + y * y;
    1098             : 
    1099       42483 :     const double slope = xx_plus_yy * psData->square_z;
    1100             : 
    1101             :     // ... then the shade value
    1102       84966 :     double cang = acos(ApproxADivByInvSqrtB(
    1103       42483 :         psData->sin_altRadians - (y * psData->cos_az_mul_cos_alt_mul_z -
    1104       42483 :                                   x * psData->sin_az_mul_cos_alt_mul_z),
    1105             :         1 + slope));
    1106             : 
    1107             :     // combined shading
    1108       42483 :     cang = 1 - cang * atan(sqrt(slope)) * INV_SQUARE_OF_HALF_PI;
    1109             : 
    1110       42483 :     const float fcang =
    1111       42483 :         cang <= 0.0 ? 1.0f : static_cast<float>(1.0 + (254.0 * cang));
    1112             : 
    1113       42483 :     return fcang;
    1114             : }
    1115             : 
    1116          31 : static void *GDALCreateHillshadeData(double *adfGeoTransform, double z,
    1117             :                                      double scale, double alt, double az,
    1118             :                                      GradientAlg eAlg)
    1119             : {
    1120             :     GDALHillshadeAlgData *pData = static_cast<GDALHillshadeAlgData *>(
    1121          31 :         CPLCalloc(1, sizeof(GDALHillshadeAlgData)));
    1122             : 
    1123          31 :     pData->inv_nsres = 1.0 / adfGeoTransform[5];
    1124          31 :     pData->inv_ewres = 1.0 / adfGeoTransform[1];
    1125          31 :     pData->sin_altRadians = sin(alt * kdfDegreesToRadians);
    1126          31 :     pData->azRadians = az * kdfDegreesToRadians;
    1127          31 :     pData->z_scaled =
    1128          31 :         z / ((eAlg == GradientAlg::ZEVENBERGEN_THORNE ? 2 : 8) * scale);
    1129          31 :     pData->cos_alt_mul_z = cos(alt * kdfDegreesToRadians) * pData->z_scaled;
    1130          31 :     pData->cos_az_mul_cos_alt_mul_z =
    1131          31 :         cos(pData->azRadians) * pData->cos_alt_mul_z;
    1132          31 :     pData->sin_az_mul_cos_alt_mul_z =
    1133          31 :         sin(pData->azRadians) * pData->cos_alt_mul_z;
    1134          31 :     pData->square_z = pData->z_scaled * pData->z_scaled;
    1135             : 
    1136          31 :     pData->sin_altRadians_mul_254 = 254.0 * pData->sin_altRadians;
    1137          31 :     pData->cos_az_mul_cos_alt_mul_z_mul_254 =
    1138          31 :         254.0 * pData->cos_az_mul_cos_alt_mul_z;
    1139          31 :     pData->sin_az_mul_cos_alt_mul_z_mul_254 =
    1140          31 :         254.0 * pData->sin_az_mul_cos_alt_mul_z;
    1141             : 
    1142          31 :     if (adfGeoTransform[1] == -adfGeoTransform[5])
    1143             :     {
    1144          31 :         pData->square_z_mul_square_inv_res =
    1145          31 :             pData->square_z * pData->inv_ewres * pData->inv_ewres;
    1146          31 :         pData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res =
    1147          31 :             pData->cos_az_mul_cos_alt_mul_z_mul_254 * -pData->inv_ewres;
    1148          31 :         pData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res =
    1149          31 :             pData->sin_az_mul_cos_alt_mul_z_mul_254 * pData->inv_ewres;
    1150             :     }
    1151             : 
    1152          31 :     return pData;
    1153             : }
    1154             : 
    1155             : /************************************************************************/
    1156             : /*                   GDALHillshadeMultiDirectional()                    */
    1157             : /************************************************************************/
    1158             : 
    1159             : typedef struct
    1160             : {
    1161             :     double inv_nsres;
    1162             :     double inv_ewres;
    1163             :     double square_z;
    1164             :     double sin_altRadians_mul_127;
    1165             :     double sin_altRadians_mul_254;
    1166             : 
    1167             :     double cos_alt_mul_z_mul_127;
    1168             :     double cos225_az_mul_cos_alt_mul_z_mul_127;
    1169             : 
    1170             : } GDALHillshadeMultiDirectionalAlgData;
    1171             : 
    1172             : template <class T, GradientAlg alg>
    1173       29282 : static float GDALHillshadeMultiDirectionalAlg(const T *afWin,
    1174             :                                               float /*fDstNoDataValue*/,
    1175             :                                               void *pData)
    1176             : {
    1177       29282 :     const GDALHillshadeMultiDirectionalAlgData *psData =
    1178             :         static_cast<const GDALHillshadeMultiDirectionalAlgData *>(pData);
    1179             : 
    1180             :     // First Slope ...
    1181             :     double x, y;
    1182       29282 :     Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
    1183             : 
    1184             :     // See http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf
    1185             :     // W225 = sin^2(aspect - 225) = 0.5 * (1 - 2 * sin(aspect) * cos(aspect))
    1186             :     // W270 = sin^2(aspect - 270) = cos^2(aspect)
    1187             :     // W315 = sin^2(aspect - 315) = 0.5 * (1 + 2 * sin(aspect) * cos(aspect))
    1188             :     // W360 = sin^2(aspect - 360) = sin^2(aspect)
    1189             :     // hillshade=  0.5 * (W225 * hillshade(az=225) +
    1190             :     //                    W270 * hillshade(az=270) +
    1191             :     //                    W315 * hillshade(az=315) +
    1192             :     //                    W360 * hillshade(az=360))
    1193             : 
    1194       29282 :     const double xx = x * x;
    1195       29282 :     const double yy = y * y;
    1196       29282 :     const double xx_plus_yy = xx + yy;
    1197       29282 :     if (xx_plus_yy == 0.0)
    1198        8617 :         return static_cast<float>(1.0 + psData->sin_altRadians_mul_254);
    1199             : 
    1200             :     // ... then the shade value from different azimuth
    1201       20665 :     double val225_mul_127 =
    1202       20665 :         psData->sin_altRadians_mul_127 +
    1203       20665 :         (x - y) * psData->cos225_az_mul_cos_alt_mul_z_mul_127;
    1204       20665 :     val225_mul_127 = (val225_mul_127 <= 0.0) ? 0.0 : val225_mul_127;
    1205       20665 :     double val270_mul_127 =
    1206       20665 :         psData->sin_altRadians_mul_127 - x * psData->cos_alt_mul_z_mul_127;
    1207       20665 :     val270_mul_127 = (val270_mul_127 <= 0.0) ? 0.0 : val270_mul_127;
    1208       20665 :     double val315_mul_127 =
    1209       20665 :         psData->sin_altRadians_mul_127 +
    1210       20665 :         (x + y) * psData->cos225_az_mul_cos_alt_mul_z_mul_127;
    1211       20665 :     val315_mul_127 = (val315_mul_127 <= 0.0) ? 0.0 : val315_mul_127;
    1212       20665 :     double val360_mul_127 =
    1213       20665 :         psData->sin_altRadians_mul_127 - y * psData->cos_alt_mul_z_mul_127;
    1214       20665 :     val360_mul_127 = (val360_mul_127 <= 0.0) ? 0.0 : val360_mul_127;
    1215             : 
    1216             :     // ... then the weighted shading
    1217       20665 :     const double weight_225 = 0.5 * xx_plus_yy - x * y;
    1218       20665 :     const double weight_270 = xx;
    1219       20665 :     const double weight_315 = xx_plus_yy - weight_225;
    1220       20665 :     const double weight_360 = yy;
    1221       41330 :     const double cang_mul_127 = ApproxADivByInvSqrtB(
    1222       20665 :         (weight_225 * val225_mul_127 + weight_270 * val270_mul_127 +
    1223       20665 :          weight_315 * val315_mul_127 + weight_360 * val360_mul_127) /
    1224             :             xx_plus_yy,
    1225       20665 :         1 + psData->square_z * xx_plus_yy);
    1226             : 
    1227       20665 :     const double cang = 1.0 + cang_mul_127;
    1228             : 
    1229       20665 :     return static_cast<float>(cang);
    1230             : }
    1231             : 
    1232           2 : static void *GDALCreateHillshadeMultiDirectionalData(double *adfGeoTransform,
    1233             :                                                      double z, double scale,
    1234             :                                                      double alt,
    1235             :                                                      GradientAlg eAlg)
    1236             : {
    1237             :     GDALHillshadeMultiDirectionalAlgData *pData =
    1238             :         static_cast<GDALHillshadeMultiDirectionalAlgData *>(
    1239           2 :             CPLCalloc(1, sizeof(GDALHillshadeMultiDirectionalAlgData)));
    1240             : 
    1241           2 :     pData->inv_nsres = 1.0 / adfGeoTransform[5];
    1242           2 :     pData->inv_ewres = 1.0 / adfGeoTransform[1];
    1243           2 :     const double z_scaled =
    1244           2 :         z / ((eAlg == GradientAlg::ZEVENBERGEN_THORNE ? 2 : 8) * scale);
    1245           2 :     const double cos_alt_mul_z = cos(alt * kdfDegreesToRadians) * z_scaled;
    1246           2 :     pData->square_z = z_scaled * z_scaled;
    1247             : 
    1248           2 :     pData->sin_altRadians_mul_127 = 127.0 * sin(alt * kdfDegreesToRadians);
    1249           2 :     pData->sin_altRadians_mul_254 = 254.0 * sin(alt * kdfDegreesToRadians);
    1250           2 :     pData->cos_alt_mul_z_mul_127 = 127.0 * cos_alt_mul_z;
    1251           2 :     pData->cos225_az_mul_cos_alt_mul_z_mul_127 =
    1252           2 :         127.0 * cos(225 * kdfDegreesToRadians) * cos_alt_mul_z;
    1253             : 
    1254           2 :     return pData;
    1255             : }
    1256             : 
    1257             : /************************************************************************/
    1258             : /*                         GDALSlope()                                  */
    1259             : /************************************************************************/
    1260             : 
    1261             : typedef struct
    1262             : {
    1263             :     double nsres;
    1264             :     double ewres;
    1265             :     double scale;
    1266             :     int slopeFormat;
    1267             : } GDALSlopeAlgData;
    1268             : 
    1269             : template <class T>
    1270       14161 : static float GDALSlopeHornAlg(const T *afWin, float /*fDstNoDataValue*/,
    1271             :                               void *pData)
    1272             : {
    1273       14161 :     const GDALSlopeAlgData *psData =
    1274             :         static_cast<const GDALSlopeAlgData *>(pData);
    1275             : 
    1276       14161 :     const double dx = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
    1277       14161 :                        (afWin[2] + afWin[5] + afWin[5] + afWin[8])) /
    1278       14161 :                       psData->ewres;
    1279             : 
    1280       14161 :     const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
    1281       14161 :                        (afWin[0] + afWin[1] + afWin[1] + afWin[2])) /
    1282       14161 :                       psData->nsres;
    1283             : 
    1284       14161 :     const double key = (dx * dx + dy * dy);
    1285             : 
    1286       14161 :     if (psData->slopeFormat == 1)
    1287       14161 :         return static_cast<float>(atan(sqrt(key) / (8 * psData->scale)) *
    1288       14161 :                                   kdfRadiansToDegrees);
    1289             : 
    1290           0 :     return static_cast<float>(100 * (sqrt(key) / (8 * psData->scale)));
    1291             : }
    1292             : 
    1293             : template <class T>
    1294       14161 : static float GDALSlopeZevenbergenThorneAlg(const T *afWin,
    1295             :                                            float /*fDstNoDataValue*/,
    1296             :                                            void *pData)
    1297             : {
    1298       14161 :     const GDALSlopeAlgData *psData =
    1299             :         static_cast<const GDALSlopeAlgData *>(pData);
    1300             : 
    1301       14161 :     const double dx = (afWin[3] - afWin[5]) / psData->ewres;
    1302       14161 :     const double dy = (afWin[7] - afWin[1]) / psData->nsres;
    1303       14161 :     const double key = dx * dx + dy * dy;
    1304             : 
    1305       14161 :     if (psData->slopeFormat == 1)
    1306       14161 :         return static_cast<float>(atan(sqrt(key) / (2 * psData->scale)) *
    1307       14161 :                                   kdfRadiansToDegrees);
    1308             : 
    1309           0 :     return static_cast<float>(100 * (sqrt(key) / (2 * psData->scale)));
    1310             : }
    1311             : 
    1312           2 : static void *GDALCreateSlopeData(double *adfGeoTransform, double scale,
    1313             :                                  int slopeFormat)
    1314             : {
    1315             :     GDALSlopeAlgData *pData =
    1316           2 :         static_cast<GDALSlopeAlgData *>(CPLMalloc(sizeof(GDALSlopeAlgData)));
    1317             : 
    1318           2 :     pData->nsres = adfGeoTransform[5];
    1319           2 :     pData->ewres = adfGeoTransform[1];
    1320           2 :     pData->scale = scale;
    1321           2 :     pData->slopeFormat = slopeFormat;
    1322           2 :     return pData;
    1323             : }
    1324             : 
    1325             : /************************************************************************/
    1326             : /*                         GDALAspect()                                 */
    1327             : /************************************************************************/
    1328             : 
    1329             : typedef struct
    1330             : {
    1331             :     bool bAngleAsAzimuth;
    1332             : } GDALAspectAlgData;
    1333             : 
    1334             : template <class T>
    1335       14161 : static float GDALAspectAlg(const T *afWin, float fDstNoDataValue, void *pData)
    1336             : {
    1337       14161 :     const GDALAspectAlgData *psData =
    1338             :         static_cast<const GDALAspectAlgData *>(pData);
    1339             : 
    1340       14161 :     const double dx = ((afWin[2] + afWin[5] + afWin[5] + afWin[8]) -
    1341       14161 :                        (afWin[0] + afWin[3] + afWin[3] + afWin[6]));
    1342             : 
    1343       14161 :     const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
    1344       14161 :                        (afWin[0] + afWin[1] + afWin[1] + afWin[2]));
    1345             : 
    1346       14161 :     float aspect = static_cast<float>(atan2(dy, -dx) / kdfDegreesToRadians);
    1347             : 
    1348       14161 :     if (dx == 0 && dy == 0)
    1349             :     {
    1350             :         /* Flat area */
    1351        4183 :         aspect = fDstNoDataValue;
    1352             :     }
    1353        9978 :     else if (psData->bAngleAsAzimuth)
    1354             :     {
    1355        9978 :         if (aspect > 90.0f)
    1356        1243 :             aspect = 450.0f - aspect;
    1357             :         else
    1358        8735 :             aspect = 90.0f - aspect;
    1359             :     }
    1360             :     else
    1361             :     {
    1362           0 :         if (aspect < 0)
    1363           0 :             aspect += 360.0f;
    1364             :     }
    1365             : 
    1366       14161 :     if (aspect == 360.0f)
    1367           0 :         aspect = 0.0;
    1368             : 
    1369       14161 :     return aspect;
    1370             : }
    1371             : 
    1372             : template <class T>
    1373       14161 : static float GDALAspectZevenbergenThorneAlg(const T *afWin,
    1374             :                                             float fDstNoDataValue, void *pData)
    1375             : {
    1376       14161 :     const GDALAspectAlgData *psData =
    1377             :         static_cast<const GDALAspectAlgData *>(pData);
    1378             : 
    1379       14161 :     const double dx = afWin[5] - afWin[3];
    1380       14161 :     const double dy = afWin[7] - afWin[1];
    1381       14161 :     float aspect = static_cast<float>(atan2(dy, -dx) / kdfDegreesToRadians);
    1382       14161 :     if (dx == 0 && dy == 0)
    1383             :     {
    1384             :         /* Flat area */
    1385        4302 :         aspect = fDstNoDataValue;
    1386             :     }
    1387        9859 :     else if (psData->bAngleAsAzimuth)
    1388             :     {
    1389        9859 :         if (aspect > 90.0f)
    1390        1233 :             aspect = 450.0f - aspect;
    1391             :         else
    1392        8626 :             aspect = 90.0f - aspect;
    1393             :     }
    1394             :     else
    1395             :     {
    1396           0 :         if (aspect < 0)
    1397           0 :             aspect += 360.0f;
    1398             :     }
    1399             : 
    1400       14161 :     if (aspect == 360.0f)
    1401           0 :         aspect = 0.0;
    1402             : 
    1403       14161 :     return aspect;
    1404             : }
    1405             : 
    1406           2 : static void *GDALCreateAspectData(bool bAngleAsAzimuth)
    1407             : {
    1408             :     GDALAspectAlgData *pData =
    1409           2 :         static_cast<GDALAspectAlgData *>(CPLMalloc(sizeof(GDALAspectAlgData)));
    1410             : 
    1411           2 :     pData->bAngleAsAzimuth = bAngleAsAzimuth;
    1412           2 :     return pData;
    1413             : }
    1414             : 
    1415             : /************************************************************************/
    1416             : /*                      GDALColorRelief()                               */
    1417             : /************************************************************************/
    1418             : 
    1419             : typedef struct
    1420             : {
    1421             :     double dfVal;
    1422             :     int nR;
    1423             :     int nG;
    1424             :     int nB;
    1425             :     int nA;
    1426             : } ColorAssociation;
    1427             : 
    1428         193 : static int GDALColorReliefSortColors(const ColorAssociation &pA,
    1429             :                                      const ColorAssociation &pB)
    1430             : {
    1431             :     /* Sort NaN in first position */
    1432         193 :     return (CPLIsNan(pA.dfVal) && !CPLIsNan(pB.dfVal)) || pA.dfVal < pB.dfVal;
    1433             : }
    1434             : 
    1435             : static void
    1436          18 : GDALColorReliefProcessColors(ColorAssociation **ppasColorAssociation,
    1437             :                              int *pnColorAssociation, int bSrcHasNoData,
    1438             :                              double dfSrcNoDataValue)
    1439             : {
    1440          18 :     ColorAssociation *pasColorAssociation = *ppasColorAssociation;
    1441          18 :     int nColorAssociation = *pnColorAssociation;
    1442             : 
    1443          18 :     std::stable_sort(pasColorAssociation,
    1444          18 :                      pasColorAssociation + nColorAssociation,
    1445             :                      GDALColorReliefSortColors);
    1446             : 
    1447          18 :     ColorAssociation *pPrevious =
    1448          18 :         (nColorAssociation > 0) ? &pasColorAssociation[0] : nullptr;
    1449          18 :     int nAdded = 0;
    1450          18 :     int nRepeatedEntryIndex = 0;
    1451         111 :     for (int i = 1; i < nColorAssociation; ++i)
    1452             :     {
    1453          93 :         ColorAssociation *pCurrent = &pasColorAssociation[i];
    1454             : 
    1455             :         // NaN comparison is always false, so it handles itself
    1456          93 :         if (bSrcHasNoData && pCurrent->dfVal == dfSrcNoDataValue)
    1457             :         {
    1458             :             // Check if there is enough distance between the nodata value and
    1459             :             // its predecessor.
    1460             :             const double dfNewValue =
    1461           2 :                 pCurrent->dfVal - std::abs(pCurrent->dfVal) * DBL_EPSILON;
    1462           2 :             if (dfNewValue > pPrevious->dfVal)
    1463             :             {
    1464             :                 // add one just below the nodata value
    1465           2 :                 ++nAdded;
    1466           2 :                 ColorAssociation sPrevious = *pPrevious;
    1467             :                 pasColorAssociation =
    1468           4 :                     static_cast<ColorAssociation *>(CPLRealloc(
    1469           2 :                         pasColorAssociation, (nColorAssociation + nAdded) *
    1470             :                                                  sizeof(ColorAssociation)));
    1471           2 :                 pCurrent = &pasColorAssociation[i];
    1472           2 :                 pasColorAssociation[nColorAssociation + nAdded - 1] = sPrevious;
    1473           2 :                 pasColorAssociation[nColorAssociation + nAdded - 1].dfVal =
    1474             :                     dfNewValue;
    1475           2 :             }
    1476             :         }
    1477          91 :         else if (bSrcHasNoData && pPrevious->dfVal == dfSrcNoDataValue)
    1478             :         {
    1479             :             // Check if there is enough distance between the nodata value and
    1480             :             // its successor.
    1481             :             const double dfNewValue =
    1482           3 :                 pPrevious->dfVal + std::abs(pPrevious->dfVal) * DBL_EPSILON;
    1483           3 :             if (dfNewValue < pCurrent->dfVal)
    1484             :             {
    1485             :                 // add one just above the nodata value
    1486           3 :                 ++nAdded;
    1487           3 :                 ColorAssociation sCurrent = *pCurrent;
    1488             :                 pasColorAssociation =
    1489           6 :                     static_cast<ColorAssociation *>(CPLRealloc(
    1490           3 :                         pasColorAssociation, (nColorAssociation + nAdded) *
    1491             :                                                  sizeof(ColorAssociation)));
    1492           3 :                 pCurrent = &pasColorAssociation[i];
    1493           3 :                 pasColorAssociation[nColorAssociation + nAdded - 1] = sCurrent;
    1494           3 :                 pasColorAssociation[nColorAssociation + nAdded - 1].dfVal =
    1495             :                     dfNewValue;
    1496           3 :             }
    1497             :         }
    1498          88 :         else if (nRepeatedEntryIndex == 0 &&
    1499          86 :                  pCurrent->dfVal == pPrevious->dfVal)
    1500             :         {
    1501             :             // second of a series of equivalent entries
    1502           2 :             nRepeatedEntryIndex = i;
    1503             :         }
    1504          86 :         else if (nRepeatedEntryIndex != 0 &&
    1505           2 :                  pCurrent->dfVal != pPrevious->dfVal)
    1506             :         {
    1507             :             // Get the distance between the predecessor and successor of the
    1508             :             // equivalent entries.
    1509           2 :             double dfTotalDist = 0.0;
    1510           2 :             double dfLeftDist = 0.0;
    1511           2 :             if (nRepeatedEntryIndex >= 2)
    1512             :             {
    1513           2 :                 ColorAssociation *pLower =
    1514           2 :                     &pasColorAssociation[nRepeatedEntryIndex - 2];
    1515           2 :                 dfTotalDist = pCurrent->dfVal - pLower->dfVal;
    1516           2 :                 dfLeftDist = pPrevious->dfVal - pLower->dfVal;
    1517             :             }
    1518             :             else
    1519             :             {
    1520           0 :                 dfTotalDist = pCurrent->dfVal - pPrevious->dfVal;
    1521             :             }
    1522             : 
    1523             :             // check if this distance is enough
    1524           2 :             const int nEquivalentCount = i - nRepeatedEntryIndex + 1;
    1525           2 :             if (dfTotalDist >
    1526           2 :                 std::abs(pPrevious->dfVal) * nEquivalentCount * DBL_EPSILON)
    1527             :             {
    1528             :                 // balance the alterations
    1529           2 :                 double dfMultiplier =
    1530           2 :                     0.5 - nEquivalentCount * dfLeftDist / dfTotalDist;
    1531           6 :                 for (int j = nRepeatedEntryIndex - 1; j < i; ++j)
    1532             :                 {
    1533           4 :                     pasColorAssociation[j].dfVal +=
    1534           4 :                         (std::abs(pPrevious->dfVal) * dfMultiplier) *
    1535             :                         DBL_EPSILON;
    1536           4 :                     dfMultiplier += 1.0;
    1537             :                 }
    1538             :             }
    1539             :             else
    1540             :             {
    1541             :                 // Fallback to the old behavior: keep equivalent entries as
    1542             :                 // they are.
    1543             :             }
    1544             : 
    1545           2 :             nRepeatedEntryIndex = 0;
    1546             :         }
    1547          93 :         pPrevious = pCurrent;
    1548             :     }
    1549             : 
    1550          18 :     if (nAdded)
    1551             :     {
    1552           3 :         std::stable_sort(pasColorAssociation,
    1553           3 :                          pasColorAssociation + nColorAssociation + nAdded,
    1554             :                          GDALColorReliefSortColors);
    1555           3 :         *ppasColorAssociation = pasColorAssociation;
    1556           3 :         *pnColorAssociation = nColorAssociation + nAdded;
    1557             :     }
    1558          18 : }
    1559             : 
    1560      234522 : static bool GDALColorReliefGetRGBA(ColorAssociation *pasColorAssociation,
    1561             :                                    int nColorAssociation, double dfVal,
    1562             :                                    ColorSelectionMode eColorSelectionMode,
    1563             :                                    int *pnR, int *pnG, int *pnB, int *pnA)
    1564             : {
    1565      234522 :     int lower = 0;
    1566             : 
    1567             :     // Special case for NaN
    1568      234522 :     if (CPLIsNan(pasColorAssociation[0].dfVal))
    1569             :     {
    1570           4 :         if (CPLIsNan(dfVal))
    1571             :         {
    1572           1 :             *pnR = pasColorAssociation[0].nR;
    1573           1 :             *pnG = pasColorAssociation[0].nG;
    1574           1 :             *pnB = pasColorAssociation[0].nB;
    1575           1 :             *pnA = pasColorAssociation[0].nA;
    1576           1 :             return true;
    1577             :         }
    1578             :         else
    1579             :         {
    1580           3 :             lower = 1;
    1581             :         }
    1582             :     }
    1583             : 
    1584             :     // Find the index of the first element in the LUT input array that
    1585             :     // is not smaller than the dfVal value.
    1586      234521 :     int i = 0;
    1587      234521 :     int upper = nColorAssociation - 1;
    1588             :     while (true)
    1589             :     {
    1590      759763 :         const int mid = (lower + upper) / 2;
    1591      759763 :         if (upper - lower <= 1)
    1592             :         {
    1593      234521 :             if (dfVal <= pasColorAssociation[lower].dfVal)
    1594           6 :                 i = lower;
    1595      234515 :             else if (dfVal <= pasColorAssociation[upper].dfVal)
    1596      234261 :                 i = upper;
    1597             :             else
    1598         254 :                 i = upper + 1;
    1599      234521 :             break;
    1600             :         }
    1601      525242 :         else if (pasColorAssociation[mid].dfVal >= dfVal)
    1602             :         {
    1603      310198 :             upper = mid;
    1604             :         }
    1605             :         else
    1606             :         {
    1607      215044 :             lower = mid;
    1608             :         }
    1609      525242 :     }
    1610             : 
    1611      234521 :     if (i == 0)
    1612             :     {
    1613           3 :         if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY &&
    1614           0 :             pasColorAssociation[0].dfVal != dfVal)
    1615             :         {
    1616           0 :             *pnR = 0;
    1617           0 :             *pnG = 0;
    1618           0 :             *pnB = 0;
    1619           0 :             *pnA = 0;
    1620           0 :             return false;
    1621             :         }
    1622             :         else
    1623             :         {
    1624           3 :             *pnR = pasColorAssociation[0].nR;
    1625           3 :             *pnG = pasColorAssociation[0].nG;
    1626           3 :             *pnB = pasColorAssociation[0].nB;
    1627           3 :             *pnA = pasColorAssociation[0].nA;
    1628           3 :             return true;
    1629             :         }
    1630             :     }
    1631      234518 :     else if (i == nColorAssociation)
    1632             :     {
    1633         254 :         if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY &&
    1634           0 :             pasColorAssociation[i - 1].dfVal != dfVal)
    1635             :         {
    1636           0 :             *pnR = 0;
    1637           0 :             *pnG = 0;
    1638           0 :             *pnB = 0;
    1639           0 :             *pnA = 0;
    1640           0 :             return false;
    1641             :         }
    1642             :         else
    1643             :         {
    1644         254 :             *pnR = pasColorAssociation[i - 1].nR;
    1645         254 :             *pnG = pasColorAssociation[i - 1].nG;
    1646         254 :             *pnB = pasColorAssociation[i - 1].nB;
    1647         254 :             *pnA = pasColorAssociation[i - 1].nA;
    1648         254 :             return true;
    1649             :         }
    1650             :     }
    1651             :     else
    1652             :     {
    1653      234264 :         if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY &&
    1654       14641 :             pasColorAssociation[i - 1].dfVal != dfVal)
    1655             :         {
    1656       14641 :             *pnR = 0;
    1657       14641 :             *pnG = 0;
    1658       14641 :             *pnB = 0;
    1659       14641 :             *pnA = 0;
    1660       14641 :             return false;
    1661             :         }
    1662             : 
    1663      219623 :         if (eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY &&
    1664       29282 :             pasColorAssociation[i - 1].dfVal != dfVal)
    1665             :         {
    1666       29282 :             int index = i;
    1667       29282 :             if (dfVal - pasColorAssociation[i - 1].dfVal <
    1668       29282 :                 pasColorAssociation[i].dfVal - dfVal)
    1669             :             {
    1670       13432 :                 --index;
    1671             :             }
    1672             : 
    1673       29282 :             *pnR = pasColorAssociation[index].nR;
    1674       29282 :             *pnG = pasColorAssociation[index].nG;
    1675       29282 :             *pnB = pasColorAssociation[index].nB;
    1676       29282 :             *pnA = pasColorAssociation[index].nA;
    1677       29282 :             return true;
    1678             :         }
    1679             : 
    1680      190341 :         if (pasColorAssociation[i - 1].dfVal == dfVal)
    1681             :         {
    1682           0 :             *pnR = pasColorAssociation[i - 1].nR;
    1683           0 :             *pnG = pasColorAssociation[i - 1].nG;
    1684           0 :             *pnB = pasColorAssociation[i - 1].nB;
    1685           0 :             *pnA = pasColorAssociation[i - 1].nA;
    1686           0 :             return true;
    1687             :         }
    1688             : 
    1689      190341 :         if (CPLIsNan(pasColorAssociation[i - 1].dfVal))
    1690             :         {
    1691           3 :             *pnR = pasColorAssociation[i].nR;
    1692           3 :             *pnG = pasColorAssociation[i].nG;
    1693           3 :             *pnB = pasColorAssociation[i].nB;
    1694           3 :             *pnA = pasColorAssociation[i].nA;
    1695           3 :             return true;
    1696             :         }
    1697             : 
    1698      190338 :         const double dfRatio =
    1699      190338 :             (dfVal - pasColorAssociation[i - 1].dfVal) /
    1700      190338 :             (pasColorAssociation[i].dfVal - pasColorAssociation[i - 1].dfVal);
    1701      190338 :         *pnR = static_cast<int>(0.45 + pasColorAssociation[i - 1].nR +
    1702      190338 :                                 dfRatio * (pasColorAssociation[i].nR -
    1703      190338 :                                            pasColorAssociation[i - 1].nR));
    1704      190338 :         if (*pnR < 0)
    1705           0 :             *pnR = 0;
    1706      190338 :         else if (*pnR > 255)
    1707           0 :             *pnR = 255;
    1708      190338 :         *pnG = static_cast<int>(0.45 + pasColorAssociation[i - 1].nG +
    1709      190338 :                                 dfRatio * (pasColorAssociation[i].nG -
    1710      190338 :                                            pasColorAssociation[i - 1].nG));
    1711      190338 :         if (*pnG < 0)
    1712           0 :             *pnG = 0;
    1713      190338 :         else if (*pnG > 255)
    1714           0 :             *pnG = 255;
    1715      190338 :         *pnB = static_cast<int>(0.45 + pasColorAssociation[i - 1].nB +
    1716      190338 :                                 dfRatio * (pasColorAssociation[i].nB -
    1717      190338 :                                            pasColorAssociation[i - 1].nB));
    1718      190338 :         if (*pnB < 0)
    1719           0 :             *pnB = 0;
    1720      190338 :         else if (*pnB > 255)
    1721           0 :             *pnB = 255;
    1722      190338 :         *pnA = static_cast<int>(0.45 + pasColorAssociation[i - 1].nA +
    1723      190338 :                                 dfRatio * (pasColorAssociation[i].nA -
    1724      190338 :                                            pasColorAssociation[i - 1].nA));
    1725      190338 :         if (*pnA < 0)
    1726           0 :             *pnA = 0;
    1727      190338 :         else if (*pnA > 255)
    1728           0 :             *pnA = 255;
    1729             : 
    1730      190338 :         return true;
    1731             :     }
    1732             : }
    1733             : 
    1734             : /* dfPct : percentage between 0 and 1 */
    1735           0 : static double GDALColorReliefGetAbsoluteValFromPct(GDALRasterBandH hSrcBand,
    1736             :                                                    double dfPct)
    1737             : {
    1738           0 :     int bSuccessMin = FALSE;
    1739           0 :     int bSuccessMax = FALSE;
    1740           0 :     double dfMin = GDALGetRasterMinimum(hSrcBand, &bSuccessMin);
    1741           0 :     double dfMax = GDALGetRasterMaximum(hSrcBand, &bSuccessMax);
    1742           0 :     if (!bSuccessMin || !bSuccessMax)
    1743             :     {
    1744           0 :         double dfMean = 0.0;
    1745           0 :         double dfStdDev = 0.0;
    1746           0 :         fprintf(stderr, "Computing source raster statistics...\n");
    1747           0 :         GDALComputeRasterStatistics(hSrcBand, FALSE, &dfMin, &dfMax, &dfMean,
    1748             :                                     &dfStdDev, nullptr, nullptr);
    1749             :     }
    1750           0 :     return dfMin + dfPct * (dfMax - dfMin);
    1751             : }
    1752             : 
    1753             : typedef struct
    1754             : {
    1755             :     const char *name;
    1756             :     float r, g, b;
    1757             : } NamedColor;
    1758             : 
    1759             : static const NamedColor namedColors[] = {
    1760             :     {"white", 1.00, 1.00, 1.00},   {"black", 0.00, 0.00, 0.00},
    1761             :     {"red", 1.00, 0.00, 0.00},     {"green", 0.00, 1.00, 0.00},
    1762             :     {"blue", 0.00, 0.00, 1.00},    {"yellow", 1.00, 1.00, 0.00},
    1763             :     {"magenta", 1.00, 0.00, 1.00}, {"cyan", 0.00, 1.00, 1.00},
    1764             :     {"aqua", 0.00, 0.75, 0.75},    {"grey", 0.75, 0.75, 0.75},
    1765             :     {"gray", 0.75, 0.75, 0.75},    {"orange", 1.00, 0.50, 0.00},
    1766             :     {"brown", 0.75, 0.50, 0.25},   {"purple", 0.50, 0.00, 1.00},
    1767             :     {"violet", 0.50, 0.00, 1.00},  {"indigo", 0.00, 0.50, 1.00},
    1768             : };
    1769             : 
    1770           0 : static bool GDALColorReliefFindNamedColor(const char *pszColorName, int *pnR,
    1771             :                                           int *pnG, int *pnB)
    1772             : {
    1773           0 :     *pnR = 0;
    1774           0 :     *pnG = 0;
    1775           0 :     *pnB = 0;
    1776           0 :     for (unsigned int i = 0; i < sizeof(namedColors) / sizeof(namedColors[0]);
    1777             :          i++)
    1778             :     {
    1779           0 :         if (EQUAL(pszColorName, namedColors[i].name))
    1780             :         {
    1781           0 :             *pnR = static_cast<int>(255.0 * namedColors[i].r);
    1782           0 :             *pnG = static_cast<int>(255.0 * namedColors[i].g);
    1783           0 :             *pnB = static_cast<int>(255.0 * namedColors[i].b);
    1784           0 :             return true;
    1785             :         }
    1786             :     }
    1787           0 :     return false;
    1788             : }
    1789             : 
    1790             : static ColorAssociation *
    1791          19 : GDALColorReliefParseColorFile(GDALRasterBandH hSrcBand,
    1792             :                               const char *pszColorFilename, int *pnColors)
    1793             : {
    1794          19 :     VSILFILE *fpColorFile = VSIFOpenL(pszColorFilename, "rt");
    1795          19 :     if (fpColorFile == nullptr)
    1796             :     {
    1797           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1798             :                  pszColorFilename);
    1799           0 :         *pnColors = 0;
    1800           0 :         return nullptr;
    1801             :     }
    1802             : 
    1803          19 :     ColorAssociation *pasColorAssociation = nullptr;
    1804          19 :     int nColorAssociation = 0;
    1805             : 
    1806          19 :     int bSrcHasNoData = FALSE;
    1807             :     double dfSrcNoDataValue =
    1808          19 :         GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData);
    1809             : 
    1810          19 :     const char *pszLine = nullptr;
    1811          19 :     bool bIsGMT_CPT = false;
    1812         133 :     while ((pszLine = CPLReadLineL(fpColorFile)) != nullptr)
    1813             :     {
    1814         114 :         if (pszLine[0] == '#' && strstr(pszLine, "COLOR_MODEL"))
    1815             :         {
    1816           1 :             if (strstr(pszLine, "COLOR_MODEL = RGB") == nullptr)
    1817             :             {
    1818           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1819             :                          "Only COLOR_MODEL = RGB is supported");
    1820           0 :                 CPLFree(pasColorAssociation);
    1821           0 :                 *pnColors = 0;
    1822           0 :                 return nullptr;
    1823             :             }
    1824           1 :             bIsGMT_CPT = true;
    1825             :         }
    1826             : 
    1827             :         char **papszFields =
    1828         114 :             CSLTokenizeStringComplex(pszLine, " ,\t:", FALSE, FALSE);
    1829             :         /* Skip comment lines */
    1830         114 :         const int nTokens = CSLCount(papszFields);
    1831         114 :         if (nTokens >= 1 &&
    1832         114 :             (papszFields[0][0] == '#' || papszFields[0][0] == '/'))
    1833             :         {
    1834           3 :             CSLDestroy(papszFields);
    1835           3 :             continue;
    1836             :         }
    1837             : 
    1838         111 :         if (bIsGMT_CPT && nTokens == 8)
    1839             :         {
    1840           3 :             pasColorAssociation = static_cast<ColorAssociation *>(
    1841           6 :                 CPLRealloc(pasColorAssociation,
    1842           3 :                            (nColorAssociation + 2) * sizeof(ColorAssociation)));
    1843             : 
    1844           6 :             pasColorAssociation[nColorAssociation].dfVal =
    1845           3 :                 CPLAtof(papszFields[0]);
    1846           3 :             pasColorAssociation[nColorAssociation].nR = atoi(papszFields[1]);
    1847           3 :             pasColorAssociation[nColorAssociation].nG = atoi(papszFields[2]);
    1848           3 :             pasColorAssociation[nColorAssociation].nB = atoi(papszFields[3]);
    1849           3 :             pasColorAssociation[nColorAssociation].nA = 255;
    1850           3 :             nColorAssociation++;
    1851             : 
    1852           6 :             pasColorAssociation[nColorAssociation].dfVal =
    1853           3 :                 CPLAtof(papszFields[4]);
    1854           3 :             pasColorAssociation[nColorAssociation].nR = atoi(papszFields[5]);
    1855           3 :             pasColorAssociation[nColorAssociation].nG = atoi(papszFields[6]);
    1856           3 :             pasColorAssociation[nColorAssociation].nB = atoi(papszFields[7]);
    1857           3 :             pasColorAssociation[nColorAssociation].nA = 255;
    1858           3 :             nColorAssociation++;
    1859             :         }
    1860         108 :         else if (bIsGMT_CPT && nTokens == 4)
    1861             :         {
    1862             :             // The first token might be B (background), F (foreground) or N
    1863             :             // (nodata) Just interested in N.
    1864           3 :             if (EQUAL(papszFields[0], "N") && bSrcHasNoData)
    1865             :             {
    1866           1 :                 pasColorAssociation =
    1867           2 :                     static_cast<ColorAssociation *>(CPLRealloc(
    1868             :                         pasColorAssociation,
    1869           1 :                         (nColorAssociation + 1) * sizeof(ColorAssociation)));
    1870             : 
    1871           1 :                 pasColorAssociation[nColorAssociation].dfVal = dfSrcNoDataValue;
    1872           1 :                 pasColorAssociation[nColorAssociation].nR =
    1873           1 :                     atoi(papszFields[1]);
    1874           1 :                 pasColorAssociation[nColorAssociation].nG =
    1875           1 :                     atoi(papszFields[2]);
    1876           1 :                 pasColorAssociation[nColorAssociation].nB =
    1877           1 :                     atoi(papszFields[3]);
    1878           1 :                 pasColorAssociation[nColorAssociation].nA = 255;
    1879           1 :                 nColorAssociation++;
    1880             :             }
    1881             :         }
    1882         105 :         else if (!bIsGMT_CPT && nTokens >= 2)
    1883             :         {
    1884         105 :             pasColorAssociation = static_cast<ColorAssociation *>(
    1885         210 :                 CPLRealloc(pasColorAssociation,
    1886         105 :                            (nColorAssociation + 1) * sizeof(ColorAssociation)));
    1887         105 :             if (EQUAL(papszFields[0], "nv"))
    1888             :             {
    1889           5 :                 if (!bSrcHasNoData)
    1890             :                 {
    1891           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1892             :                              "Input dataset has no nodata value. "
    1893             :                              "Ignoring 'nv' entry in color palette");
    1894           1 :                     CSLDestroy(papszFields);
    1895           1 :                     continue;
    1896             :                 }
    1897           4 :                 pasColorAssociation[nColorAssociation].dfVal = dfSrcNoDataValue;
    1898             :             }
    1899         100 :             else if (strlen(papszFields[0]) > 1 &&
    1900          91 :                      papszFields[0][strlen(papszFields[0]) - 1] == '%')
    1901             :             {
    1902           0 :                 const double dfPct = CPLAtof(papszFields[0]) / 100.0;
    1903           0 :                 if (dfPct < 0.0 || dfPct > 1.0)
    1904             :                 {
    1905           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1906             :                              "Wrong value for a percentage : %s",
    1907             :                              papszFields[0]);
    1908           0 :                     CSLDestroy(papszFields);
    1909           0 :                     VSIFCloseL(fpColorFile);
    1910           0 :                     CPLFree(pasColorAssociation);
    1911           0 :                     *pnColors = 0;
    1912           0 :                     return nullptr;
    1913             :                 }
    1914           0 :                 pasColorAssociation[nColorAssociation].dfVal =
    1915           0 :                     GDALColorReliefGetAbsoluteValFromPct(hSrcBand, dfPct);
    1916             :             }
    1917             :             else
    1918             :             {
    1919         200 :                 pasColorAssociation[nColorAssociation].dfVal =
    1920         100 :                     CPLAtof(papszFields[0]);
    1921             :             }
    1922             : 
    1923         104 :             if (nTokens >= 4)
    1924             :             {
    1925         104 :                 pasColorAssociation[nColorAssociation].nR =
    1926         104 :                     atoi(papszFields[1]);
    1927         104 :                 pasColorAssociation[nColorAssociation].nG =
    1928         104 :                     atoi(papszFields[2]);
    1929         104 :                 pasColorAssociation[nColorAssociation].nB =
    1930         104 :                     atoi(papszFields[3]);
    1931         104 :                 pasColorAssociation[nColorAssociation].nA =
    1932         104 :                     (CSLCount(papszFields) >= 5) ? atoi(papszFields[4]) : 255;
    1933             :             }
    1934             :             else
    1935             :             {
    1936           0 :                 int nR = 0;
    1937           0 :                 int nG = 0;
    1938           0 :                 int nB = 0;
    1939           0 :                 if (!GDALColorReliefFindNamedColor(papszFields[1], &nR, &nG,
    1940             :                                                    &nB))
    1941             :                 {
    1942           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Unknown color : %s",
    1943           0 :                              papszFields[1]);
    1944           0 :                     CSLDestroy(papszFields);
    1945           0 :                     VSIFCloseL(fpColorFile);
    1946           0 :                     CPLFree(pasColorAssociation);
    1947           0 :                     *pnColors = 0;
    1948           0 :                     return nullptr;
    1949             :                 }
    1950           0 :                 pasColorAssociation[nColorAssociation].nR = nR;
    1951           0 :                 pasColorAssociation[nColorAssociation].nG = nG;
    1952           0 :                 pasColorAssociation[nColorAssociation].nB = nB;
    1953           0 :                 pasColorAssociation[nColorAssociation].nA =
    1954           0 :                     (CSLCount(papszFields) >= 3) ? atoi(papszFields[2]) : 255;
    1955             :             }
    1956             : 
    1957         104 :             nColorAssociation++;
    1958             :         }
    1959         110 :         CSLDestroy(papszFields);
    1960             :     }
    1961          19 :     VSIFCloseL(fpColorFile);
    1962             : 
    1963          19 :     if (nColorAssociation == 0)
    1964             :     {
    1965           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1966             :                  "No color association found in %s", pszColorFilename);
    1967           1 :         CPLFree(pasColorAssociation);
    1968           1 :         *pnColors = 0;
    1969           1 :         return nullptr;
    1970             :     }
    1971             : 
    1972          18 :     GDALColorReliefProcessColors(&pasColorAssociation, &nColorAssociation,
    1973             :                                  bSrcHasNoData, dfSrcNoDataValue);
    1974             : 
    1975          18 :     *pnColors = nColorAssociation;
    1976          18 :     return pasColorAssociation;
    1977             : }
    1978             : 
    1979          15 : static GByte *GDALColorReliefPrecompute(GDALRasterBandH hSrcBand,
    1980             :                                         ColorAssociation *pasColorAssociation,
    1981             :                                         int nColorAssociation,
    1982             :                                         ColorSelectionMode eColorSelectionMode,
    1983             :                                         int *pnIndexOffset)
    1984             : {
    1985          15 :     const GDALDataType eDT = GDALGetRasterDataType(hSrcBand);
    1986          15 :     GByte *pabyPrecomputed = nullptr;
    1987          15 :     const int nIndexOffset = (eDT == GDT_Int16) ? 32768 : 0;
    1988          15 :     *pnIndexOffset = nIndexOffset;
    1989          15 :     const int nXSize = GDALGetRasterBandXSize(hSrcBand);
    1990          15 :     const int nYSize = GDALGetRasterBandYSize(hSrcBand);
    1991          15 :     if (eDT == GDT_Byte || ((eDT == GDT_Int16 || eDT == GDT_UInt16) &&
    1992          10 :                             static_cast<GIntBig>(nXSize) * nYSize > 65536))
    1993             :     {
    1994           1 :         const int iMax = (eDT == GDT_Byte) ? 256 : 65536;
    1995           1 :         pabyPrecomputed = static_cast<GByte *>(VSI_MALLOC2_VERBOSE(4, iMax));
    1996           1 :         if (pabyPrecomputed)
    1997             :         {
    1998         257 :             for (int i = 0; i < iMax; i++)
    1999             :             {
    2000         256 :                 int nR = 0;
    2001         256 :                 int nG = 0;
    2002         256 :                 int nB = 0;
    2003         256 :                 int nA = 0;
    2004         256 :                 GDALColorReliefGetRGBA(pasColorAssociation, nColorAssociation,
    2005         256 :                                        i - nIndexOffset, eColorSelectionMode,
    2006             :                                        &nR, &nG, &nB, &nA);
    2007         256 :                 pabyPrecomputed[4 * i] = static_cast<GByte>(nR);
    2008         256 :                 pabyPrecomputed[4 * i + 1] = static_cast<GByte>(nG);
    2009         256 :                 pabyPrecomputed[4 * i + 2] = static_cast<GByte>(nB);
    2010         256 :                 pabyPrecomputed[4 * i + 3] = static_cast<GByte>(nA);
    2011             :             }
    2012             :         }
    2013             :     }
    2014          15 :     return pabyPrecomputed;
    2015             : }
    2016             : 
    2017             : /************************************************************************/
    2018             : /* ==================================================================== */
    2019             : /*                       GDALColorReliefDataset                        */
    2020             : /* ==================================================================== */
    2021             : /************************************************************************/
    2022             : 
    2023             : class GDALColorReliefRasterBand;
    2024             : 
    2025             : class GDALColorReliefDataset : public GDALDataset
    2026             : {
    2027             :     friend class GDALColorReliefRasterBand;
    2028             : 
    2029             :     GDALDatasetH hSrcDS;
    2030             :     GDALRasterBandH hSrcBand;
    2031             :     int nColorAssociation;
    2032             :     ColorAssociation *pasColorAssociation;
    2033             :     ColorSelectionMode eColorSelectionMode;
    2034             :     GByte *pabyPrecomputed;
    2035             :     int nIndexOffset;
    2036             :     float *pafSourceBuf;
    2037             :     int *panSourceBuf;
    2038             :     int nCurBlockXOff;
    2039             :     int nCurBlockYOff;
    2040             : 
    2041             :   public:
    2042             :     GDALColorReliefDataset(GDALDatasetH hSrcDS, GDALRasterBandH hSrcBand,
    2043             :                            const char *pszColorFilename,
    2044             :                            ColorSelectionMode eColorSelectionMode, int bAlpha);
    2045             :     ~GDALColorReliefDataset();
    2046             : 
    2047           2 :     bool InitOK() const
    2048             :     {
    2049           2 :         return pafSourceBuf != nullptr || panSourceBuf != nullptr;
    2050             :     }
    2051             : 
    2052             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
    2053             :     const OGRSpatialReference *GetSpatialRef() const override;
    2054             : };
    2055             : 
    2056             : /************************************************************************/
    2057             : /* ==================================================================== */
    2058             : /*                    GDALColorReliefRasterBand                       */
    2059             : /* ==================================================================== */
    2060             : /************************************************************************/
    2061             : 
    2062             : class GDALColorReliefRasterBand : public GDALRasterBand
    2063             : {
    2064             :     friend class GDALColorReliefDataset;
    2065             : 
    2066             :   public:
    2067             :     GDALColorReliefRasterBand(GDALColorReliefDataset *, int);
    2068             : 
    2069             :     virtual CPLErr IReadBlock(int, int, void *) override;
    2070             :     virtual GDALColorInterp GetColorInterpretation() override;
    2071             : };
    2072             : 
    2073           2 : GDALColorReliefDataset::GDALColorReliefDataset(
    2074             :     GDALDatasetH hSrcDSIn, GDALRasterBandH hSrcBandIn,
    2075             :     const char *pszColorFilename, ColorSelectionMode eColorSelectionModeIn,
    2076           2 :     int bAlpha)
    2077             :     : hSrcDS(hSrcDSIn), hSrcBand(hSrcBandIn), nColorAssociation(0),
    2078             :       pasColorAssociation(nullptr), eColorSelectionMode(eColorSelectionModeIn),
    2079             :       pabyPrecomputed(nullptr), nIndexOffset(0), pafSourceBuf(nullptr),
    2080           2 :       panSourceBuf(nullptr), nCurBlockXOff(-1), nCurBlockYOff(-1)
    2081             : {
    2082           2 :     pasColorAssociation = GDALColorReliefParseColorFile(
    2083             :         hSrcBand, pszColorFilename, &nColorAssociation);
    2084             : 
    2085           2 :     nRasterXSize = GDALGetRasterXSize(hSrcDS);
    2086           2 :     nRasterYSize = GDALGetRasterYSize(hSrcDS);
    2087             : 
    2088           2 :     int nBlockXSize = 0;
    2089           2 :     int nBlockYSize = 0;
    2090           2 :     GDALGetBlockSize(hSrcBand, &nBlockXSize, &nBlockYSize);
    2091             : 
    2092           2 :     pabyPrecomputed = GDALColorReliefPrecompute(
    2093             :         hSrcBand, pasColorAssociation, nColorAssociation, eColorSelectionMode,
    2094             :         &nIndexOffset);
    2095             : 
    2096           8 :     for (int i = 0; i < ((bAlpha) ? 4 : 3); i++)
    2097             :     {
    2098           6 :         SetBand(i + 1, new GDALColorReliefRasterBand(this, i + 1));
    2099             :     }
    2100             : 
    2101           2 :     if (pabyPrecomputed)
    2102           0 :         panSourceBuf = static_cast<int *>(
    2103           0 :             VSI_MALLOC3_VERBOSE(sizeof(int), nBlockXSize, nBlockYSize));
    2104             :     else
    2105           2 :         pafSourceBuf = static_cast<float *>(
    2106           2 :             VSI_MALLOC3_VERBOSE(sizeof(float), nBlockXSize, nBlockYSize));
    2107           2 : }
    2108             : 
    2109           4 : GDALColorReliefDataset::~GDALColorReliefDataset()
    2110             : {
    2111           2 :     CPLFree(pasColorAssociation);
    2112           2 :     CPLFree(pabyPrecomputed);
    2113           2 :     CPLFree(panSourceBuf);
    2114           2 :     CPLFree(pafSourceBuf);
    2115           4 : }
    2116             : 
    2117           2 : CPLErr GDALColorReliefDataset::GetGeoTransform(double *padfGeoTransform)
    2118             : {
    2119           2 :     return GDALGetGeoTransform(hSrcDS, padfGeoTransform);
    2120             : }
    2121             : 
    2122           2 : const OGRSpatialReference *GDALColorReliefDataset::GetSpatialRef() const
    2123             : {
    2124           2 :     return GDALDataset::FromHandle(hSrcDS)->GetSpatialRef();
    2125             : }
    2126             : 
    2127           6 : GDALColorReliefRasterBand::GDALColorReliefRasterBand(
    2128           6 :     GDALColorReliefDataset *poDSIn, int nBandIn)
    2129             : {
    2130           6 :     poDS = poDSIn;
    2131           6 :     nBand = nBandIn;
    2132           6 :     eDataType = GDT_Byte;
    2133           6 :     GDALGetBlockSize(poDSIn->hSrcBand, &nBlockXSize, &nBlockYSize);
    2134           6 : }
    2135             : 
    2136          36 : CPLErr GDALColorReliefRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    2137             :                                              void *pImage)
    2138             : {
    2139             :     GDALColorReliefDataset *poGDS =
    2140          36 :         cpl::down_cast<GDALColorReliefDataset *>(poDS);
    2141          72 :     const int nReqXSize = (nBlockXOff + 1) * nBlockXSize >= nRasterXSize
    2142          36 :                               ? nRasterXSize - nBlockXOff * nBlockXSize
    2143             :                               : nBlockXSize;
    2144             : 
    2145          72 :     const int nReqYSize = (nBlockYOff + 1) * nBlockYSize >= nRasterYSize
    2146          36 :                               ? nRasterYSize - nBlockYOff * nBlockYSize
    2147             :                               : nBlockYSize;
    2148             : 
    2149          36 :     if (poGDS->nCurBlockXOff != nBlockXOff ||
    2150          34 :         poGDS->nCurBlockYOff != nBlockYOff)
    2151             :     {
    2152          12 :         poGDS->nCurBlockXOff = nBlockXOff;
    2153          12 :         poGDS->nCurBlockYOff = nBlockYOff;
    2154             : 
    2155          24 :         const CPLErr eErr = GDALRasterIO(
    2156          12 :             poGDS->hSrcBand, GF_Read, nBlockXOff * nBlockXSize,
    2157          12 :             nBlockYOff * nBlockYSize, nReqXSize, nReqYSize,
    2158          12 :             (poGDS->panSourceBuf) ? static_cast<void *>(poGDS->panSourceBuf)
    2159             :                                   : static_cast<void *>(poGDS->pafSourceBuf),
    2160             :             nReqXSize, nReqYSize,
    2161          12 :             (poGDS->panSourceBuf) ? GDT_Int32 : GDT_Float32, 0, 0);
    2162          12 :         if (eErr != CE_None)
    2163             :         {
    2164           0 :             memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
    2165           0 :             return eErr;
    2166             :         }
    2167             :     }
    2168             : 
    2169          36 :     int j = 0;
    2170          36 :     if (poGDS->panSourceBuf)
    2171             :     {
    2172           0 :         for (int y = 0; y < nReqYSize; y++)
    2173             :         {
    2174           0 :             for (int x = 0; x < nReqXSize; x++)
    2175             :             {
    2176           0 :                 const int nIndex = poGDS->panSourceBuf[j] + poGDS->nIndexOffset;
    2177           0 :                 static_cast<GByte *>(pImage)[y * nBlockXSize + x] =
    2178           0 :                     poGDS->pabyPrecomputed[4 * nIndex + nBand - 1];
    2179           0 :                 j++;
    2180             :             }
    2181             :         }
    2182             :     }
    2183             :     else
    2184             :     {
    2185          36 :         int anComponents[4] = {0, 0, 0, 0};
    2186         762 :         for (int y = 0; y < nReqYSize; y++)
    2187             :         {
    2188       88572 :             for (int x = 0; x < nReqXSize; x++)
    2189             :             {
    2190       87846 :                 GDALColorReliefGetRGBA(
    2191             :                     poGDS->pasColorAssociation, poGDS->nColorAssociation,
    2192       87846 :                     poGDS->pafSourceBuf[j], poGDS->eColorSelectionMode,
    2193             :                     &anComponents[0], &anComponents[1], &anComponents[2],
    2194             :                     &anComponents[3]);
    2195       87846 :                 static_cast<GByte *>(pImage)[y * nBlockXSize + x] =
    2196       87846 :                     static_cast<GByte>(anComponents[nBand - 1]);
    2197       87846 :                 j++;
    2198             :             }
    2199             :         }
    2200             :     }
    2201             : 
    2202          36 :     return CE_None;
    2203             : }
    2204             : 
    2205          12 : GDALColorInterp GDALColorReliefRasterBand::GetColorInterpretation()
    2206             : {
    2207          12 :     return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
    2208             : }
    2209             : 
    2210             : static CPLErr
    2211          14 : GDALColorRelief(GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand1,
    2212             :                 GDALRasterBandH hDstBand2, GDALRasterBandH hDstBand3,
    2213             :                 GDALRasterBandH hDstBand4, const char *pszColorFilename,
    2214             :                 ColorSelectionMode eColorSelectionMode,
    2215             :                 GDALProgressFunc pfnProgress, void *pProgressData)
    2216             : {
    2217          14 :     if (hSrcBand == nullptr || hDstBand1 == nullptr || hDstBand2 == nullptr ||
    2218             :         hDstBand3 == nullptr)
    2219           0 :         return CE_Failure;
    2220             : 
    2221          14 :     int nColorAssociation = 0;
    2222          14 :     ColorAssociation *pasColorAssociation = GDALColorReliefParseColorFile(
    2223             :         hSrcBand, pszColorFilename, &nColorAssociation);
    2224          14 :     if (pasColorAssociation == nullptr)
    2225           1 :         return CE_Failure;
    2226             : 
    2227          13 :     if (pfnProgress == nullptr)
    2228           6 :         pfnProgress = GDALDummyProgress;
    2229             : 
    2230             :     /* -------------------------------------------------------------------- */
    2231             :     /*      Precompute the map from values to RGBA quadruplets              */
    2232             :     /*      for GDT_Byte, GDT_Int16 or GDT_UInt16                           */
    2233             :     /* -------------------------------------------------------------------- */
    2234          13 :     int nIndexOffset = 0;
    2235          13 :     GByte *pabyPrecomputed = GDALColorReliefPrecompute(
    2236             :         hSrcBand, pasColorAssociation, nColorAssociation, eColorSelectionMode,
    2237             :         &nIndexOffset);
    2238             : 
    2239             :     /* -------------------------------------------------------------------- */
    2240             :     /*      Initialize progress counter.                                    */
    2241             :     /* -------------------------------------------------------------------- */
    2242             : 
    2243          13 :     const int nXSize = GDALGetRasterBandXSize(hSrcBand);
    2244          13 :     const int nYSize = GDALGetRasterBandYSize(hSrcBand);
    2245             : 
    2246          13 :     float *pafSourceBuf = nullptr;
    2247          13 :     int *panSourceBuf = nullptr;
    2248          13 :     if (pabyPrecomputed)
    2249             :         panSourceBuf =
    2250           1 :             static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nXSize));
    2251             :     else
    2252             :         pafSourceBuf =
    2253          12 :             static_cast<float *>(VSI_MALLOC2_VERBOSE(sizeof(float), nXSize));
    2254          13 :     GByte *pabyDestBuf1 = static_cast<GByte *>(VSI_MALLOC2_VERBOSE(4, nXSize));
    2255          13 :     GByte *pabyDestBuf2 = pabyDestBuf1 ? pabyDestBuf1 + nXSize : nullptr;
    2256          13 :     GByte *pabyDestBuf3 = pabyDestBuf2 ? pabyDestBuf2 + nXSize : nullptr;
    2257          13 :     GByte *pabyDestBuf4 = pabyDestBuf3 ? pabyDestBuf3 + nXSize : nullptr;
    2258             : 
    2259          13 :     if ((pabyPrecomputed != nullptr && panSourceBuf == nullptr) ||
    2260          13 :         (pabyPrecomputed == nullptr && pafSourceBuf == nullptr) ||
    2261             :         pabyDestBuf1 == nullptr)
    2262             :     {
    2263           0 :         VSIFree(pabyPrecomputed);
    2264           0 :         CPLFree(pafSourceBuf);
    2265           0 :         CPLFree(panSourceBuf);
    2266           0 :         CPLFree(pabyDestBuf1);
    2267           0 :         CPLFree(pasColorAssociation);
    2268             : 
    2269           0 :         return CE_Failure;
    2270             :     }
    2271             : 
    2272          13 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    2273             :     {
    2274           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    2275           0 :         VSIFree(pabyPrecomputed);
    2276           0 :         CPLFree(pafSourceBuf);
    2277           0 :         CPLFree(panSourceBuf);
    2278           0 :         CPLFree(pabyDestBuf1);
    2279           0 :         CPLFree(pasColorAssociation);
    2280             : 
    2281           0 :         return CE_Failure;
    2282             :     }
    2283             : 
    2284          13 :     int nR = 0;
    2285          13 :     int nG = 0;
    2286          13 :     int nB = 0;
    2287          13 :     int nA = 0;
    2288             : 
    2289        1229 :     for (int i = 0; i < nYSize; i++)
    2290             :     {
    2291             :         /* Read source buffer */
    2292        1216 :         CPLErr eErr = GDALRasterIO(
    2293             :             hSrcBand, GF_Read, 0, i, nXSize, 1,
    2294             :             panSourceBuf ? static_cast<void *>(panSourceBuf)
    2295             :                          : static_cast<void *>(pafSourceBuf),
    2296             :             nXSize, 1, panSourceBuf ? GDT_Int32 : GDT_Float32, 0, 0);
    2297        1216 :         if (eErr != CE_None)
    2298             :         {
    2299           0 :             VSIFree(pabyPrecomputed);
    2300           0 :             CPLFree(pafSourceBuf);
    2301           0 :             CPLFree(panSourceBuf);
    2302           0 :             CPLFree(pabyDestBuf1);
    2303           0 :             CPLFree(pasColorAssociation);
    2304           0 :             return eErr;
    2305             :         }
    2306             : 
    2307        1216 :         if (pabyPrecomputed)
    2308             :         {
    2309           2 :             for (int j = 0; j < nXSize; j++)
    2310             :             {
    2311           1 :                 int nIndex = panSourceBuf[j] + nIndexOffset;
    2312           1 :                 pabyDestBuf1[j] = pabyPrecomputed[4 * nIndex];
    2313           1 :                 pabyDestBuf2[j] = pabyPrecomputed[4 * nIndex + 1];
    2314           1 :                 pabyDestBuf3[j] = pabyPrecomputed[4 * nIndex + 2];
    2315           1 :                 pabyDestBuf4[j] = pabyPrecomputed[4 * nIndex + 3];
    2316             :             }
    2317             :         }
    2318             :         else
    2319             :         {
    2320      147635 :             for (int j = 0; j < nXSize; j++)
    2321             :             {
    2322      146420 :                 GDALColorReliefGetRGBA(pasColorAssociation, nColorAssociation,
    2323      146420 :                                        pafSourceBuf[j], eColorSelectionMode,
    2324             :                                        &nR, &nG, &nB, &nA);
    2325      146420 :                 pabyDestBuf1[j] = static_cast<GByte>(nR);
    2326      146420 :                 pabyDestBuf2[j] = static_cast<GByte>(nG);
    2327      146420 :                 pabyDestBuf3[j] = static_cast<GByte>(nB);
    2328      146420 :                 pabyDestBuf4[j] = static_cast<GByte>(nA);
    2329             :             }
    2330             :         }
    2331             : 
    2332             :         /* -----------------------------------------
    2333             :          * Write Line to Raster
    2334             :          */
    2335        1216 :         eErr = GDALRasterIO(hDstBand1, GF_Write, 0, i, nXSize, 1, pabyDestBuf1,
    2336             :                             nXSize, 1, GDT_Byte, 0, 0);
    2337        1216 :         if (eErr != CE_None)
    2338             :         {
    2339           0 :             VSIFree(pabyPrecomputed);
    2340           0 :             CPLFree(pafSourceBuf);
    2341           0 :             CPLFree(panSourceBuf);
    2342           0 :             CPLFree(pabyDestBuf1);
    2343           0 :             CPLFree(pasColorAssociation);
    2344             : 
    2345           0 :             return eErr;
    2346             :         }
    2347             : 
    2348        1216 :         eErr = GDALRasterIO(hDstBand2, GF_Write, 0, i, nXSize, 1, pabyDestBuf2,
    2349             :                             nXSize, 1, GDT_Byte, 0, 0);
    2350        1216 :         if (eErr != CE_None)
    2351             :         {
    2352           0 :             VSIFree(pabyPrecomputed);
    2353           0 :             CPLFree(pafSourceBuf);
    2354           0 :             CPLFree(panSourceBuf);
    2355           0 :             CPLFree(pabyDestBuf1);
    2356           0 :             CPLFree(pasColorAssociation);
    2357             : 
    2358           0 :             return eErr;
    2359             :         }
    2360             : 
    2361        1216 :         eErr = GDALRasterIO(hDstBand3, GF_Write, 0, i, nXSize, 1, pabyDestBuf3,
    2362             :                             nXSize, 1, GDT_Byte, 0, 0);
    2363        1216 :         if (eErr != CE_None)
    2364             :         {
    2365           0 :             VSIFree(pabyPrecomputed);
    2366           0 :             CPLFree(pafSourceBuf);
    2367           0 :             CPLFree(panSourceBuf);
    2368           0 :             CPLFree(pabyDestBuf1);
    2369           0 :             CPLFree(pasColorAssociation);
    2370             : 
    2371           0 :             return eErr;
    2372             :         }
    2373             : 
    2374        1216 :         if (hDstBand4)
    2375             :         {
    2376         121 :             eErr = GDALRasterIO(hDstBand4, GF_Write, 0, i, nXSize, 1,
    2377             :                                 pabyDestBuf4, nXSize, 1, GDT_Byte, 0, 0);
    2378         121 :             if (eErr != CE_None)
    2379             :             {
    2380           0 :                 VSIFree(pabyPrecomputed);
    2381           0 :                 CPLFree(pafSourceBuf);
    2382           0 :                 CPLFree(panSourceBuf);
    2383           0 :                 CPLFree(pabyDestBuf1);
    2384           0 :                 CPLFree(pasColorAssociation);
    2385             : 
    2386           0 :                 return eErr;
    2387             :             }
    2388             :         }
    2389             : 
    2390        1216 :         if (!pfnProgress(1.0 * (i + 1) / nYSize, nullptr, pProgressData))
    2391             :         {
    2392           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    2393             : 
    2394           0 :             VSIFree(pabyPrecomputed);
    2395           0 :             CPLFree(pafSourceBuf);
    2396           0 :             CPLFree(panSourceBuf);
    2397           0 :             CPLFree(pabyDestBuf1);
    2398           0 :             CPLFree(pasColorAssociation);
    2399             : 
    2400           0 :             return CE_Failure;
    2401             :         }
    2402             :     }
    2403             : 
    2404          13 :     pfnProgress(1.0, nullptr, pProgressData);
    2405             : 
    2406          13 :     VSIFree(pabyPrecomputed);
    2407          13 :     CPLFree(pafSourceBuf);
    2408          13 :     CPLFree(panSourceBuf);
    2409          13 :     CPLFree(pabyDestBuf1);
    2410          13 :     CPLFree(pasColorAssociation);
    2411             : 
    2412          13 :     return CE_None;
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                     GDALGenerateVRTColorRelief()                     */
    2417             : /************************************************************************/
    2418             : 
    2419           3 : static CPLErr GDALGenerateVRTColorRelief(const char *pszDstFilename,
    2420             :                                          GDALDatasetH hSrcDataset,
    2421             :                                          GDALRasterBandH hSrcBand,
    2422             :                                          const char *pszColorFilename,
    2423             :                                          ColorSelectionMode eColorSelectionMode,
    2424             :                                          bool bAddAlpha)
    2425             : {
    2426           3 :     int nColorAssociation = 0;
    2427           3 :     ColorAssociation *pasColorAssociation = GDALColorReliefParseColorFile(
    2428             :         hSrcBand, pszColorFilename, &nColorAssociation);
    2429           3 :     if (pasColorAssociation == nullptr)
    2430           0 :         return CE_Failure;
    2431             : 
    2432           3 :     VSILFILE *fp = VSIFOpenL(pszDstFilename, "wt");
    2433           3 :     if (fp == nullptr)
    2434             :     {
    2435           0 :         CPLFree(pasColorAssociation);
    2436           0 :         return CE_Failure;
    2437             :     }
    2438             : 
    2439           3 :     const int nXSize = GDALGetRasterBandXSize(hSrcBand);
    2440           3 :     const int nYSize = GDALGetRasterBandYSize(hSrcBand);
    2441             : 
    2442             :     bool bOK =
    2443           3 :         VSIFPrintfL(fp, "<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">\n",
    2444           3 :                     nXSize, nYSize) > 0;
    2445           3 :     const char *pszProjectionRef = GDALGetProjectionRef(hSrcDataset);
    2446           3 :     if (pszProjectionRef && pszProjectionRef[0] != '\0')
    2447             :     {
    2448             :         char *pszEscapedString =
    2449           2 :             CPLEscapeString(pszProjectionRef, -1, CPLES_XML);
    2450           2 :         bOK &= VSIFPrintfL(fp, "  <SRS>%s</SRS>\n", pszEscapedString) > 0;
    2451           2 :         VSIFree(pszEscapedString);
    2452             :     }
    2453           3 :     double adfGT[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    2454           3 :     if (GDALGetGeoTransform(hSrcDataset, adfGT) == CE_None)
    2455             :     {
    2456           3 :         bOK &= VSIFPrintfL(fp,
    2457             :                            "  <GeoTransform> %.16g, %.16g, %.16g, "
    2458             :                            "%.16g, %.16g, %.16g</GeoTransform>\n",
    2459             :                            adfGT[0], adfGT[1], adfGT[2], adfGT[3], adfGT[4],
    2460           3 :                            adfGT[5]) > 0;
    2461             :     }
    2462             : 
    2463           3 :     int nBlockXSize = 0;
    2464           3 :     int nBlockYSize = 0;
    2465           3 :     GDALGetBlockSize(hSrcBand, &nBlockXSize, &nBlockYSize);
    2466             : 
    2467           3 :     int bRelativeToVRT = FALSE;
    2468           3 :     CPLString osPath = CPLGetPath(pszDstFilename);
    2469           3 :     char *pszSourceFilename = CPLStrdup(CPLExtractRelativePath(
    2470             :         osPath.c_str(), GDALGetDescription(hSrcDataset), &bRelativeToVRT));
    2471             : 
    2472           3 :     const int nBands = 3 + (bAddAlpha ? 1 : 0);
    2473             : 
    2474          12 :     for (int iBand = 0; iBand < nBands; iBand++)
    2475             :     {
    2476           9 :         bOK &=
    2477           9 :             VSIFPrintfL(fp, "  <VRTRasterBand dataType=\"Byte\" band=\"%d\">\n",
    2478           9 :                         iBand + 1) > 0;
    2479           9 :         bOK &= VSIFPrintfL(
    2480             :                    fp, "    <ColorInterp>%s</ColorInterp>\n",
    2481             :                    GDALGetColorInterpretationName(
    2482           9 :                        static_cast<GDALColorInterp>(GCI_RedBand + iBand))) > 0;
    2483           9 :         bOK &= VSIFPrintfL(fp, "    <ComplexSource>\n") > 0;
    2484           9 :         bOK &= VSIFPrintfL(fp,
    2485             :                            "      <SourceFilename "
    2486             :                            "relativeToVRT=\"%d\">%s</SourceFilename>\n",
    2487           9 :                            bRelativeToVRT, pszSourceFilename) > 0;
    2488           9 :         bOK &= VSIFPrintfL(fp, "      <SourceBand>%d</SourceBand>\n",
    2489           9 :                            GDALGetBandNumber(hSrcBand)) > 0;
    2490           9 :         bOK &= VSIFPrintfL(fp,
    2491             :                            "      <SourceProperties RasterXSize=\"%d\" "
    2492             :                            "RasterYSize=\"%d\" DataType=\"%s\" "
    2493             :                            "BlockXSize=\"%d\" BlockYSize=\"%d\"/>\n",
    2494             :                            nXSize, nYSize,
    2495             :                            GDALGetDataTypeName(GDALGetRasterDataType(hSrcBand)),
    2496           9 :                            nBlockXSize, nBlockYSize) > 0;
    2497           9 :         bOK &=
    2498           9 :             VSIFPrintfL(
    2499             :                 fp,
    2500             :                 "      "
    2501             :                 "<SrcRect xOff=\"0\" yOff=\"0\" xSize=\"%d\" ySize=\"%d\"/>\n",
    2502           9 :                 nXSize, nYSize) > 0;
    2503           9 :         bOK &=
    2504           9 :             VSIFPrintfL(
    2505             :                 fp,
    2506             :                 "      "
    2507             :                 "<DstRect xOff=\"0\" yOff=\"0\" xSize=\"%d\" ySize=\"%d\"/>\n",
    2508           9 :                 nXSize, nYSize) > 0;
    2509             : 
    2510           9 :         bOK &= VSIFPrintfL(fp, "      <LUT>") > 0;
    2511             : 
    2512          72 :         for (int iColor = 0; iColor < nColorAssociation; iColor++)
    2513             :         {
    2514          63 :             if (eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY)
    2515             :             {
    2516          21 :                 if (iColor > 1)
    2517          15 :                     bOK &= VSIFPrintfL(fp, ",") > 0;
    2518             :             }
    2519          42 :             else if (iColor > 0)
    2520          36 :                 bOK &= VSIFPrintfL(fp, ",") > 0;
    2521             : 
    2522          63 :             const double dfVal = pasColorAssociation[iColor].dfVal;
    2523             : 
    2524          63 :             if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY)
    2525             :             {
    2526           0 :                 bOK &= VSIFPrintfL(fp, "%.18g:0,",
    2527           0 :                                    dfVal - fabs(dfVal) * DBL_EPSILON) > 0;
    2528             :             }
    2529          63 :             else if (iColor > 0 &&
    2530             :                      eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY)
    2531             :             {
    2532          18 :                 const double dfMidVal =
    2533          18 :                     (dfVal + pasColorAssociation[iColor - 1].dfVal) / 2.0;
    2534          18 :                 bOK &=
    2535          30 :                     VSIFPrintfL(
    2536          18 :                         fp, "%.18g:%d", dfMidVal - fabs(dfMidVal) * DBL_EPSILON,
    2537           6 :                         (iBand == 0)   ? pasColorAssociation[iColor - 1].nR
    2538          18 :                         : (iBand == 1) ? pasColorAssociation[iColor - 1].nG
    2539             :                         : (iBand == 2)
    2540           6 :                             ? pasColorAssociation[iColor - 1].nB
    2541           0 :                             : pasColorAssociation[iColor - 1].nA) > 0;
    2542          36 :                 bOK &= VSIFPrintfL(
    2543             :                            fp, ",%.18g:%d", dfMidVal,
    2544           6 :                            (iBand == 0)   ? pasColorAssociation[iColor].nR
    2545          18 :                            : (iBand == 1) ? pasColorAssociation[iColor].nG
    2546           6 :                            : (iBand == 2) ? pasColorAssociation[iColor].nB
    2547          18 :                                           : pasColorAssociation[iColor].nA) > 0;
    2548             :             }
    2549             : 
    2550          63 :             if (eColorSelectionMode != COLOR_SELECTION_NEAREST_ENTRY)
    2551             :             {
    2552          42 :                 if (dfVal != static_cast<double>(static_cast<int>(dfVal)))
    2553           9 :                     bOK &= VSIFPrintfL(fp, "%.18g", dfVal) > 0;
    2554             :                 else
    2555          33 :                     bOK &= VSIFPrintfL(fp, "%d", static_cast<int>(dfVal)) > 0;
    2556          84 :                 bOK &= VSIFPrintfL(
    2557             :                            fp, ":%d",
    2558          14 :                            (iBand == 0)   ? pasColorAssociation[iColor].nR
    2559          42 :                            : (iBand == 1) ? pasColorAssociation[iColor].nG
    2560          14 :                            : (iBand == 2) ? pasColorAssociation[iColor].nB
    2561          42 :                                           : pasColorAssociation[iColor].nA) > 0;
    2562             :             }
    2563             : 
    2564          63 :             if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY)
    2565             :             {
    2566           0 :                 bOK &= VSIFPrintfL(fp, ",%.18g:0",
    2567           0 :                                    dfVal + fabs(dfVal) * DBL_EPSILON) > 0;
    2568             :             }
    2569             :         }
    2570           9 :         bOK &= VSIFPrintfL(fp, "</LUT>\n") > 0;
    2571             : 
    2572           9 :         bOK &= VSIFPrintfL(fp, "    </ComplexSource>\n") > 0;
    2573           9 :         bOK &= VSIFPrintfL(fp, "  </VRTRasterBand>\n") > 0;
    2574             :     }
    2575             : 
    2576           3 :     CPLFree(pszSourceFilename);
    2577             : 
    2578           3 :     bOK &= VSIFPrintfL(fp, "</VRTDataset>\n") > 0;
    2579             : 
    2580           3 :     VSIFCloseL(fp);
    2581             : 
    2582           3 :     CPLFree(pasColorAssociation);
    2583             : 
    2584           3 :     return (bOK) ? CE_None : CE_Failure;
    2585             : }
    2586             : 
    2587             : /************************************************************************/
    2588             : /*                         GDALTRIAlg()                                 */
    2589             : /************************************************************************/
    2590             : 
    2591             : template <class T> static T MyAbs(T x);
    2592             : 
    2593           0 : template <> float MyAbs(float x)
    2594             : {
    2595           0 :     return fabsf(x);
    2596             : }
    2597             : 
    2598      113288 : template <> int MyAbs(int x)
    2599             : {
    2600      113288 :     return x >= 0 ? x : -x;
    2601             : }
    2602             : 
    2603             : // Implements Wilson et al. (2007), for bathymetric use cases
    2604             : template <class T>
    2605       14161 : static float GDALTRIAlgWilson(const T *afWin, float /*fDstNoDataValue*/,
    2606             :                               void * /*pData*/)
    2607             : {
    2608             :     // Terrain Ruggedness is average difference in height
    2609       14161 :     return (MyAbs(afWin[0] - afWin[4]) + MyAbs(afWin[1] - afWin[4]) +
    2610       14161 :             MyAbs(afWin[2] - afWin[4]) + MyAbs(afWin[3] - afWin[4]) +
    2611       14161 :             MyAbs(afWin[5] - afWin[4]) + MyAbs(afWin[6] - afWin[4]) +
    2612       14161 :             MyAbs(afWin[7] - afWin[4]) + MyAbs(afWin[8] - afWin[4])) *
    2613       14161 :            0.125f;
    2614             : }
    2615             : 
    2616             : // Implements Riley, S.J., De Gloria, S.D., Elliot, R. (1999): A Terrain
    2617             : // Ruggedness that Quantifies Topographic Heterogeneity. Intermountain Journal
    2618             : // of Science, Vol.5, No.1-4, pp.23-27 for terrestrial use cases
    2619             : template <class T>
    2620       14161 : static float GDALTRIAlgRiley(const T *afWin, float /*fDstNoDataValue*/,
    2621             :                              void * /*pData*/)
    2622             : {
    2623      113288 :     const auto square = [](double x) { return x * x; };
    2624             : 
    2625             :     return static_cast<float>(
    2626       14161 :         std::sqrt(square(afWin[0] - afWin[4]) + square(afWin[1] - afWin[4]) +
    2627       14161 :                   square(afWin[2] - afWin[4]) + square(afWin[3] - afWin[4]) +
    2628       14161 :                   square(afWin[5] - afWin[4]) + square(afWin[6] - afWin[4]) +
    2629       14161 :                   square(afWin[7] - afWin[4]) + square(afWin[8] - afWin[4])));
    2630             : }
    2631             : 
    2632             : /************************************************************************/
    2633             : /*                         GDALTPIAlg()                                 */
    2634             : /************************************************************************/
    2635             : 
    2636             : template <class T>
    2637       14161 : static float GDALTPIAlg(const T *afWin, float /*fDstNoDataValue*/,
    2638             :                         void * /*pData*/)
    2639             : {
    2640             :     // Terrain Position is the difference between
    2641             :     // The central cell and the mean of the surrounding cells
    2642       14161 :     return afWin[4] - ((afWin[0] + afWin[1] + afWin[2] + afWin[3] + afWin[5] +
    2643       14161 :                         afWin[6] + afWin[7] + afWin[8]) *
    2644       14161 :                        0.125f);
    2645             : }
    2646             : 
    2647             : /************************************************************************/
    2648             : /*                     GDALRoughnessAlg()                               */
    2649             : /************************************************************************/
    2650             : 
    2651             : template <class T>
    2652       14161 : static float GDALRoughnessAlg(const T *afWin, float /*fDstNoDataValue*/,
    2653             :                               void * /*pData*/)
    2654             : {
    2655             :     // Roughness is the largest difference
    2656             :     //  between any two cells
    2657             : 
    2658       14161 :     T pafRoughnessMin = afWin[0];
    2659       14161 :     T pafRoughnessMax = afWin[0];
    2660             : 
    2661      127449 :     for (int k = 1; k < 9; k++)
    2662             :     {
    2663      113288 :         if (afWin[k] > pafRoughnessMax)
    2664             :         {
    2665       13318 :             pafRoughnessMax = afWin[k];
    2666             :         }
    2667      113288 :         if (afWin[k] < pafRoughnessMin)
    2668             :         {
    2669       24495 :             pafRoughnessMin = afWin[k];
    2670             :         }
    2671             :     }
    2672       14161 :     return static_cast<float>(pafRoughnessMax - pafRoughnessMin);
    2673             : }
    2674             : 
    2675             : /************************************************************************/
    2676             : /* ==================================================================== */
    2677             : /*                       GDALGeneric3x3Dataset                        */
    2678             : /* ==================================================================== */
    2679             : /************************************************************************/
    2680             : 
    2681             : template <class T> class GDALGeneric3x3RasterBand;
    2682             : 
    2683             : template <class T> class GDALGeneric3x3Dataset : public GDALDataset
    2684             : {
    2685             :     friend class GDALGeneric3x3RasterBand<T>;
    2686             : 
    2687             :     typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg;
    2688             :     void *pAlgData;
    2689             :     GDALDatasetH hSrcDS;
    2690             :     GDALRasterBandH hSrcBand;
    2691             :     T *apafSourceBuf[3];
    2692             :     int bDstHasNoData;
    2693             :     double dfDstNoDataValue;
    2694             :     int nCurLine;
    2695             :     bool bComputeAtEdges;
    2696             : 
    2697             :   public:
    2698             :     GDALGeneric3x3Dataset(GDALDatasetH hSrcDS, GDALRasterBandH hSrcBand,
    2699             :                           GDALDataType eDstDataType, int bDstHasNoData,
    2700             :                           double dfDstNoDataValue,
    2701             :                           typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
    2702             :                           void *pAlgData, bool bComputeAtEdges);
    2703             :     ~GDALGeneric3x3Dataset();
    2704             : 
    2705           4 :     bool InitOK() const
    2706             :     {
    2707           8 :         return apafSourceBuf[0] != nullptr && apafSourceBuf[1] != nullptr &&
    2708           8 :                apafSourceBuf[2] != nullptr;
    2709             :     }
    2710             : 
    2711             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
    2712             :     const OGRSpatialReference *GetSpatialRef() const override;
    2713             : };
    2714             : 
    2715             : /************************************************************************/
    2716             : /* ==================================================================== */
    2717             : /*                    GDALGeneric3x3RasterBand                       */
    2718             : /* ==================================================================== */
    2719             : /************************************************************************/
    2720             : 
    2721             : template <class T> class GDALGeneric3x3RasterBand : public GDALRasterBand
    2722             : {
    2723             :     friend class GDALGeneric3x3Dataset<T>;
    2724             :     int bSrcHasNoData;
    2725             :     T fSrcNoDataValue;
    2726             :     int bIsSrcNoDataNan;
    2727             :     GDALDataType eReadDT;
    2728             : 
    2729             :     void InitWithNoData(void *pImage);
    2730             : 
    2731             :   public:
    2732             :     GDALGeneric3x3RasterBand(GDALGeneric3x3Dataset<T> *poDSIn,
    2733             :                              GDALDataType eDstDataType);
    2734             : 
    2735             :     virtual CPLErr IReadBlock(int, int, void *) override;
    2736             :     virtual double GetNoDataValue(int *pbHasNoData) override;
    2737             : };
    2738             : 
    2739             : template <class T>
    2740           4 : GDALGeneric3x3Dataset<T>::GDALGeneric3x3Dataset(
    2741             :     GDALDatasetH hSrcDSIn, GDALRasterBandH hSrcBandIn,
    2742             :     GDALDataType eDstDataType, int bDstHasNoDataIn, double dfDstNoDataValueIn,
    2743             :     typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlgIn, void *pAlgDataIn,
    2744             :     bool bComputeAtEdgesIn)
    2745             :     : pfnAlg(pfnAlgIn), pAlgData(pAlgDataIn), hSrcDS(hSrcDSIn),
    2746             :       hSrcBand(hSrcBandIn), bDstHasNoData(bDstHasNoDataIn),
    2747             :       dfDstNoDataValue(dfDstNoDataValueIn), nCurLine(-1),
    2748           4 :       bComputeAtEdges(bComputeAtEdgesIn)
    2749             : {
    2750           4 :     CPLAssert(eDstDataType == GDT_Byte || eDstDataType == GDT_Float32);
    2751             : 
    2752           4 :     nRasterXSize = GDALGetRasterXSize(hSrcDS);
    2753           4 :     nRasterYSize = GDALGetRasterYSize(hSrcDS);
    2754             : 
    2755           4 :     SetBand(1, new GDALGeneric3x3RasterBand<T>(this, eDstDataType));
    2756             : 
    2757           4 :     apafSourceBuf[0] =
    2758           4 :         static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
    2759           4 :     apafSourceBuf[1] =
    2760           4 :         static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
    2761           4 :     apafSourceBuf[2] =
    2762           4 :         static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
    2763           4 : }
    2764             : 
    2765           8 : template <class T> GDALGeneric3x3Dataset<T>::~GDALGeneric3x3Dataset()
    2766             : {
    2767           4 :     CPLFree(apafSourceBuf[0]);
    2768           4 :     CPLFree(apafSourceBuf[1]);
    2769           4 :     CPLFree(apafSourceBuf[2]);
    2770          12 : }
    2771             : 
    2772             : template <class T>
    2773           6 : CPLErr GDALGeneric3x3Dataset<T>::GetGeoTransform(double *padfGeoTransform)
    2774             : {
    2775           6 :     return GDALGetGeoTransform(hSrcDS, padfGeoTransform);
    2776             : }
    2777             : 
    2778             : template <class T>
    2779           7 : const OGRSpatialReference *GDALGeneric3x3Dataset<T>::GetSpatialRef() const
    2780             : {
    2781           7 :     return GDALDataset::FromHandle(hSrcDS)->GetSpatialRef();
    2782             : }
    2783             : 
    2784             : template <class T>
    2785           4 : GDALGeneric3x3RasterBand<T>::GDALGeneric3x3RasterBand(
    2786             :     GDALGeneric3x3Dataset<T> *poDSIn, GDALDataType eDstDataType)
    2787             :     : bSrcHasNoData(FALSE), fSrcNoDataValue(0), bIsSrcNoDataNan(FALSE),
    2788           4 :       eReadDT(GDT_Unknown)
    2789             : {
    2790           4 :     poDS = poDSIn;
    2791           4 :     nBand = 1;
    2792           4 :     eDataType = eDstDataType;
    2793           4 :     nBlockXSize = poDS->GetRasterXSize();
    2794           4 :     nBlockYSize = 1;
    2795             : 
    2796             :     const double dfNoDataValue =
    2797           4 :         GDALGetRasterNoDataValue(poDSIn->hSrcBand, &bSrcHasNoData);
    2798             :     if (std::numeric_limits<T>::is_integer)
    2799             :     {
    2800           3 :         eReadDT = GDT_Int32;
    2801           3 :         if (bSrcHasNoData)
    2802             :         {
    2803           3 :             GDALDataType eSrcDT = GDALGetRasterDataType(poDSIn->hSrcBand);
    2804           3 :             CPLAssert(eSrcDT == GDT_Byte || eSrcDT == GDT_UInt16 ||
    2805             :                       eSrcDT == GDT_Int16);
    2806           3 :             const int nMinVal = (eSrcDT == GDT_Byte)     ? 0
    2807             :                                 : (eSrcDT == GDT_UInt16) ? 0
    2808             :                                                          : -32768;
    2809           3 :             const int nMaxVal = (eSrcDT == GDT_Byte)     ? 255
    2810             :                                 : (eSrcDT == GDT_UInt16) ? 65535
    2811             :                                                          : 32767;
    2812             : 
    2813           3 :             if (fabs(dfNoDataValue - floor(dfNoDataValue + 0.5)) < 1e-2 &&
    2814           3 :                 dfNoDataValue >= nMinVal && dfNoDataValue <= nMaxVal)
    2815             :             {
    2816           3 :                 fSrcNoDataValue = static_cast<T>(floor(dfNoDataValue + 0.5));
    2817             :             }
    2818             :             else
    2819             :             {
    2820           0 :                 bSrcHasNoData = FALSE;
    2821             :             }
    2822             :         }
    2823             :     }
    2824             :     else
    2825             :     {
    2826           1 :         eReadDT = GDT_Float32;
    2827           1 :         fSrcNoDataValue = static_cast<T>(dfNoDataValue);
    2828           1 :         bIsSrcNoDataNan = bSrcHasNoData && CPLIsNan(dfNoDataValue);
    2829             :     }
    2830           4 : }
    2831             : 
    2832             : template <class T>
    2833           6 : void GDALGeneric3x3RasterBand<T>::InitWithNoData(void *pImage)
    2834             : {
    2835           6 :     auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
    2836           6 :     if (eDataType == GDT_Byte)
    2837             :     {
    2838         732 :         for (int j = 0; j < nBlockXSize; j++)
    2839         726 :             static_cast<GByte *>(pImage)[j] =
    2840         726 :                 static_cast<GByte>(poGDS->dfDstNoDataValue);
    2841             :     }
    2842             :     else
    2843             :     {
    2844           0 :         for (int j = 0; j < nBlockXSize; j++)
    2845           0 :             static_cast<float *>(pImage)[j] =
    2846           0 :                 static_cast<float>(poGDS->dfDstNoDataValue);
    2847             :     }
    2848           6 : }
    2849             : 
    2850             : template <class T>
    2851         484 : CPLErr GDALGeneric3x3RasterBand<T>::IReadBlock(int /*nBlockXOff*/,
    2852             :                                                int nBlockYOff, void *pImage)
    2853             : {
    2854         484 :     auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
    2855             : 
    2856         484 :     if (poGDS->bComputeAtEdges && nRasterXSize >= 2 && nRasterYSize >= 2)
    2857             :     {
    2858         121 :         if (nBlockYOff == 0)
    2859             :         {
    2860           3 :             for (int i = 0; i < 2; i++)
    2861             :             {
    2862           4 :                 CPLErr eErr = GDALRasterIO(
    2863             :                     poGDS->hSrcBand, GF_Read, 0, i, nBlockXSize, 1,
    2864           2 :                     poGDS->apafSourceBuf[i + 1], nBlockXSize, 1, eReadDT, 0, 0);
    2865           2 :                 if (eErr != CE_None)
    2866             :                 {
    2867           0 :                     InitWithNoData(pImage);
    2868           0 :                     return eErr;
    2869             :                 }
    2870             :             }
    2871           1 :             poGDS->nCurLine = 0;
    2872             : 
    2873         122 :             for (int j = 0; j < nRasterXSize; j++)
    2874             :             {
    2875         121 :                 int jmin = (j == 0) ? j : j - 1;
    2876         121 :                 int jmax = (j == nRasterXSize - 1) ? j : j + 1;
    2877             : 
    2878         484 :                 T afWin[9] = {INTERPOL(poGDS->apafSourceBuf[1][jmin],
    2879         121 :                                        poGDS->apafSourceBuf[2][jmin],
    2880             :                                        bSrcHasNoData, fSrcNoDataValue),
    2881         121 :                               INTERPOL(poGDS->apafSourceBuf[1][j],
    2882         121 :                                        poGDS->apafSourceBuf[2][j],
    2883             :                                        bSrcHasNoData, fSrcNoDataValue),
    2884         121 :                               INTERPOL(poGDS->apafSourceBuf[1][jmax],
    2885         121 :                                        poGDS->apafSourceBuf[2][jmax],
    2886             :                                        bSrcHasNoData, fSrcNoDataValue),
    2887         121 :                               poGDS->apafSourceBuf[1][jmin],
    2888         121 :                               poGDS->apafSourceBuf[1][j],
    2889         121 :                               poGDS->apafSourceBuf[1][jmax],
    2890         121 :                               poGDS->apafSourceBuf[2][jmin],
    2891         121 :                               poGDS->apafSourceBuf[2][j],
    2892         121 :                               poGDS->apafSourceBuf[2][jmax]};
    2893             : 
    2894         121 :                 const float fVal = ComputeVal(
    2895         121 :                     CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
    2896         121 :                     CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
    2897         121 :                     static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
    2898         121 :                     poGDS->pAlgData, poGDS->bComputeAtEdges);
    2899             : 
    2900         121 :                 if (eDataType == GDT_Byte)
    2901         121 :                     static_cast<GByte *>(pImage)[j] =
    2902         121 :                         static_cast<GByte>(fVal + 0.5);
    2903             :                 else
    2904           0 :                     static_cast<float *>(pImage)[j] = fVal;
    2905             :             }
    2906             : 
    2907           1 :             return CE_None;
    2908             :         }
    2909         120 :         else if (nBlockYOff == nRasterYSize - 1)
    2910             :         {
    2911           1 :             if (poGDS->nCurLine != nRasterYSize - 2)
    2912             :             {
    2913           0 :                 for (int i = 0; i < 2; i++)
    2914             :                 {
    2915           0 :                     CPLErr eErr = GDALRasterIO(
    2916           0 :                         poGDS->hSrcBand, GF_Read, 0, nRasterYSize - 2 + i,
    2917           0 :                         nBlockXSize, 1, poGDS->apafSourceBuf[i + 1],
    2918             :                         nBlockXSize, 1, eReadDT, 0, 0);
    2919           0 :                     if (eErr != CE_None)
    2920             :                     {
    2921           0 :                         InitWithNoData(pImage);
    2922           0 :                         return eErr;
    2923             :                     }
    2924             :                 }
    2925             :             }
    2926             : 
    2927         122 :             for (int j = 0; j < nRasterXSize; j++)
    2928             :             {
    2929         121 :                 int jmin = (j == 0) ? j : j - 1;
    2930         121 :                 int jmax = (j == nRasterXSize - 1) ? j : j + 1;
    2931             : 
    2932         484 :                 T afWin[9] = {poGDS->apafSourceBuf[1][jmin],
    2933         121 :                               poGDS->apafSourceBuf[1][j],
    2934         121 :                               poGDS->apafSourceBuf[1][jmax],
    2935         121 :                               poGDS->apafSourceBuf[2][jmin],
    2936         121 :                               poGDS->apafSourceBuf[2][j],
    2937         121 :                               poGDS->apafSourceBuf[2][jmax],
    2938         121 :                               INTERPOL(poGDS->apafSourceBuf[2][jmin],
    2939         121 :                                        poGDS->apafSourceBuf[1][jmin],
    2940             :                                        bSrcHasNoData, fSrcNoDataValue),
    2941         121 :                               INTERPOL(poGDS->apafSourceBuf[2][j],
    2942         121 :                                        poGDS->apafSourceBuf[1][j],
    2943             :                                        bSrcHasNoData, fSrcNoDataValue),
    2944         121 :                               INTERPOL(poGDS->apafSourceBuf[2][jmax],
    2945         121 :                                        poGDS->apafSourceBuf[1][jmax],
    2946             :                                        bSrcHasNoData, fSrcNoDataValue)};
    2947             : 
    2948         121 :                 const float fVal = ComputeVal(
    2949         121 :                     CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
    2950         121 :                     CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
    2951         121 :                     static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
    2952         121 :                     poGDS->pAlgData, poGDS->bComputeAtEdges);
    2953             : 
    2954         121 :                 if (eDataType == GDT_Byte)
    2955         121 :                     static_cast<GByte *>(pImage)[j] =
    2956         121 :                         static_cast<GByte>(fVal + 0.5);
    2957             :                 else
    2958           0 :                     static_cast<float *>(pImage)[j] = fVal;
    2959             :             }
    2960             : 
    2961           1 :             return CE_None;
    2962         119 :         }
    2963             :     }
    2964         363 :     else if (nBlockYOff == 0 || nBlockYOff == nRasterYSize - 1)
    2965             :     {
    2966           6 :         InitWithNoData(pImage);
    2967           6 :         return CE_None;
    2968             :     }
    2969             : 
    2970         476 :     if (poGDS->nCurLine != nBlockYOff)
    2971             :     {
    2972         476 :         if (poGDS->nCurLine + 1 == nBlockYOff)
    2973             :         {
    2974         473 :             T *pafTmp = poGDS->apafSourceBuf[0];
    2975         473 :             poGDS->apafSourceBuf[0] = poGDS->apafSourceBuf[1];
    2976         473 :             poGDS->apafSourceBuf[1] = poGDS->apafSourceBuf[2];
    2977         473 :             poGDS->apafSourceBuf[2] = pafTmp;
    2978             : 
    2979         946 :             CPLErr eErr = GDALRasterIO(
    2980             :                 poGDS->hSrcBand, GF_Read, 0, nBlockYOff + 1, nBlockXSize, 1,
    2981         473 :                 poGDS->apafSourceBuf[2], nBlockXSize, 1, eReadDT, 0, 0);
    2982             : 
    2983         473 :             if (eErr != CE_None)
    2984             :             {
    2985           0 :                 InitWithNoData(pImage);
    2986           0 :                 return eErr;
    2987             :             }
    2988             :         }
    2989             :         else
    2990             :         {
    2991          12 :             for (int i = 0; i < 3; i++)
    2992             :             {
    2993          18 :                 const CPLErr eErr = GDALRasterIO(
    2994           9 :                     poGDS->hSrcBand, GF_Read, 0, nBlockYOff + i - 1,
    2995           9 :                     nBlockXSize, 1, poGDS->apafSourceBuf[i], nBlockXSize, 1,
    2996             :                     eReadDT, 0, 0);
    2997           9 :                 if (eErr != CE_None)
    2998             :                 {
    2999           0 :                     InitWithNoData(pImage);
    3000           0 :                     return eErr;
    3001             :                 }
    3002             :             }
    3003             :         }
    3004             : 
    3005         476 :         poGDS->nCurLine = nBlockYOff;
    3006             :     }
    3007             : 
    3008         476 :     if (poGDS->bComputeAtEdges && nRasterXSize >= 2)
    3009             :     {
    3010         119 :         int j = 0;
    3011         357 :         T afWin[9] = {
    3012         119 :             INTERPOL(poGDS->apafSourceBuf[0][j], poGDS->apafSourceBuf[0][j + 1],
    3013             :                      bSrcHasNoData, fSrcNoDataValue),
    3014         119 :             poGDS->apafSourceBuf[0][j],
    3015         119 :             poGDS->apafSourceBuf[0][j + 1],
    3016         119 :             INTERPOL(poGDS->apafSourceBuf[1][j], poGDS->apafSourceBuf[1][j + 1],
    3017             :                      bSrcHasNoData, fSrcNoDataValue),
    3018         119 :             poGDS->apafSourceBuf[1][j],
    3019         119 :             poGDS->apafSourceBuf[1][j + 1],
    3020         119 :             INTERPOL(poGDS->apafSourceBuf[2][j], poGDS->apafSourceBuf[2][j + 1],
    3021             :                      bSrcHasNoData, fSrcNoDataValue),
    3022         119 :             poGDS->apafSourceBuf[2][j],
    3023         119 :             poGDS->apafSourceBuf[2][j + 1]};
    3024             : 
    3025             :         {
    3026         119 :             const float fVal = ComputeVal(
    3027         119 :                 CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
    3028         119 :                 CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
    3029         119 :                 static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
    3030         119 :                 poGDS->pAlgData, poGDS->bComputeAtEdges);
    3031             : 
    3032         119 :             if (eDataType == GDT_Byte)
    3033         119 :                 static_cast<GByte *>(pImage)[j] =
    3034         119 :                     static_cast<GByte>(fVal + 0.5);
    3035             :             else
    3036           0 :                 static_cast<float *>(pImage)[j] = fVal;
    3037             :         }
    3038             : 
    3039         119 :         j = nRasterXSize - 1;
    3040             : 
    3041         119 :         afWin[0] = poGDS->apafSourceBuf[0][j - 1];
    3042         119 :         afWin[1] = poGDS->apafSourceBuf[0][j];
    3043         119 :         afWin[2] =
    3044         119 :             INTERPOL(poGDS->apafSourceBuf[0][j], poGDS->apafSourceBuf[0][j - 1],
    3045             :                      bSrcHasNoData, fSrcNoDataValue);
    3046         119 :         afWin[3] = poGDS->apafSourceBuf[1][j - 1];
    3047         119 :         afWin[4] = poGDS->apafSourceBuf[1][j];
    3048         119 :         afWin[5] =
    3049         119 :             INTERPOL(poGDS->apafSourceBuf[1][j], poGDS->apafSourceBuf[1][j - 1],
    3050             :                      bSrcHasNoData, fSrcNoDataValue);
    3051         119 :         afWin[6] = poGDS->apafSourceBuf[2][j - 1];
    3052         119 :         afWin[7] = poGDS->apafSourceBuf[2][j];
    3053         119 :         afWin[8] =
    3054         119 :             INTERPOL(poGDS->apafSourceBuf[2][j], poGDS->apafSourceBuf[2][j - 1],
    3055             :                      bSrcHasNoData, fSrcNoDataValue);
    3056             : 
    3057             :         const float fVal =
    3058         119 :             ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
    3059         119 :                        CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
    3060         119 :                        static_cast<float>(poGDS->dfDstNoDataValue),
    3061         119 :                        poGDS->pfnAlg, poGDS->pAlgData, poGDS->bComputeAtEdges);
    3062             : 
    3063         119 :         if (eDataType == GDT_Byte)
    3064         119 :             static_cast<GByte *>(pImage)[j] = static_cast<GByte>(fVal + 0.5);
    3065             :         else
    3066         119 :             static_cast<float *>(pImage)[j] = fVal;
    3067             :     }
    3068             :     else
    3069             :     {
    3070         357 :         if (eDataType == GDT_Byte)
    3071             :         {
    3072         357 :             static_cast<GByte *>(pImage)[0] =
    3073         357 :                 static_cast<GByte>(poGDS->dfDstNoDataValue);
    3074         357 :             if (nBlockXSize > 1)
    3075         357 :                 static_cast<GByte *>(pImage)[nBlockXSize - 1] =
    3076         357 :                     static_cast<GByte>(poGDS->dfDstNoDataValue);
    3077             :         }
    3078             :         else
    3079             :         {
    3080           0 :             static_cast<float *>(pImage)[0] =
    3081           0 :                 static_cast<float>(poGDS->dfDstNoDataValue);
    3082           0 :             if (nBlockXSize > 1)
    3083           0 :                 static_cast<float *>(pImage)[nBlockXSize - 1] =
    3084           0 :                     static_cast<float>(poGDS->dfDstNoDataValue);
    3085             :         }
    3086             :     }
    3087             : 
    3088       57120 :     for (int j = 1; j < nBlockXSize - 1; j++)
    3089             :     {
    3090       56644 :         T afWin[9] = {
    3091       56644 :             poGDS->apafSourceBuf[0][j - 1], poGDS->apafSourceBuf[0][j],
    3092       56644 :             poGDS->apafSourceBuf[0][j + 1], poGDS->apafSourceBuf[1][j - 1],
    3093       56644 :             poGDS->apafSourceBuf[1][j],     poGDS->apafSourceBuf[1][j + 1],
    3094       56644 :             poGDS->apafSourceBuf[2][j - 1], poGDS->apafSourceBuf[2][j],
    3095       56644 :             poGDS->apafSourceBuf[2][j + 1]};
    3096             : 
    3097             :         const float fVal =
    3098       56644 :             ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
    3099       56644 :                        CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
    3100       56644 :                        static_cast<float>(poGDS->dfDstNoDataValue),
    3101       56644 :                        poGDS->pfnAlg, poGDS->pAlgData, poGDS->bComputeAtEdges);
    3102             : 
    3103       56644 :         if (eDataType == GDT_Byte)
    3104       56644 :             static_cast<GByte *>(pImage)[j] = static_cast<GByte>(fVal + 0.5);
    3105             :         else
    3106           0 :             static_cast<float *>(pImage)[j] = fVal;
    3107             :     }
    3108             : 
    3109         476 :     return CE_None;
    3110             : }
    3111             : 
    3112             : template <class T>
    3113          16 : double GDALGeneric3x3RasterBand<T>::GetNoDataValue(int *pbHasNoData)
    3114             : {
    3115          16 :     auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
    3116          16 :     if (pbHasNoData)
    3117          12 :         *pbHasNoData = poGDS->bDstHasNoData;
    3118          16 :     return poGDS->dfDstNoDataValue;
    3119             : }
    3120             : 
    3121             : /************************************************************************/
    3122             : /*                            GetAlgorithm()                            */
    3123             : /************************************************************************/
    3124             : 
    3125             : typedef enum
    3126             : {
    3127             :     INVALID,
    3128             :     HILL_SHADE,
    3129             :     SLOPE,
    3130             :     ASPECT,
    3131             :     COLOR_RELIEF,
    3132             :     TRI,
    3133             :     TPI,
    3134             :     ROUGHNESS
    3135             : } Algorithm;
    3136             : 
    3137          81 : static Algorithm GetAlgorithm(const char *pszProcessing)
    3138             : {
    3139          81 :     if (EQUAL(pszProcessing, "shade") || EQUAL(pszProcessing, "hillshade"))
    3140             :     {
    3141          40 :         return HILL_SHADE;
    3142             :     }
    3143          41 :     else if (EQUAL(pszProcessing, "slope"))
    3144             :     {
    3145           3 :         return SLOPE;
    3146             :     }
    3147          38 :     else if (EQUAL(pszProcessing, "aspect"))
    3148             :     {
    3149           3 :         return ASPECT;
    3150             :     }
    3151          35 :     else if (EQUAL(pszProcessing, "color-relief"))
    3152             :     {
    3153          31 :         return COLOR_RELIEF;
    3154             :     }
    3155           4 :     else if (EQUAL(pszProcessing, "TRI"))
    3156             :     {
    3157           2 :         return TRI;
    3158             :     }
    3159           2 :     else if (EQUAL(pszProcessing, "TPI"))
    3160             :     {
    3161           1 :         return TPI;
    3162             :     }
    3163           1 :     else if (EQUAL(pszProcessing, "roughness"))
    3164             :     {
    3165           1 :         return ROUGHNESS;
    3166             :     }
    3167             :     else
    3168             :     {
    3169           0 :         return INVALID;
    3170             :     }
    3171             : }
    3172             : 
    3173             : /************************************************************************/
    3174             : /*                            GDALDEMProcessing()                       */
    3175             : /************************************************************************/
    3176             : 
    3177             : /**
    3178             :  * Apply a DEM processing.
    3179             :  *
    3180             :  * This is the equivalent of the <a href="/programs/gdaldem.html">gdaldem</a>
    3181             :  * utility.
    3182             :  *
    3183             :  * GDALDEMProcessingOptions* must be allocated and freed with
    3184             :  * GDALDEMProcessingOptionsNew() and GDALDEMProcessingOptionsFree()
    3185             :  * respectively.
    3186             :  *
    3187             :  * @param pszDest the destination dataset path.
    3188             :  * @param hSrcDataset the source dataset handle.
    3189             :  * @param pszProcessing the processing to apply (one of "hillshade", "slope",
    3190             :  * "aspect", "color-relief", "TRI", "TPI", "Roughness")
    3191             :  * @param pszColorFilename color file (mandatory for "color-relief" processing,
    3192             :  * should be NULL otherwise)
    3193             :  * @param psOptionsIn the options struct returned by
    3194             :  * GDALDEMProcessingOptionsNew() or NULL.
    3195             :  * @param pbUsageError pointer to a integer output variable to store if any
    3196             :  * usage error has occurred or NULL.
    3197             :  * @return the output dataset (new dataset that must be closed using
    3198             :  * GDALClose()) or NULL in case of error.
    3199             :  *
    3200             :  * @since GDAL 2.1
    3201             :  */
    3202             : 
    3203          60 : GDALDatasetH GDALDEMProcessing(const char *pszDest, GDALDatasetH hSrcDataset,
    3204             :                                const char *pszProcessing,
    3205             :                                const char *pszColorFilename,
    3206             :                                const GDALDEMProcessingOptions *psOptionsIn,
    3207             :                                int *pbUsageError)
    3208             : {
    3209          60 :     if (hSrcDataset == nullptr)
    3210             :     {
    3211           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
    3212             : 
    3213           0 :         if (pbUsageError)
    3214           0 :             *pbUsageError = TRUE;
    3215           0 :         return nullptr;
    3216             :     }
    3217          60 :     if (pszDest == nullptr)
    3218             :     {
    3219           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
    3220             : 
    3221           0 :         if (pbUsageError)
    3222           0 :             *pbUsageError = TRUE;
    3223           0 :         return nullptr;
    3224             :     }
    3225          60 :     if (pszProcessing == nullptr)
    3226             :     {
    3227           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
    3228             : 
    3229           0 :         if (pbUsageError)
    3230           0 :             *pbUsageError = TRUE;
    3231           0 :         return nullptr;
    3232             :     }
    3233             : 
    3234          60 :     Algorithm eUtilityMode = GetAlgorithm(pszProcessing);
    3235          60 :     if (eUtilityMode == INVALID)
    3236             :     {
    3237           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Invalid processing");
    3238           0 :         if (pbUsageError)
    3239           0 :             *pbUsageError = TRUE;
    3240           0 :         return nullptr;
    3241             :     }
    3242             : 
    3243          60 :     if (eUtilityMode == COLOR_RELIEF && pszColorFilename == nullptr)
    3244             :     {
    3245           0 :         CPLError(CE_Failure, CPLE_AppDefined, "pszColorFilename == NULL.");
    3246             : 
    3247           0 :         if (pbUsageError)
    3248           0 :             *pbUsageError = TRUE;
    3249           0 :         return nullptr;
    3250             :     }
    3251          60 :     else if (eUtilityMode != COLOR_RELIEF && pszColorFilename != nullptr)
    3252             :     {
    3253           0 :         CPLError(CE_Failure, CPLE_AppDefined, "pszColorFilename != NULL.");
    3254             : 
    3255           0 :         if (pbUsageError)
    3256           0 :             *pbUsageError = TRUE;
    3257           0 :         return nullptr;
    3258             :     }
    3259             : 
    3260          60 :     if (psOptionsIn && psOptionsIn->bCombined && eUtilityMode != HILL_SHADE)
    3261             :     {
    3262           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3263             :                  "-combined can only be used with hillshade");
    3264             : 
    3265           0 :         if (pbUsageError)
    3266           0 :             *pbUsageError = TRUE;
    3267           0 :         return nullptr;
    3268             :     }
    3269             : 
    3270          60 :     if (psOptionsIn && psOptionsIn->bIgor && eUtilityMode != HILL_SHADE)
    3271             :     {
    3272           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3273             :                  "-igor can only be used with hillshade");
    3274             : 
    3275           0 :         if (pbUsageError)
    3276           0 :             *pbUsageError = TRUE;
    3277           0 :         return nullptr;
    3278             :     }
    3279             : 
    3280          60 :     if (psOptionsIn && psOptionsIn->bMultiDirectional &&
    3281             :         eUtilityMode != HILL_SHADE)
    3282             :     {
    3283           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3284             :                  "-multidirectional can only be used with hillshade");
    3285             : 
    3286           0 :         if (pbUsageError)
    3287           0 :             *pbUsageError = TRUE;
    3288           0 :         return nullptr;
    3289             :     }
    3290             : 
    3291          60 :     GDALDEMProcessingOptions *psOptionsToFree = nullptr;
    3292          60 :     const GDALDEMProcessingOptions *psOptions = psOptionsIn;
    3293          60 :     if (!psOptions)
    3294             :     {
    3295           0 :         psOptionsToFree = GDALDEMProcessingOptionsNew(nullptr, nullptr);
    3296           0 :         psOptions = psOptionsToFree;
    3297             :     }
    3298             : 
    3299          60 :     if (psOptions->bGradientAlgSpecified &&
    3300           2 :         !(eUtilityMode == HILL_SHADE || eUtilityMode == SLOPE ||
    3301             :           eUtilityMode == ASPECT))
    3302             :     {
    3303           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    3304             :                  "This value of -alg is only value for hillshade, slope or "
    3305             :                  "aspect processing");
    3306           0 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3307           0 :         return nullptr;
    3308             :     }
    3309             : 
    3310          60 :     if (psOptions->bTRIAlgSpecified && !(eUtilityMode == TRI))
    3311             :     {
    3312           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    3313             :                  "This value of -alg is only value for TRI processing");
    3314           0 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3315           0 :         return nullptr;
    3316             :     }
    3317             : 
    3318          60 :     double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    3319             : 
    3320          60 :     const int nXSize = GDALGetRasterXSize(hSrcDataset);
    3321          60 :     const int nYSize = GDALGetRasterYSize(hSrcDataset);
    3322             : 
    3323         120 :     if (psOptions->nBand <= 0 ||
    3324          60 :         psOptions->nBand > GDALGetRasterCount(hSrcDataset))
    3325             :     {
    3326           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Unable to fetch band #%d",
    3327           0 :                  psOptions->nBand);
    3328           0 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3329           0 :         return nullptr;
    3330             :     }
    3331          60 :     GDALRasterBandH hSrcBand = GDALGetRasterBand(hSrcDataset, psOptions->nBand);
    3332             : 
    3333          60 :     GDALGetGeoTransform(hSrcDataset, adfGeoTransform);
    3334             : 
    3335         120 :     CPLString osFormat;
    3336          60 :     if (psOptions->pszFormat == nullptr)
    3337             :     {
    3338          14 :         osFormat = GetOutputDriverForRaster(pszDest);
    3339          14 :         if (osFormat.empty())
    3340             :         {
    3341           0 :             GDALDEMProcessingOptionsFree(psOptionsToFree);
    3342           0 :             return nullptr;
    3343             :         }
    3344             :     }
    3345             :     else
    3346             :     {
    3347          46 :         osFormat = psOptions->pszFormat;
    3348             :     }
    3349          60 :     GDALDriverH hDriver = GDALGetDriverByName(osFormat);
    3350         120 :     if (hDriver == nullptr ||
    3351          60 :         (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr &&
    3352           5 :          GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) ==
    3353             :              nullptr))
    3354             :     {
    3355           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3356             :                  "Output driver `%s' not recognised to have output support.",
    3357             :                  osFormat.c_str());
    3358           0 :         fprintf(stderr, "The following format drivers are configured\n"
    3359             :                         "and support output:\n");
    3360             : 
    3361           0 :         for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
    3362             :         {
    3363           0 :             hDriver = GDALGetDriver(iDr);
    3364             : 
    3365           0 :             if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
    3366           0 :                     nullptr &&
    3367           0 :                 (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
    3368           0 :                      nullptr ||
    3369           0 :                  GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
    3370             :                      nullptr))
    3371             :             {
    3372           0 :                 fprintf(stderr, "  %s: %s\n", GDALGetDriverShortName(hDriver),
    3373             :                         GDALGetDriverLongName(hDriver));
    3374             :             }
    3375             :         }
    3376           0 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3377           0 :         return nullptr;
    3378             :     }
    3379             : 
    3380          60 :     double dfDstNoDataValue = 0.0;
    3381          60 :     bool bDstHasNoData = false;
    3382          60 :     void *pData = nullptr;
    3383          60 :     GDALGeneric3x3ProcessingAlg<float>::type pfnAlgFloat = nullptr;
    3384          60 :     GDALGeneric3x3ProcessingAlg<GInt32>::type pfnAlgInt32 = nullptr;
    3385             :     GDALGeneric3x3ProcessingAlg_multisample<GInt32>::type
    3386          60 :         pfnAlgInt32_multisample = nullptr;
    3387             : 
    3388          60 :     if (eUtilityMode == HILL_SHADE && psOptions->bMultiDirectional)
    3389             :     {
    3390           2 :         dfDstNoDataValue = 0;
    3391           2 :         bDstHasNoData = true;
    3392           4 :         pData = GDALCreateHillshadeMultiDirectionalData(
    3393           2 :             adfGeoTransform, psOptions->z, psOptions->scale, psOptions->alt,
    3394           2 :             psOptions->eGradientAlg);
    3395           2 :         if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
    3396             :         {
    3397           1 :             pfnAlgFloat = GDALHillshadeMultiDirectionalAlg<
    3398             :                 float, GradientAlg::ZEVENBERGEN_THORNE>;
    3399           1 :             pfnAlgInt32 = GDALHillshadeMultiDirectionalAlg<
    3400             :                 GInt32, GradientAlg::ZEVENBERGEN_THORNE>;
    3401             :         }
    3402             :         else
    3403             :         {
    3404           1 :             pfnAlgFloat =
    3405             :                 GDALHillshadeMultiDirectionalAlg<float, GradientAlg::HORN>;
    3406           1 :             pfnAlgInt32 =
    3407             :                 GDALHillshadeMultiDirectionalAlg<GInt32, GradientAlg::HORN>;
    3408             :         }
    3409             :     }
    3410          58 :     else if (eUtilityMode == HILL_SHADE)
    3411             :     {
    3412          31 :         dfDstNoDataValue = 0;
    3413          31 :         bDstHasNoData = true;
    3414          62 :         pData = GDALCreateHillshadeData(adfGeoTransform, psOptions->z,
    3415          31 :                                         psOptions->scale, psOptions->alt,
    3416          31 :                                         psOptions->az, psOptions->eGradientAlg);
    3417          31 :         if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
    3418             :         {
    3419           3 :             if (psOptions->bCombined)
    3420             :             {
    3421           1 :                 pfnAlgFloat =
    3422             :                     GDALHillshadeCombinedAlg<float,
    3423             :                                              GradientAlg::ZEVENBERGEN_THORNE>;
    3424           1 :                 pfnAlgInt32 =
    3425             :                     GDALHillshadeCombinedAlg<GInt32,
    3426             :                                              GradientAlg::ZEVENBERGEN_THORNE>;
    3427             :             }
    3428           2 :             else if (psOptions->bIgor)
    3429             :             {
    3430           1 :                 pfnAlgFloat =
    3431             :                     GDALHillshadeIgorAlg<float,
    3432             :                                          GradientAlg::ZEVENBERGEN_THORNE>;
    3433           1 :                 pfnAlgInt32 =
    3434             :                     GDALHillshadeIgorAlg<GInt32,
    3435             :                                          GradientAlg::ZEVENBERGEN_THORNE>;
    3436             :             }
    3437             :             else
    3438             :             {
    3439           1 :                 pfnAlgFloat =
    3440             :                     GDALHillshadeAlg<float, GradientAlg::ZEVENBERGEN_THORNE>;
    3441           1 :                 pfnAlgInt32 =
    3442             :                     GDALHillshadeAlg<GInt32, GradientAlg::ZEVENBERGEN_THORNE>;
    3443             :             }
    3444             :         }
    3445             :         else
    3446             :         {
    3447          28 :             if (psOptions->bCombined)
    3448             :             {
    3449           2 :                 pfnAlgFloat =
    3450             :                     GDALHillshadeCombinedAlg<float, GradientAlg::HORN>;
    3451           2 :                 pfnAlgInt32 =
    3452             :                     GDALHillshadeCombinedAlg<GInt32, GradientAlg::HORN>;
    3453             :             }
    3454          26 :             else if (psOptions->bIgor)
    3455             :             {
    3456           1 :                 pfnAlgFloat = GDALHillshadeIgorAlg<float, GradientAlg::HORN>;
    3457           1 :                 pfnAlgInt32 = GDALHillshadeIgorAlg<GInt32, GradientAlg::HORN>;
    3458             :             }
    3459             :             else
    3460             :             {
    3461          25 :                 if (adfGeoTransform[1] == -adfGeoTransform[5])
    3462             :                 {
    3463          25 :                     pfnAlgFloat = GDALHillshadeAlg_same_res<float>;
    3464          25 :                     pfnAlgInt32 = GDALHillshadeAlg_same_res<GInt32>;
    3465             : #ifdef HAVE_16_SSE_REG
    3466          25 :                     pfnAlgInt32_multisample =
    3467             :                         GDALHillshadeAlg_same_res_multisample<GInt32>;
    3468             : #endif
    3469             :                 }
    3470             :                 else
    3471             :                 {
    3472           0 :                     pfnAlgFloat = GDALHillshadeAlg<float, GradientAlg::HORN>;
    3473           0 :                     pfnAlgInt32 = GDALHillshadeAlg<GInt32, GradientAlg::HORN>;
    3474             :                 }
    3475             :             }
    3476             :         }
    3477             :     }
    3478          27 :     else if (eUtilityMode == SLOPE)
    3479             :     {
    3480           2 :         dfDstNoDataValue = -9999;
    3481           2 :         bDstHasNoData = true;
    3482             : 
    3483           4 :         pData = GDALCreateSlopeData(adfGeoTransform, psOptions->scale,
    3484           2 :                                     psOptions->slopeFormat);
    3485           2 :         if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
    3486             :         {
    3487           1 :             pfnAlgFloat = GDALSlopeZevenbergenThorneAlg<float>;
    3488           1 :             pfnAlgInt32 = GDALSlopeZevenbergenThorneAlg<GInt32>;
    3489             :         }
    3490             :         else
    3491             :         {
    3492           1 :             pfnAlgFloat = GDALSlopeHornAlg<float>;
    3493           1 :             pfnAlgInt32 = GDALSlopeHornAlg<GInt32>;
    3494             :         }
    3495             :     }
    3496             : 
    3497          25 :     else if (eUtilityMode == ASPECT)
    3498             :     {
    3499           2 :         if (!psOptions->bZeroForFlat)
    3500             :         {
    3501           2 :             dfDstNoDataValue = -9999;
    3502           2 :             bDstHasNoData = true;
    3503             :         }
    3504             : 
    3505           2 :         pData = GDALCreateAspectData(psOptions->bAngleAsAzimuth);
    3506           2 :         if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
    3507             :         {
    3508           1 :             pfnAlgFloat = GDALAspectZevenbergenThorneAlg<float>;
    3509           1 :             pfnAlgInt32 = GDALAspectZevenbergenThorneAlg<GInt32>;
    3510             :         }
    3511             :         else
    3512             :         {
    3513           1 :             pfnAlgFloat = GDALAspectAlg<float>;
    3514           1 :             pfnAlgInt32 = GDALAspectAlg<GInt32>;
    3515             :         }
    3516             :     }
    3517          23 :     else if (eUtilityMode == TRI)
    3518             :     {
    3519           2 :         dfDstNoDataValue = -9999;
    3520           2 :         bDstHasNoData = true;
    3521           2 :         if (psOptions->eTRIAlg == TRIAlg::WILSON)
    3522             :         {
    3523           1 :             pfnAlgFloat = GDALTRIAlgWilson<float>;
    3524           1 :             pfnAlgInt32 = GDALTRIAlgWilson<GInt32>;
    3525             :         }
    3526             :         else
    3527             :         {
    3528           1 :             pfnAlgFloat = GDALTRIAlgRiley<float>;
    3529           1 :             pfnAlgInt32 = GDALTRIAlgRiley<GInt32>;
    3530             :         }
    3531             :     }
    3532          21 :     else if (eUtilityMode == TPI)
    3533             :     {
    3534           1 :         dfDstNoDataValue = -9999;
    3535           1 :         bDstHasNoData = true;
    3536           1 :         pfnAlgFloat = GDALTPIAlg<float>;
    3537           1 :         pfnAlgInt32 = GDALTPIAlg<GInt32>;
    3538             :     }
    3539          20 :     else if (eUtilityMode == ROUGHNESS)
    3540             :     {
    3541           1 :         dfDstNoDataValue = -9999;
    3542           1 :         bDstHasNoData = true;
    3543           1 :         pfnAlgFloat = GDALRoughnessAlg<float>;
    3544           1 :         pfnAlgInt32 = GDALRoughnessAlg<GInt32>;
    3545             :     }
    3546             : 
    3547          60 :     const GDALDataType eDstDataType =
    3548          27 :         (eUtilityMode == HILL_SHADE || eUtilityMode == COLOR_RELIEF)
    3549          87 :             ? GDT_Byte
    3550             :             : GDT_Float32;
    3551             : 
    3552          60 :     if (EQUAL(osFormat, "VRT"))
    3553             :     {
    3554           3 :         if (eUtilityMode == COLOR_RELIEF)
    3555             :         {
    3556           3 :             GDALGenerateVRTColorRelief(
    3557             :                 pszDest, hSrcDataset, hSrcBand, pszColorFilename,
    3558           3 :                 psOptions->eColorSelectionMode, psOptions->bAddAlpha);
    3559             : 
    3560           3 :             CPLFree(pData);
    3561             : 
    3562           3 :             GDALDEMProcessingOptionsFree(psOptionsToFree);
    3563           3 :             return GDALOpen(pszDest, GA_Update);
    3564             :         }
    3565             :         else
    3566             :         {
    3567           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3568             :                      "VRT driver can only be used with color-relief utility.");
    3569           0 :             GDALDEMProcessingOptionsFree(psOptionsToFree);
    3570           0 :             CPLFree(pData);
    3571           0 :             return nullptr;
    3572             :         }
    3573             :     }
    3574             : 
    3575             :     // We might actually want to always go through the intermediate dataset
    3576          57 :     bool bForceUseIntermediateDataset = false;
    3577             : 
    3578          57 :     GDALProgressFunc pfnProgress = psOptions->pfnProgress;
    3579          57 :     void *pProgressData = psOptions->pProgressData;
    3580             : 
    3581          57 :     if (EQUAL(osFormat, "GTiff"))
    3582             :     {
    3583          14 :         if (!EQUAL(CSLFetchNameValueDef(psOptions->papszCreateOptions,
    3584             :                                         "COMPRESS", "NONE"),
    3585          16 :                    "NONE") &&
    3586           2 :             CPLTestBool(CSLFetchNameValueDef(psOptions->papszCreateOptions,
    3587             :                                              "TILED", "NO")))
    3588             :         {
    3589           1 :             bForceUseIntermediateDataset = true;
    3590             :         }
    3591          13 :         else if (strcmp(pszDest, "/vsistdout/") == 0)
    3592             :         {
    3593           0 :             bForceUseIntermediateDataset = true;
    3594           0 :             pfnProgress = GDALDummyProgress;
    3595           0 :             pProgressData = nullptr;
    3596             :         }
    3597             : #ifdef S_ISFIFO
    3598             :         else
    3599             :         {
    3600             :             VSIStatBufL sStat;
    3601          13 :             if (VSIStatExL(pszDest, &sStat,
    3602          13 :                            VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    3603           0 :                 S_ISFIFO(sStat.st_mode))
    3604             :             {
    3605           0 :                 bForceUseIntermediateDataset = true;
    3606             :             }
    3607             :         }
    3608             : #endif
    3609             :     }
    3610             : 
    3611          57 :     const GDALDataType eSrcDT = GDALGetRasterDataType(hSrcBand);
    3612             : 
    3613         114 :     if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) != nullptr &&
    3614          56 :         ((bForceUseIntermediateDataset ||
    3615          56 :           GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr) &&
    3616           6 :          GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
    3617             :              nullptr))
    3618             :     {
    3619           6 :         GDALDatasetH hIntermediateDataset = nullptr;
    3620             : 
    3621           6 :         if (eUtilityMode == COLOR_RELIEF)
    3622             :         {
    3623             :             GDALColorReliefDataset *poDS = new GDALColorReliefDataset(
    3624             :                 hSrcDataset, hSrcBand, pszColorFilename,
    3625           2 :                 psOptions->eColorSelectionMode, psOptions->bAddAlpha);
    3626           2 :             if (!(poDS->InitOK()))
    3627             :             {
    3628           0 :                 delete poDS;
    3629           0 :                 CPLFree(pData);
    3630           0 :                 GDALDEMProcessingOptionsFree(psOptionsToFree);
    3631           0 :                 return nullptr;
    3632             :             }
    3633           2 :             hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
    3634             :         }
    3635             :         else
    3636             :         {
    3637           4 :             if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 ||
    3638             :                 eSrcDT == GDT_UInt16)
    3639             :             {
    3640             :                 GDALGeneric3x3Dataset<GInt32> *poDS =
    3641             :                     new GDALGeneric3x3Dataset<GInt32>(
    3642             :                         hSrcDataset, hSrcBand, eDstDataType, bDstHasNoData,
    3643             :                         dfDstNoDataValue, pfnAlgInt32, pData,
    3644           3 :                         psOptions->bComputeAtEdges);
    3645             : 
    3646           3 :                 if (!(poDS->InitOK()))
    3647             :                 {
    3648           0 :                     delete poDS;
    3649           0 :                     CPLFree(pData);
    3650           0 :                     GDALDEMProcessingOptionsFree(psOptionsToFree);
    3651           0 :                     return nullptr;
    3652             :                 }
    3653           3 :                 hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
    3654             :             }
    3655             :             else
    3656             :             {
    3657             :                 GDALGeneric3x3Dataset<float> *poDS =
    3658             :                     new GDALGeneric3x3Dataset<float>(
    3659             :                         hSrcDataset, hSrcBand, eDstDataType, bDstHasNoData,
    3660             :                         dfDstNoDataValue, pfnAlgFloat, pData,
    3661           1 :                         psOptions->bComputeAtEdges);
    3662             : 
    3663           1 :                 if (!(poDS->InitOK()))
    3664             :                 {
    3665           0 :                     delete poDS;
    3666           0 :                     CPLFree(pData);
    3667           0 :                     GDALDEMProcessingOptionsFree(psOptionsToFree);
    3668           0 :                     return nullptr;
    3669             :                 }
    3670           1 :                 hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
    3671             :             }
    3672             :         }
    3673             : 
    3674          12 :         GDALDatasetH hOutDS = GDALCreateCopy(
    3675             :             hDriver, pszDest, hIntermediateDataset, TRUE,
    3676           6 :             psOptions->papszCreateOptions, pfnProgress, pProgressData);
    3677             : 
    3678           6 :         GDALClose(hIntermediateDataset);
    3679             : 
    3680           6 :         CPLFree(pData);
    3681             : 
    3682           6 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3683           6 :         return hOutDS;
    3684             :     }
    3685             : 
    3686          51 :     const int nDstBands =
    3687          51 :         eUtilityMode == COLOR_RELIEF ? ((psOptions->bAddAlpha) ? 4 : 3) : 1;
    3688             : 
    3689             :     GDALDatasetH hDstDataset =
    3690         102 :         GDALCreate(hDriver, pszDest, nXSize, nYSize, nDstBands, eDstDataType,
    3691          51 :                    psOptions->papszCreateOptions);
    3692             : 
    3693          51 :     if (hDstDataset == nullptr)
    3694             :     {
    3695           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to create dataset %s",
    3696             :                  pszDest);
    3697           0 :         GDALDEMProcessingOptionsFree(psOptionsToFree);
    3698           0 :         CPLFree(pData);
    3699           0 :         return nullptr;
    3700             :     }
    3701             : 
    3702          51 :     GDALRasterBandH hDstBand = GDALGetRasterBand(hDstDataset, 1);
    3703             : 
    3704          51 :     GDALSetGeoTransform(hDstDataset, adfGeoTransform);
    3705          51 :     GDALSetProjection(hDstDataset, GDALGetProjectionRef(hSrcDataset));
    3706             : 
    3707          51 :     if (eUtilityMode == COLOR_RELIEF)
    3708             :     {
    3709          14 :         GDALColorRelief(hSrcBand, GDALGetRasterBand(hDstDataset, 1),
    3710             :                         GDALGetRasterBand(hDstDataset, 2),
    3711             :                         GDALGetRasterBand(hDstDataset, 3),
    3712           1 :                         psOptions->bAddAlpha ? GDALGetRasterBand(hDstDataset, 4)
    3713             :                                              : nullptr,
    3714          14 :                         pszColorFilename, psOptions->eColorSelectionMode,
    3715             :                         pfnProgress, pProgressData);
    3716             :     }
    3717             :     else
    3718             :     {
    3719          37 :         if (bDstHasNoData)
    3720          37 :             GDALSetRasterNoDataValue(hDstBand, dfDstNoDataValue);
    3721             : 
    3722          37 :         if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16)
    3723             :         {
    3724          33 :             GDALGeneric3x3Processing<GInt32>(
    3725             :                 hSrcBand, hDstBand, pfnAlgInt32, pfnAlgInt32_multisample, pData,
    3726          33 :                 psOptions->bComputeAtEdges, pfnProgress, pProgressData);
    3727             :         }
    3728             :         else
    3729             :         {
    3730           4 :             GDALGeneric3x3Processing<float>(
    3731             :                 hSrcBand, hDstBand, pfnAlgFloat, nullptr, pData,
    3732           4 :                 psOptions->bComputeAtEdges, pfnProgress, pProgressData);
    3733             :         }
    3734             :     }
    3735             : 
    3736          51 :     CPLFree(pData);
    3737             : 
    3738          51 :     GDALDEMProcessingOptionsFree(psOptionsToFree);
    3739          51 :     return hDstDataset;
    3740             : }
    3741             : 
    3742             : /************************************************************************/
    3743             : /*                           GDALDEMProcessingOptionsNew()              */
    3744             : /************************************************************************/
    3745             : 
    3746             : /**
    3747             :  * Allocates a GDALDEMProcessingOptions struct.
    3748             :  *
    3749             :  * @param papszArgv NULL terminated list of options (potentially including
    3750             :  * filename and open options too), or NULL. The accepted options are the ones of
    3751             :  * the <a href="/programs/gdaldem.html">gdaldem</a> utility.
    3752             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    3753             :  * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
    3754             :  *                           GDALDEMProcessingOptionsForBinaryNew() prior to
    3755             :  * this function. Will be filled with potentially present filename, open
    3756             :  * options,...
    3757             :  * @return pointer to the allocated GDALDEMProcessingOptions struct. Must be
    3758             :  * freed with GDALDEMProcessingOptionsFree().
    3759             :  *
    3760             :  * @since GDAL 2.1
    3761             :  */
    3762             : 
    3763          60 : GDALDEMProcessingOptions *GDALDEMProcessingOptionsNew(
    3764             :     char **papszArgv, GDALDEMProcessingOptionsForBinary *psOptionsForBinary)
    3765             : {
    3766          60 :     GDALDEMProcessingOptions *psOptions = new GDALDEMProcessingOptions;
    3767          60 :     Algorithm eUtilityMode = INVALID;
    3768          60 :     bool bAzimuthSpecified = false;
    3769          60 :     bool bAltSpecified = false;
    3770             : 
    3771             :     /* -------------------------------------------------------------------- */
    3772             :     /*      Handle command line arguments.                                  */
    3773             :     /* -------------------------------------------------------------------- */
    3774          60 :     int argc = CSLCount(papszArgv);
    3775         299 :     for (int i = 0; papszArgv != nullptr && i < argc; i++)
    3776             :     {
    3777         239 :         if (i == 0 && psOptionsForBinary)
    3778             :         {
    3779          21 :             eUtilityMode = GetAlgorithm(papszArgv[0]);
    3780          21 :             if (eUtilityMode == INVALID)
    3781             :             {
    3782           0 :                 CPLError(CE_Failure, CPLE_IllegalArg, "Invalid utility mode");
    3783           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3784           0 :                 return nullptr;
    3785             :             }
    3786          21 :             psOptionsForBinary->pszProcessing = CPLStrdup(papszArgv[0]);
    3787          21 :             continue;
    3788             :         }
    3789             : 
    3790         218 :         if (i < argc - 1 &&
    3791         190 :             (EQUAL(papszArgv[i], "-of") || EQUAL(papszArgv[i], "-f")))
    3792             :         {
    3793          46 :             ++i;
    3794          46 :             CPLFree(psOptions->pszFormat);
    3795          46 :             psOptions->pszFormat = CPLStrdup(papszArgv[i]);
    3796             :         }
    3797             : 
    3798         172 :         else if (EQUAL(papszArgv[i], "-q") || EQUAL(papszArgv[i], "-quiet"))
    3799             :         {
    3800           0 :             if (psOptionsForBinary)
    3801           0 :                 psOptionsForBinary->bQuiet = TRUE;
    3802             :         }
    3803             : 
    3804         172 :         else if ((EQUAL(papszArgv[i], "--z") || EQUAL(papszArgv[i], "-z")) &&
    3805          22 :                  i + 1 < argc)
    3806             :         {
    3807          22 :             ++i;
    3808          22 :             if (!ArgIsNumeric(papszArgv[i]))
    3809             :             {
    3810           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3811             :                          "Numeric value expected for -z");
    3812           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3813           0 :                 return nullptr;
    3814             :             }
    3815          22 :             psOptions->z = CPLAtof(papszArgv[i]);
    3816             :         }
    3817         150 :         else if (EQUAL(papszArgv[i], "-p"))
    3818             :         {
    3819           0 :             psOptions->slopeFormat = 0;
    3820             :         }
    3821         150 :         else if (EQUAL(papszArgv[i], "-alg") && i + 1 < argc)
    3822             :         {
    3823           7 :             i++;
    3824           7 :             if (EQUAL(papszArgv[i], "ZevenbergenThorne"))
    3825             :             {
    3826           6 :                 psOptions->bGradientAlgSpecified = true;
    3827           6 :                 psOptions->eGradientAlg = GradientAlg::ZEVENBERGEN_THORNE;
    3828             :             }
    3829           1 :             else if (EQUAL(papszArgv[i], "Horn"))
    3830             :             {
    3831           0 :                 psOptions->bGradientAlgSpecified = true;
    3832           0 :                 psOptions->eGradientAlg = GradientAlg::HORN;
    3833             :             }
    3834           1 :             else if (EQUAL(papszArgv[i], "Riley"))
    3835             :             {
    3836           0 :                 psOptions->bTRIAlgSpecified = true;
    3837           0 :                 psOptions->eTRIAlg = TRIAlg::RILEY;
    3838             :             }
    3839           1 :             else if (EQUAL(papszArgv[i], "Wilson"))
    3840             :             {
    3841           1 :                 psOptions->bTRIAlgSpecified = true;
    3842           1 :                 psOptions->eTRIAlg = TRIAlg::WILSON;
    3843             :             }
    3844             :             else
    3845             :             {
    3846           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3847           0 :                          "Invalid value for -alg: %s", papszArgv[i - 1]);
    3848           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3849           0 :                 return nullptr;
    3850             :             }
    3851             :         }
    3852         143 :         else if (EQUAL(papszArgv[i], "-trigonometric"))
    3853             :         {
    3854           0 :             psOptions->bAngleAsAzimuth = false;
    3855             :         }
    3856         143 :         else if (EQUAL(papszArgv[i], "-zero_for_flat"))
    3857             :         {
    3858           0 :             psOptions->bZeroForFlat = true;
    3859             :         }
    3860         143 :         else if (EQUAL(papszArgv[i], "-exact_color_entry"))
    3861             :         {
    3862           1 :             psOptions->eColorSelectionMode = COLOR_SELECTION_EXACT_ENTRY;
    3863             :         }
    3864         142 :         else if (EQUAL(papszArgv[i], "-nearest_color_entry"))
    3865             :         {
    3866           3 :             psOptions->eColorSelectionMode = COLOR_SELECTION_NEAREST_ENTRY;
    3867             :         }
    3868         139 :         else if ((EQUAL(papszArgv[i], "--s") || EQUAL(papszArgv[i], "-s") ||
    3869         116 :                   EQUAL(papszArgv[i], "--scale") ||
    3870         116 :                   EQUAL(papszArgv[i], "-scale")) &&
    3871          23 :                  i + 1 < argc)
    3872             :         {
    3873          23 :             ++i;
    3874          23 :             if (!ArgIsNumeric(papszArgv[i]))
    3875             :             {
    3876           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3877           0 :                          "Numeric value expected for %s", papszArgv[i - 1]);
    3878           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3879           0 :                 return nullptr;
    3880             :             }
    3881          23 :             psOptions->scale = CPLAtof(papszArgv[i]);
    3882             :         }
    3883         116 :         else if ((EQUAL(papszArgv[i], "--az") || EQUAL(papszArgv[i], "-az") ||
    3884         114 :                   EQUAL(papszArgv[i], "--azimuth") ||
    3885         114 :                   EQUAL(papszArgv[i], "-azimuth")) &&
    3886           2 :                  i + 1 < argc)
    3887             :         {
    3888           2 :             ++i;
    3889           2 :             if (!ArgIsNumeric(papszArgv[i]))
    3890             :             {
    3891           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3892           0 :                          "Numeric value expected for %s", papszArgv[i - 1]);
    3893           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3894           0 :                 return nullptr;
    3895             :             }
    3896           2 :             bAzimuthSpecified = true;
    3897           2 :             psOptions->az = CPLAtof(papszArgv[i]);
    3898             :         }
    3899         114 :         else if ((EQUAL(papszArgv[i], "--alt") || EQUAL(papszArgv[i], "-alt") ||
    3900         114 :                   EQUAL(papszArgv[i], "--altitude") ||
    3901         114 :                   EQUAL(papszArgv[i], "-altitude")) &&
    3902           0 :                  i + 1 < argc)
    3903             :         {
    3904           0 :             ++i;
    3905           0 :             if (!ArgIsNumeric(papszArgv[i]))
    3906             :             {
    3907           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    3908           0 :                          "Numeric value expected for %s", papszArgv[i - 1]);
    3909           0 :                 GDALDEMProcessingOptionsFree(psOptions);
    3910           0 :                 return nullptr;
    3911             :             }
    3912           0 :             bAltSpecified = true;
    3913           0 :             psOptions->alt = CPLAtof(papszArgv[i]);
    3914             :         }
    3915         114 :         else if ((EQUAL(papszArgv[i], "-combined") ||
    3916         111 :                   EQUAL(papszArgv[i], "--combined")))
    3917             :         {
    3918           3 :             psOptions->bCombined = true;
    3919             :         }
    3920         111 :         else if ((EQUAL(papszArgv[i], "-igor") ||
    3921         109 :                   EQUAL(papszArgv[i], "--igor")))
    3922             :         {
    3923           2 :             psOptions->bIgor = true;
    3924             :         }
    3925         109 :         else if (EQUAL(papszArgv[i], "-multidirectional") ||
    3926         107 :                  EQUAL(papszArgv[i], "--multidirectional"))
    3927             :         {
    3928           2 :             psOptions->bMultiDirectional = true;
    3929             :         }
    3930         107 :         else if (EQUAL(papszArgv[i], "-alpha"))
    3931             :         {
    3932           1 :             psOptions->bAddAlpha = true;
    3933             :         }
    3934         106 :         else if (EQUAL(papszArgv[i], "-compute_edges"))
    3935             :         {
    3936          10 :             psOptions->bComputeAtEdges = true;
    3937             :         }
    3938          96 :         else if (i + 1 < argc &&
    3939          77 :                  (EQUAL(papszArgv[i], "--b") || EQUAL(papszArgv[i], "-b")))
    3940             :         {
    3941          39 :             psOptions->nBand = atoi(papszArgv[++i]);
    3942             :         }
    3943          57 :         else if (EQUAL(papszArgv[i], "-co") && i + 1 < argc)
    3944             :         {
    3945           6 :             psOptions->papszCreateOptions =
    3946           3 :                 CSLAddString(psOptions->papszCreateOptions, papszArgv[++i]);
    3947             :         }
    3948          54 :         else if (papszArgv[i][0] == '-')
    3949             :         {
    3950           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'",
    3951           0 :                      papszArgv[i]);
    3952           0 :             GDALDEMProcessingOptionsFree(psOptions);
    3953           0 :             return nullptr;
    3954             :         }
    3955          54 :         else if (psOptionsForBinary &&
    3956          54 :                  psOptionsForBinary->pszSrcFilename == nullptr)
    3957             :         {
    3958          21 :             psOptionsForBinary->pszSrcFilename = CPLStrdup(papszArgv[i]);
    3959             :         }
    3960          33 :         else if (psOptionsForBinary && eUtilityMode == COLOR_RELIEF &&
    3961          24 :                  psOptionsForBinary->pszColorFilename == nullptr)
    3962             :         {
    3963          12 :             psOptionsForBinary->pszColorFilename = CPLStrdup(papszArgv[i]);
    3964             :         }
    3965          21 :         else if (psOptionsForBinary &&
    3966          21 :                  psOptionsForBinary->pszDstFilename == nullptr)
    3967             :         {
    3968          21 :             psOptionsForBinary->pszDstFilename = CPLStrdup(papszArgv[i]);
    3969             :         }
    3970             :         else
    3971             :         {
    3972           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3973           0 :                      "Too many command options '%s'", papszArgv[i]);
    3974           0 :             GDALDEMProcessingOptionsFree(psOptions);
    3975           0 :             return nullptr;
    3976             :         }
    3977             :     }
    3978             : 
    3979          60 :     if (psOptions->bMultiDirectional + psOptions->bCombined + psOptions->bIgor >
    3980             :         1)
    3981             :     {
    3982           0 :         CPLError(
    3983             :             CE_Failure, CPLE_NotSupported,
    3984             :             "only one of -multidirectional, -combined or -igor can be used");
    3985           0 :         GDALDEMProcessingOptionsFree(psOptions);
    3986           0 :         return nullptr;
    3987             :     }
    3988             : 
    3989          60 :     if (psOptions->bMultiDirectional && bAzimuthSpecified)
    3990             :     {
    3991           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3992             :                  "-multidirectional and -az cannot be used together");
    3993           0 :         GDALDEMProcessingOptionsFree(psOptions);
    3994           0 :         return nullptr;
    3995             :     }
    3996             : 
    3997          60 :     if (psOptions->bIgor && bAltSpecified)
    3998             :     {
    3999           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4000             :                  "-igor and -alt cannot be used together");
    4001           0 :         GDALDEMProcessingOptionsFree(psOptions);
    4002           0 :         return nullptr;
    4003             :     }
    4004             : 
    4005          60 :     return psOptions;
    4006             : }
    4007             : 
    4008             : /************************************************************************/
    4009             : /*                       GDALDEMProcessingOptionsFree()                 */
    4010             : /************************************************************************/
    4011             : 
    4012             : /**
    4013             :  * Frees the GDALDEMProcessingOptions struct.
    4014             :  *
    4015             :  * @param psOptions the options struct for GDALDEMProcessing().
    4016             :  *
    4017             :  * @since GDAL 2.1
    4018             :  */
    4019             : 
    4020         120 : void GDALDEMProcessingOptionsFree(GDALDEMProcessingOptions *psOptions)
    4021             : {
    4022         120 :     if (psOptions)
    4023             :     {
    4024          60 :         CPLFree(psOptions->pszFormat);
    4025          60 :         CSLDestroy(psOptions->papszCreateOptions);
    4026             : 
    4027          60 :         delete psOptions;
    4028             :     }
    4029         120 : }
    4030             : 
    4031             : /************************************************************************/
    4032             : /*                 GDALDEMProcessingOptionsSetProgress()                */
    4033             : /************************************************************************/
    4034             : 
    4035             : /**
    4036             :  * Set a progress function.
    4037             :  *
    4038             :  * @param psOptions the options struct for GDALDEMProcessing().
    4039             :  * @param pfnProgress the progress callback.
    4040             :  * @param pProgressData the user data for the progress callback.
    4041             :  *
    4042             :  * @since GDAL 2.1
    4043             :  */
    4044             : 
    4045          21 : void GDALDEMProcessingOptionsSetProgress(GDALDEMProcessingOptions *psOptions,
    4046             :                                          GDALProgressFunc pfnProgress,
    4047             :                                          void *pProgressData)
    4048             : {
    4049          21 :     psOptions->pfnProgress = pfnProgress;
    4050          21 :     psOptions->pProgressData = pProgressData;
    4051          21 : }

Generated by: LCOV version 1.14