LCOV - code coverage report
Current view: top level - alg - rasterfill.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 317 366 86.6 %
Date: 2024-05-04 12:52:34 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Interpolate in nodata areas.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008, Frank Warmerdam
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2015, Sean Gillies <sean@mapbox.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ***************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : #include "gdal_alg.h"
      33             : 
      34             : #include <cmath>
      35             : #include <cstring>
      36             : 
      37             : #include <algorithm>
      38             : #include <string>
      39             : #include <utility>
      40             : 
      41             : #include "cpl_conv.h"
      42             : #include "cpl_error.h"
      43             : #include "cpl_progress.h"
      44             : #include "cpl_string.h"
      45             : #include "cpl_vsi.h"
      46             : #include "gdal.h"
      47             : #include "gdal_priv.h"
      48             : 
      49             : /************************************************************************/
      50             : /*                           GDALFilterLine()                           */
      51             : /*                                                                      */
      52             : /*      Apply 3x3 filtering one one scanline with masking for which     */
      53             : /*      pixels are to be interpolated (ThisFMask) and which window      */
      54             : /*      pixels are valid to include in the interpolation (TMask).       */
      55             : /************************************************************************/
      56             : 
      57        2280 : static void GDALFilterLine(const float *pafLastLine, const float *pafThisLine,
      58             :                            const float *pafNextLine, float *pafOutLine,
      59             :                            const GByte *pabyLastTMask,
      60             :                            const GByte *pabyThisTMask,
      61             :                            const GByte *pabyNextTMask,
      62             :                            const GByte *pabyThisFMask, int nXSize)
      63             : 
      64             : {
      65      631866 :     for (int iX = 0; iX < nXSize; iX++)
      66             :     {
      67      629586 :         if (!pabyThisFMask[iX])
      68             :         {
      69      579740 :             pafOutLine[iX] = pafThisLine[iX];
      70      579740 :             continue;
      71             :         }
      72             : 
      73       49846 :         CPLAssert(pabyThisTMask[iX]);
      74             : 
      75       49846 :         double dfValSum = 0.0;
      76       49846 :         double dfWeightSum = 0.0;
      77             : 
      78             :         // Previous line.
      79       49846 :         if (pafLastLine != nullptr)
      80             :         {
      81       49846 :             if (iX > 0 && pabyLastTMask[iX - 1])
      82             :             {
      83       47442 :                 dfValSum += pafLastLine[iX - 1];
      84       47442 :                 dfWeightSum += 1.0;
      85             :             }
      86       49846 :             if (pabyLastTMask[iX])
      87             :             {
      88       48642 :                 dfValSum += pafLastLine[iX];
      89       48642 :                 dfWeightSum += 1.0;
      90             :             }
      91       49846 :             if (iX < nXSize - 1 && pabyLastTMask[iX + 1])
      92             :             {
      93       47282 :                 dfValSum += pafLastLine[iX + 1];
      94       47282 :                 dfWeightSum += 1.0;
      95             :             }
      96             :         }
      97             : 
      98             :         // Current Line.
      99       49846 :         if (iX > 0 && pabyThisTMask[iX - 1])
     100             :         {
     101       47846 :             dfValSum += pafThisLine[iX - 1];
     102       47846 :             dfWeightSum += 1.0;
     103             :         }
     104       49846 :         if (pabyThisTMask[iX])
     105             :         {
     106       49846 :             dfValSum += pafThisLine[iX];
     107       49846 :             dfWeightSum += 1.0;
     108             :         }
     109       49846 :         if (iX < nXSize - 1 && pabyThisTMask[iX + 1])
     110             :         {
     111       47644 :             dfValSum += pafThisLine[iX + 1];
     112       47644 :             dfWeightSum += 1.0;
     113             :         }
     114             : 
     115             :         // Next line.
     116       49846 :         if (pafNextLine != nullptr)
     117             :         {
     118       49846 :             if (iX > 0 && pabyNextTMask[iX - 1])
     119             :             {
     120       41958 :                 dfValSum += pafNextLine[iX - 1];
     121       41958 :                 dfWeightSum += 1.0;
     122             :             }
     123       49846 :             if (pabyNextTMask[iX])
     124             :             {
     125       43084 :                 dfValSum += pafNextLine[iX];
     126       43084 :                 dfWeightSum += 1.0;
     127             :             }
     128       49846 :             if (iX < nXSize - 1 && pabyNextTMask[iX + 1])
     129             :             {
     130       41720 :                 dfValSum += pafNextLine[iX + 1];
     131       41720 :                 dfWeightSum += 1.0;
     132             :             }
     133             :         }
     134             : 
     135       49846 :         pafOutLine[iX] = static_cast<float>(dfValSum / dfWeightSum);
     136             :     }
     137        2280 : }
     138             : 
     139             : /************************************************************************/
     140             : /*                          GDALMultiFilter()                           */
     141             : /*                                                                      */
     142             : /*      Apply multiple iterations of a 3x3 smoothing filter over a      */
     143             : /*      band with masking controlling what pixels should be             */
     144             : /*      filtered (FiltMaskBand non zero) and which pixels can be        */
     145             : /*      considered valid contributors to the filter                     */
     146             : /*      (TargetMaskBand non zero).                                      */
     147             : /*                                                                      */
     148             : /*      This implementation attempts to apply many iterations in        */
     149             : /*      one IO pass by managing the filtering over a rolling buffer     */
     150             : /*      of nIterations+2 scanlines.  While possibly clever this        */
     151             : /*      makes the algorithm implementation largely                      */
     152             : /*      incomprehensible.                                               */
     153             : /************************************************************************/
     154             : 
     155          92 : static CPLErr GDALMultiFilter(GDALRasterBandH hTargetBand,
     156             :                               GDALRasterBandH hTargetMaskBand,
     157             :                               GDALRasterBandH hFiltMaskBand, int nIterations,
     158             :                               GDALProgressFunc pfnProgress, void *pProgressArg)
     159             : 
     160             : {
     161          92 :     const int nXSize = GDALGetRasterBandXSize(hTargetBand);
     162          92 :     const int nYSize = GDALGetRasterBandYSize(hTargetBand);
     163             : 
     164             :     /* -------------------------------------------------------------------- */
     165             :     /*      Report starting progress value.                                 */
     166             :     /* -------------------------------------------------------------------- */
     167          92 :     if (!pfnProgress(0.0, "Smoothing Filter...", pProgressArg))
     168             :     {
     169           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     170           0 :         return CE_Failure;
     171             :     }
     172             : 
     173             :     /* -------------------------------------------------------------------- */
     174             :     /*      Allocate rotating buffers.                                      */
     175             :     /* -------------------------------------------------------------------- */
     176          92 :     const int nBufLines = nIterations + 2;
     177             : 
     178             :     GByte *pabyTMaskBuf =
     179          92 :         static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nXSize, nBufLines));
     180             :     GByte *pabyFMaskBuf =
     181          92 :         static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nXSize, nBufLines));
     182             : 
     183             :     float *paf3PassLineBuf = static_cast<float *>(
     184          92 :         VSI_MALLOC3_VERBOSE(nXSize, nBufLines, 3 * sizeof(float)));
     185          92 :     if (pabyTMaskBuf == nullptr || pabyFMaskBuf == nullptr ||
     186             :         paf3PassLineBuf == nullptr)
     187             :     {
     188           0 :         CPLFree(pabyTMaskBuf);
     189           0 :         CPLFree(pabyFMaskBuf);
     190           0 :         CPLFree(paf3PassLineBuf);
     191             : 
     192           0 :         return CE_Failure;
     193             :     }
     194             : 
     195             :     /* -------------------------------------------------------------------- */
     196             :     /*      Process rotating buffers.                                       */
     197             :     /* -------------------------------------------------------------------- */
     198             : 
     199          92 :     CPLErr eErr = CE_None;
     200             : 
     201          92 :     int iPassCounter = 0;
     202             : 
     203        2612 :     for (int nNewLine = 0;  // Line being loaded (zero based scanline).
     204        2612 :          eErr == CE_None && nNewLine < nYSize + nIterations; nNewLine++)
     205             :     {
     206             :         /* --------------------------------------------------------------------
     207             :          */
     208             :         /*      Rotate pass buffers. */
     209             :         /* --------------------------------------------------------------------
     210             :          */
     211        2520 :         iPassCounter = (iPassCounter + 1) % 3;
     212             : 
     213        2520 :         float *const pafSLastPass =
     214        2520 :             paf3PassLineBuf + ((iPassCounter + 0) % 3) * nXSize * nBufLines;
     215        2520 :         float *const pafLastPass =
     216        2520 :             paf3PassLineBuf + ((iPassCounter + 1) % 3) * nXSize * nBufLines;
     217        2520 :         float *const pafThisPass =
     218        2520 :             paf3PassLineBuf + ((iPassCounter + 2) % 3) * nXSize * nBufLines;
     219             : 
     220             :         /* --------------------------------------------------------------------
     221             :          */
     222             :         /*      Where does the new line go in the rotating buffer? */
     223             :         /* --------------------------------------------------------------------
     224             :          */
     225        2520 :         const int iBufOffset = nNewLine % nBufLines;
     226             : 
     227             :         /* --------------------------------------------------------------------
     228             :          */
     229             :         /*      Read the new data line if it is't off the bottom of the */
     230             :         /*      image. */
     231             :         /* --------------------------------------------------------------------
     232             :          */
     233        2520 :         if (nNewLine < nYSize)
     234             :         {
     235        4820 :             eErr = GDALRasterIO(hTargetMaskBand, GF_Read, 0, nNewLine, nXSize,
     236        2410 :                                 1, pabyTMaskBuf + nXSize * iBufOffset, nXSize,
     237             :                                 1, GDT_Byte, 0, 0);
     238             : 
     239        2410 :             if (eErr != CE_None)
     240           0 :                 break;
     241             : 
     242        4820 :             eErr = GDALRasterIO(hFiltMaskBand, GF_Read, 0, nNewLine, nXSize, 1,
     243        2410 :                                 pabyFMaskBuf + nXSize * iBufOffset, nXSize, 1,
     244             :                                 GDT_Byte, 0, 0);
     245             : 
     246        2410 :             if (eErr != CE_None)
     247           0 :                 break;
     248             : 
     249        4820 :             eErr = GDALRasterIO(hTargetBand, GF_Read, 0, nNewLine, nXSize, 1,
     250        2410 :                                 pafThisPass + nXSize * iBufOffset, nXSize, 1,
     251             :                                 GDT_Float32, 0, 0);
     252             : 
     253        2410 :             if (eErr != CE_None)
     254           0 :                 break;
     255             :         }
     256             : 
     257             :         /* --------------------------------------------------------------------
     258             :          */
     259             :         /*      Loop over the loaded data, applying the filter to all loaded */
     260             :         /*      lines with neighbours. */
     261             :         /* --------------------------------------------------------------------
     262             :          */
     263        5310 :         for (int iFLine = nNewLine - 1;
     264        5310 :              eErr == CE_None && iFLine >= nNewLine - nIterations; iFLine--)
     265             :         {
     266        2790 :             const int iLastOffset = (iFLine - 1) % nBufLines;
     267        2790 :             const int iThisOffset = (iFLine) % nBufLines;
     268        2790 :             const int iNextOffset = (iFLine + 1) % nBufLines;
     269             : 
     270             :             // Default to preserving the old value.
     271        2790 :             if (iFLine >= 0)
     272        2590 :                 memcpy(pafThisPass + iThisOffset * nXSize,
     273        2590 :                        pafLastPass + iThisOffset * nXSize,
     274        2590 :                        sizeof(float) * nXSize);
     275             : 
     276             :             // TODO: Enable first and last line.
     277             :             // Skip the first and last line.
     278        2790 :             if (iFLine < 1 || iFLine >= nYSize - 1)
     279             :             {
     280         510 :                 continue;
     281             :             }
     282             : 
     283        2280 :             GDALFilterLine(pafSLastPass + iLastOffset * nXSize,
     284        2280 :                            pafLastPass + iThisOffset * nXSize,
     285        2280 :                            pafThisPass + iNextOffset * nXSize,
     286        2280 :                            pafThisPass + iThisOffset * nXSize,
     287        2280 :                            pabyTMaskBuf + iLastOffset * nXSize,
     288        2280 :                            pabyTMaskBuf + iThisOffset * nXSize,
     289        2280 :                            pabyTMaskBuf + iNextOffset * nXSize,
     290        2280 :                            pabyFMaskBuf + iThisOffset * nXSize, nXSize);
     291             :         }
     292             : 
     293             :         /* --------------------------------------------------------------------
     294             :          */
     295             :         /*      Write out the top data line that will be rolling out of our */
     296             :         /*      buffer. */
     297             :         /* --------------------------------------------------------------------
     298             :          */
     299        2520 :         const int iLineToSave = nNewLine - nIterations;
     300             : 
     301        2520 :         if (iLineToSave >= 0 && eErr == CE_None)
     302             :         {
     303        2410 :             const int iBufOffset2 = iLineToSave % nBufLines;
     304             : 
     305        2410 :             eErr = GDALRasterIO(hTargetBand, GF_Write, 0, iLineToSave, nXSize,
     306        2410 :                                 1, pafThisPass + nXSize * iBufOffset2, nXSize,
     307             :                                 1, GDT_Float32, 0, 0);
     308             :         }
     309             : 
     310             :         /* --------------------------------------------------------------------
     311             :          */
     312             :         /*      Report progress. */
     313             :         /* --------------------------------------------------------------------
     314             :          */
     315        5040 :         if (eErr == CE_None &&
     316        2520 :             !pfnProgress((nNewLine + 1) /
     317        2520 :                              static_cast<double>(nYSize + nIterations),
     318             :                          "Smoothing Filter...", pProgressArg))
     319             :         {
     320           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     321           0 :             eErr = CE_Failure;
     322             :         }
     323             :     }
     324             : 
     325             :     /* -------------------------------------------------------------------- */
     326             :     /*      Cleanup                                                         */
     327             :     /* -------------------------------------------------------------------- */
     328          92 :     CPLFree(pabyTMaskBuf);
     329          92 :     CPLFree(pabyFMaskBuf);
     330          92 :     CPLFree(paf3PassLineBuf);
     331             : 
     332          92 :     return eErr;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                             QUAD_CHECK()                             */
     337             : /*                                                                      */
     338             : /*      macro for checking whether a point is nearer than the           */
     339             : /*      existing closest point.                                         */
     340             : /************************************************************************/
     341             : 
     342     4370290 : inline void QUAD_CHECK(double &dfQuadDist, float &fQuadValue, int target_x,
     343             :                        GUInt32 target_y, int origin_x, int origin_y,
     344             :                        float fTargetValue, GUInt32 nNoDataVal)
     345             : {
     346     4370290 :     if (target_y != nNoDataVal)
     347             :     {
     348      468800 :         const double dfDx =
     349      468800 :             static_cast<double>(target_x) - static_cast<double>(origin_x);
     350      468800 :         const double dfDy =
     351      468800 :             static_cast<double>(target_y) - static_cast<double>(origin_y);
     352      468800 :         double dfDistSq = dfDx * dfDx + dfDy * dfDy;
     353             : 
     354      468800 :         if (dfDistSq < dfQuadDist * dfQuadDist)
     355             :         {
     356      170775 :             CPLAssert(dfDistSq > 0.0);
     357      170775 :             dfQuadDist = sqrt(dfDistSq);
     358      170775 :             fQuadValue = fTargetValue;
     359             :         }
     360             :     }
     361     4370290 : }
     362             : 
     363             : /************************************************************************/
     364             : /*                           GDALFillNodata()                           */
     365             : /************************************************************************/
     366             : 
     367             : /**
     368             :  * Fill selected raster regions by interpolation from the edges.
     369             :  *
     370             :  * This algorithm will interpolate values for all designated
     371             :  * nodata pixels (marked by zeros in hMaskBand). For each pixel
     372             :  * a four direction conic search is done to find values to interpolate
     373             :  * from (using inverse distance weighting by default). Once all values are
     374             :  * interpolated, zero or more smoothing iterations (3x3 average
     375             :  * filters on interpolated pixels) are applied to smooth out
     376             :  * artifacts.
     377             :  *
     378             :  * This algorithm is generally suitable for interpolating missing
     379             :  * regions of fairly continuously varying rasters (such as elevation
     380             :  * models for instance). It is also suitable for filling small holes
     381             :  * and cracks in more irregularly varying images (like airphotos). It
     382             :  * is generally not so great for interpolating a raster from sparse
     383             :  * point data - see the algorithms defined in gdal_grid.h for that case.
     384             :  *
     385             :  * @param hTargetBand the raster band to be modified in place.
     386             :  * @param hMaskBand a mask band indicating pixels to be interpolated
     387             :  * (zero valued). If hMaskBand is set to NULL, this method will internally use
     388             :  * the mask band returned by GDALGetMaskBand(hTargetBand).
     389             :  * @param dfMaxSearchDist the maximum number of pixels to search in all
     390             :  * directions to find values to interpolate from.
     391             :  * @param bDeprecatedOption unused argument, should be zero.
     392             :  * @param nSmoothingIterations the number of 3x3 smoothing filter passes to
     393             :  * run (0 or more).
     394             :  * @param papszOptions additional name=value options in a string list.
     395             :  * <ul>
     396             :  * <li>TEMP_FILE_DRIVER=gdal_driver_name. For example MEM.</li>
     397             :  * <li>NODATA=value (starting with GDAL 2.4).
     398             :  * Source pixels at that value will be ignored by the interpolator. Warning:
     399             :  * currently this will not be honored by smoothing passes.</li>
     400             :  * <li>INTERPOLATION=INV_DIST/NEAREST (GDAL >= 3.9). By default, pixels are
     401             :  * interpolated using an inverse distance weighting (INV_DIST). It is also
     402             :  * possible to choose a nearest neighbour (NEAREST) strategy.</li>
     403             :  * </ul>
     404             :  * @param pfnProgress the progress function to report completion.
     405             :  * @param pProgressArg callback data for progress function.
     406             :  *
     407             :  * @return CE_None on success or CE_Failure if something goes wrong.
     408             :  */
     409             : 
     410         117 : CPLErr CPL_STDCALL GDALFillNodata(GDALRasterBandH hTargetBand,
     411             :                                   GDALRasterBandH hMaskBand,
     412             :                                   double dfMaxSearchDist,
     413             :                                   CPL_UNUSED int bDeprecatedOption,
     414             :                                   int nSmoothingIterations, char **papszOptions,
     415             :                                   GDALProgressFunc pfnProgress,
     416             :                                   void *pProgressArg)
     417             : 
     418             : {
     419         117 :     VALIDATE_POINTER1(hTargetBand, "GDALFillNodata", CE_Failure);
     420             : 
     421         117 :     const int nXSize = GDALGetRasterBandXSize(hTargetBand);
     422         117 :     const int nYSize = GDALGetRasterBandYSize(hTargetBand);
     423             : 
     424         117 :     if (dfMaxSearchDist == 0.0)
     425           0 :         dfMaxSearchDist = std::max(nXSize, nYSize) + 1;
     426             : 
     427         117 :     const int nMaxSearchDist = static_cast<int>(floor(dfMaxSearchDist));
     428             : 
     429             :     const char *pszInterpolation =
     430         117 :         CSLFetchNameValueDef(papszOptions, "INTERPOLATION", "INV_DIST");
     431         117 :     const bool bNearest = EQUAL(pszInterpolation, "NEAREST");
     432         117 :     if (!EQUAL(pszInterpolation, "INV_DIST") &&
     433           6 :         !EQUAL(pszInterpolation, "NEAREST"))
     434             :     {
     435           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     436             :                  "Unsupported interpolation method: %s", pszInterpolation);
     437           0 :         return CE_Failure;
     438             :     }
     439             : 
     440             :     // Special "x" pixel values identifying pixels as special.
     441         117 :     GDALDataType eType = GDT_UInt16;
     442         117 :     GUInt32 nNoDataVal = 65535;
     443             : 
     444         117 :     if (nXSize > 65533 || nYSize > 65533)
     445             :     {
     446           0 :         eType = GDT_UInt32;
     447           0 :         nNoDataVal = 4000002;
     448             :     }
     449             : 
     450             :     /* -------------------------------------------------------------------- */
     451             :     /*      Determine format driver for temp work files.                    */
     452             :     /* -------------------------------------------------------------------- */
     453             :     CPLString osTmpFileDriver =
     454         234 :         CSLFetchNameValueDef(papszOptions, "TEMP_FILE_DRIVER", "GTiff");
     455         117 :     GDALDriverH hDriver = GDALGetDriverByName(osTmpFileDriver.c_str());
     456             : 
     457         117 :     if (hDriver == nullptr)
     458             :     {
     459           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     460             :                  "TEMP_FILE_DRIVER=%s driver is not registered",
     461             :                  osTmpFileDriver.c_str());
     462           0 :         return CE_Failure;
     463             :     }
     464             : 
     465         117 :     if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr)
     466             :     {
     467           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     468             :                  "TEMP_FILE_DRIVER=%s driver is incapable of creating "
     469             :                  "temp work files",
     470             :                  osTmpFileDriver.c_str());
     471           0 :         return CE_Failure;
     472             :     }
     473             : 
     474         234 :     CPLStringList aosWorkFileOptions;
     475         117 :     if (osTmpFileDriver == "GTiff")
     476             :     {
     477         117 :         aosWorkFileOptions.SetNameValue("COMPRESS", "LZW");
     478         117 :         aosWorkFileOptions.SetNameValue("BIGTIFF", "IF_SAFER");
     479             :     }
     480             : 
     481         234 :     const CPLString osTmpFile = CPLGenerateTempFilename("");
     482             : 
     483         117 :     std::unique_ptr<GDALDataset> poTmpMaskDS;
     484         117 :     if (hMaskBand == nullptr)
     485             :     {
     486         112 :         hMaskBand = GDALGetMaskBand(hTargetBand);
     487             :     }
     488           7 :     else if (nSmoothingIterations > 0 &&
     489           2 :              hMaskBand != GDALGetMaskBand(hTargetBand))
     490             :     {
     491             :         // If doing smoothing operations and the user provided its own
     492             :         // mask band, we must make a copy of it to be able to update it
     493             :         // when we fill pixels during the initial pass.
     494           1 :         const CPLString osMaskTmpFile = osTmpFile + "fill_mask_work.tif";
     495           1 :         poTmpMaskDS.reset(GDALDataset::FromHandle(
     496             :             GDALCreate(hDriver, osMaskTmpFile, nXSize, nYSize, 1, GDT_Byte,
     497           1 :                        aosWorkFileOptions.List())));
     498           1 :         if (poTmpMaskDS == nullptr)
     499             :         {
     500           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     501             :                      "Could not create poTmpMaskDS work file. Check driver "
     502             :                      "capabilities.");
     503           0 :             return CE_Failure;
     504             :         }
     505           1 :         poTmpMaskDS->MarkSuppressOnClose();
     506             :         auto hTmpMaskBand =
     507           1 :             GDALRasterBand::ToHandle(poTmpMaskDS->GetRasterBand(1));
     508           1 :         if (GDALRasterBandCopyWholeRaster(hMaskBand, hTmpMaskBand, nullptr,
     509           1 :                                           nullptr, nullptr) != CE_None)
     510             :         {
     511           0 :             return CE_Failure;
     512             :         }
     513           1 :         hMaskBand = hTmpMaskBand;
     514             :     }
     515             : 
     516             :     // If there are smoothing iterations, reserve 10% of the progress for them.
     517         117 :     const double dfProgressRatio = nSmoothingIterations > 0 ? 0.9 : 1.0;
     518             : 
     519         117 :     const char *pszNoData = CSLFetchNameValue(papszOptions, "NODATA");
     520         117 :     bool bHasNoData = false;
     521         117 :     float fNoData = 0.0f;
     522         117 :     if (pszNoData)
     523             :     {
     524           2 :         bHasNoData = true;
     525           2 :         fNoData = static_cast<float>(CPLAtof(pszNoData));
     526             :     }
     527             : 
     528             :     /* -------------------------------------------------------------------- */
     529             :     /*      Initialize progress counter.                                    */
     530             :     /* -------------------------------------------------------------------- */
     531         117 :     if (pfnProgress == nullptr)
     532         113 :         pfnProgress = GDALDummyProgress;
     533             : 
     534         117 :     if (!pfnProgress(0.0, "Filling...", pProgressArg))
     535             :     {
     536           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     537           0 :         return CE_Failure;
     538             :     }
     539             : 
     540             :     /* -------------------------------------------------------------------- */
     541             :     /*      Create a work file to hold the Y "last value" indices.          */
     542             :     /* -------------------------------------------------------------------- */
     543         234 :     const CPLString osYTmpFile = osTmpFile + "fill_y_work.tif";
     544             : 
     545             :     auto poYDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     546             :         GDALCreate(hDriver, osYTmpFile, nXSize, nYSize, 1, eType,
     547         234 :                    aosWorkFileOptions.List())));
     548             : 
     549         117 :     if (poYDS == nullptr)
     550             :     {
     551           0 :         CPLError(
     552             :             CE_Failure, CPLE_AppDefined,
     553             :             "Could not create Y index work file. Check driver capabilities.");
     554           0 :         return CE_Failure;
     555             :     }
     556         117 :     poYDS->MarkSuppressOnClose();
     557             : 
     558             :     GDALRasterBandH hYBand =
     559         117 :         GDALRasterBand::FromHandle(poYDS->GetRasterBand(1));
     560             : 
     561             :     /* -------------------------------------------------------------------- */
     562             :     /*      Create a work file to hold the pixel value associated with      */
     563             :     /*      the "last xy value" pixel.                                      */
     564             :     /* -------------------------------------------------------------------- */
     565         234 :     const CPLString osValTmpFile = osTmpFile + "fill_val_work.tif";
     566             : 
     567             :     auto poValDS =
     568             :         std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALCreate(
     569             :             hDriver, osValTmpFile, nXSize, nYSize, 1,
     570         234 :             GDALGetRasterDataType(hTargetBand), aosWorkFileOptions.List())));
     571             : 
     572         117 :     if (poValDS == nullptr)
     573             :     {
     574           0 :         CPLError(
     575             :             CE_Failure, CPLE_AppDefined,
     576             :             "Could not create XY value work file. Check driver capabilities.");
     577           0 :         return CE_Failure;
     578             :     }
     579         117 :     poValDS->MarkSuppressOnClose();
     580             : 
     581             :     GDALRasterBandH hValBand =
     582         117 :         GDALRasterBand::FromHandle(poValDS->GetRasterBand(1));
     583             : 
     584             :     /* -------------------------------------------------------------------- */
     585             :     /*      Create a mask file to make it clear what pixels can be filtered */
     586             :     /*      on the filtering pass.                                          */
     587             :     /* -------------------------------------------------------------------- */
     588         234 :     const CPLString osFiltMaskTmpFile = osTmpFile + "fill_filtmask_work.tif";
     589             : 
     590             :     auto poFiltMaskDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     591             :         GDALCreate(hDriver, osFiltMaskTmpFile, nXSize, nYSize, 1, GDT_Byte,
     592         234 :                    aosWorkFileOptions.List())));
     593             : 
     594         117 :     if (poFiltMaskDS == nullptr)
     595             :     {
     596           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     597             :                  "Could not create mask work file. Check driver capabilities.");
     598           0 :         return CE_Failure;
     599             :     }
     600         117 :     poFiltMaskDS->MarkSuppressOnClose();
     601             : 
     602             :     GDALRasterBandH hFiltMaskBand =
     603         117 :         GDALRasterBand::FromHandle(poFiltMaskDS->GetRasterBand(1));
     604             : 
     605             :     /* -------------------------------------------------------------------- */
     606             :     /*      Allocate buffers for last scanline and this scanline.           */
     607             :     /* -------------------------------------------------------------------- */
     608             : 
     609             :     GUInt32 *panLastY =
     610         117 :         static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
     611             :     GUInt32 *panThisY =
     612         117 :         static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
     613             :     GUInt32 *panTopDownY =
     614         117 :         static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
     615             :     float *pafLastValue =
     616         117 :         static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
     617             :     float *pafThisValue =
     618         117 :         static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
     619             :     float *pafTopDownValue =
     620         117 :         static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
     621             :     float *pafScanline =
     622         117 :         static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
     623         117 :     GByte *pabyMask = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nXSize, 1));
     624         117 :     GByte *pabyFiltMask = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nXSize, 1));
     625             : 
     626         117 :     CPLErr eErr = CE_None;
     627             : 
     628         117 :     if (panLastY == nullptr || panThisY == nullptr || panTopDownY == nullptr ||
     629         117 :         pafLastValue == nullptr || pafThisValue == nullptr ||
     630         117 :         pafTopDownValue == nullptr || pafScanline == nullptr ||
     631         117 :         pabyMask == nullptr || pabyFiltMask == nullptr)
     632             :     {
     633           0 :         eErr = CE_Failure;
     634           0 :         goto end;
     635             :     }
     636             : 
     637       11318 :     for (int iX = 0; iX < nXSize; iX++)
     638             :     {
     639       11201 :         panLastY[iX] = nNoDataVal;
     640             :     }
     641             : 
     642             :     /* ==================================================================== */
     643             :     /*      Make first pass from top to bottom collecting the "last         */
     644             :     /*      known value" for each column and writing it out to the work     */
     645             :     /*      files.                                                          */
     646             :     /* ==================================================================== */
     647             : 
     648        2624 :     for (int iY = 0; iY < nYSize && eErr == CE_None; iY++)
     649             :     {
     650             :         /* --------------------------------------------------------------------
     651             :          */
     652             :         /*      Read data and mask for this line. */
     653             :         /* --------------------------------------------------------------------
     654             :          */
     655        2507 :         eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1, pabyMask,
     656             :                             nXSize, 1, GDT_Byte, 0, 0);
     657             : 
     658        2507 :         if (eErr != CE_None)
     659           0 :             break;
     660             : 
     661        2507 :         eErr = GDALRasterIO(hTargetBand, GF_Read, 0, iY, nXSize, 1, pafScanline,
     662             :                             nXSize, 1, GDT_Float32, 0, 0);
     663             : 
     664        2507 :         if (eErr != CE_None)
     665           0 :             break;
     666             : 
     667             :         /* --------------------------------------------------------------------
     668             :          */
     669             :         /*      Figure out the most recent pixel for each column. */
     670             :         /* --------------------------------------------------------------------
     671             :          */
     672             : 
     673      654985 :         for (int iX = 0; iX < nXSize; iX++)
     674             :         {
     675      652478 :             if (pabyMask[iX])
     676             :             {
     677      341578 :                 pafThisValue[iX] = pafScanline[iX];
     678      341578 :                 panThisY[iX] = iY;
     679             :             }
     680      310900 :             else if (iY <= dfMaxSearchDist + panLastY[iX])
     681             :             {
     682      303766 :                 pafThisValue[iX] = pafLastValue[iX];
     683      303766 :                 panThisY[iX] = panLastY[iX];
     684             :             }
     685             :             else
     686             :             {
     687        7134 :                 panThisY[iX] = nNoDataVal;
     688             :             }
     689             :         }
     690             : 
     691             :         /* --------------------------------------------------------------------
     692             :          */
     693             :         /*      Write out best index/value to working files. */
     694             :         /* --------------------------------------------------------------------
     695             :          */
     696        2507 :         eErr = GDALRasterIO(hYBand, GF_Write, 0, iY, nXSize, 1, panThisY,
     697             :                             nXSize, 1, GDT_UInt32, 0, 0);
     698        2507 :         if (eErr != CE_None)
     699           0 :             break;
     700             : 
     701        2507 :         eErr = GDALRasterIO(hValBand, GF_Write, 0, iY, nXSize, 1, pafThisValue,
     702             :                             nXSize, 1, GDT_Float32, 0, 0);
     703        2507 :         if (eErr != CE_None)
     704           0 :             break;
     705             : 
     706             :         /* --------------------------------------------------------------------
     707             :          */
     708             :         /*      Flip this/last buffers. */
     709             :         /* --------------------------------------------------------------------
     710             :          */
     711        2507 :         std::swap(pafThisValue, pafLastValue);
     712        2507 :         std::swap(panThisY, panLastY);
     713             : 
     714             :         /* --------------------------------------------------------------------
     715             :          */
     716             :         /*      report progress. */
     717             :         /* --------------------------------------------------------------------
     718             :          */
     719        2507 :         if (!pfnProgress(dfProgressRatio *
     720        2507 :                              (0.5 * (iY + 1) / static_cast<double>(nYSize)),
     721             :                          "Filling...", pProgressArg))
     722             :         {
     723           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     724           0 :             eErr = CE_Failure;
     725             :         }
     726             :     }
     727             : 
     728       11318 :     for (int iX = 0; iX < nXSize; iX++)
     729             :     {
     730       11201 :         panLastY[iX] = nNoDataVal;
     731             :     }
     732             : 
     733             :     /* ==================================================================== */
     734             :     /*      Now we will do collect similar this/last information from       */
     735             :     /*      bottom to top and use it in combination with the top to         */
     736             :     /*      bottom search info to interpolate.                              */
     737             :     /* ==================================================================== */
     738        2624 :     for (int iY = nYSize - 1; iY >= 0 && eErr == CE_None; iY--)
     739             :     {
     740        2507 :         eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1, pabyMask,
     741             :                             nXSize, 1, GDT_Byte, 0, 0);
     742             : 
     743        2507 :         if (eErr != CE_None)
     744           0 :             break;
     745             : 
     746        2507 :         eErr = GDALRasterIO(hTargetBand, GF_Read, 0, iY, nXSize, 1, pafScanline,
     747             :                             nXSize, 1, GDT_Float32, 0, 0);
     748             : 
     749        2507 :         if (eErr != CE_None)
     750           0 :             break;
     751             : 
     752             :         /* --------------------------------------------------------------------
     753             :          */
     754             :         /*      Figure out the most recent pixel for each column. */
     755             :         /* --------------------------------------------------------------------
     756             :          */
     757             : 
     758      654985 :         for (int iX = 0; iX < nXSize; iX++)
     759             :         {
     760      652478 :             if (pabyMask[iX])
     761             :             {
     762      341578 :                 pafThisValue[iX] = pafScanline[iX];
     763      341578 :                 panThisY[iX] = iY;
     764             :             }
     765      310900 :             else if (panLastY[iX] - iY <= dfMaxSearchDist)
     766             :             {
     767       25254 :                 pafThisValue[iX] = pafLastValue[iX];
     768       25254 :                 panThisY[iX] = panLastY[iX];
     769             :             }
     770             :             else
     771             :             {
     772      285646 :                 panThisY[iX] = nNoDataVal;
     773             :             }
     774             :         }
     775             : 
     776             :         /* --------------------------------------------------------------------
     777             :          */
     778             :         /*      Load the last y and corresponding value from the top down pass.
     779             :          */
     780             :         /* --------------------------------------------------------------------
     781             :          */
     782        2507 :         eErr = GDALRasterIO(hYBand, GF_Read, 0, iY, nXSize, 1, panTopDownY,
     783             :                             nXSize, 1, GDT_UInt32, 0, 0);
     784             : 
     785        2507 :         if (eErr != CE_None)
     786           0 :             break;
     787             : 
     788        2507 :         eErr = GDALRasterIO(hValBand, GF_Read, 0, iY, nXSize, 1,
     789             :                             pafTopDownValue, nXSize, 1, GDT_Float32, 0, 0);
     790             : 
     791        2507 :         if (eErr != CE_None)
     792           0 :             break;
     793             : 
     794             :         /* --------------------------------------------------------------------
     795             :          */
     796             :         /*      Attempt to interpolate any pixels that are nodata. */
     797             :         /* --------------------------------------------------------------------
     798             :          */
     799        2507 :         memset(pabyFiltMask, 0, nXSize);
     800      654985 :         for (int iX = 0; iX < nXSize; iX++)
     801             :         {
     802      652478 :             int nThisMaxSearchDist = nMaxSearchDist;
     803             : 
     804             :             // If this was a valid target - no change.
     805      652478 :             if (pabyMask[iX])
     806      341578 :                 continue;
     807             : 
     808             :             enum Quadrants
     809             :             {
     810             :                 QUAD_TOP_LEFT = 0,
     811             :                 QUAD_BOTTOM_LEFT = 1,
     812             :                 QUAD_TOP_RIGHT = 2,
     813             :                 QUAD_BOTTOM_RIGHT = 3,
     814             :             };
     815             : 
     816      310900 :             constexpr int QUAD_COUNT = 4;
     817      310900 :             double adfQuadDist[QUAD_COUNT] = {};
     818      310900 :             float afQuadValue[QUAD_COUNT] = {};
     819             : 
     820     1554500 :             for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
     821             :             {
     822     1243600 :                 adfQuadDist[iQuad] = dfMaxSearchDist + 1.0;
     823     1243600 :                 afQuadValue[iQuad] = 0.0;
     824             :             }
     825             : 
     826             :             // Step left and right by one pixel searching for the closest
     827             :             // target value for each quadrant.
     828     1558920 :             for (int iStep = 0; iStep <= nThisMaxSearchDist; iStep++)
     829             :             {
     830     1248020 :                 const int iLeftX = std::max(0, iX - iStep);
     831     1248020 :                 const int iRightX = std::min(nXSize - 1, iX + iStep);
     832             : 
     833             :                 // Top left includes current line.
     834     1248020 :                 QUAD_CHECK(adfQuadDist[QUAD_TOP_LEFT],
     835             :                            afQuadValue[QUAD_TOP_LEFT], iLeftX,
     836     1248020 :                            panTopDownY[iLeftX], iX, iY, pafTopDownValue[iLeftX],
     837             :                            nNoDataVal);
     838             : 
     839             :                 // Bottom left.
     840     1248020 :                 QUAD_CHECK(adfQuadDist[QUAD_BOTTOM_LEFT],
     841             :                            afQuadValue[QUAD_BOTTOM_LEFT], iLeftX,
     842     1248020 :                            panLastY[iLeftX], iX, iY, pafLastValue[iLeftX],
     843             :                            nNoDataVal);
     844             : 
     845             :                 // Top right and bottom right do no include center pixel.
     846     1248020 :                 if (iStep == 0)
     847      310900 :                     continue;
     848             : 
     849             :                 // Top right includes current line.
     850      937122 :                 QUAD_CHECK(adfQuadDist[QUAD_TOP_RIGHT],
     851             :                            afQuadValue[QUAD_TOP_RIGHT], iRightX,
     852      937122 :                            panTopDownY[iRightX], iX, iY,
     853      937122 :                            pafTopDownValue[iRightX], nNoDataVal);
     854             : 
     855             :                 // Bottom right.
     856      937122 :                 QUAD_CHECK(adfQuadDist[QUAD_BOTTOM_RIGHT],
     857             :                            afQuadValue[QUAD_BOTTOM_RIGHT], iRightX,
     858      937122 :                            panLastY[iRightX], iX, iY, pafLastValue[iRightX],
     859             :                            nNoDataVal);
     860             : 
     861             :                 // Every four steps, recompute maximum distance.
     862      937122 :                 if ((iStep & 0x3) == 0)
     863        1171 :                     nThisMaxSearchDist = static_cast<int>(floor(
     864             :                         std::max(std::max(adfQuadDist[0], adfQuadDist[1]),
     865        1171 :                                  std::max(adfQuadDist[2], adfQuadDist[3]))));
     866             :             }
     867             : 
     868      310900 :             bool bHasSrcValues = false;
     869      310900 :             if (bNearest)
     870             :             {
     871          12 :                 double dfNearestDist = dfMaxSearchDist + 1;
     872          12 :                 float fNearestValue = 0.0f;
     873             : 
     874          60 :                 for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
     875             :                 {
     876          48 :                     if (adfQuadDist[iQuad] < dfNearestDist)
     877             :                     {
     878          11 :                         bHasSrcValues = true;
     879          11 :                         if (!bHasNoData || afQuadValue[iQuad] != fNoData)
     880             :                         {
     881          10 :                             fNearestValue = afQuadValue[iQuad];
     882          10 :                             dfNearestDist = adfQuadDist[iQuad];
     883             :                         }
     884             :                     }
     885             :                 }
     886             : 
     887          12 :                 if (bHasSrcValues)
     888             :                 {
     889          10 :                     pabyFiltMask[iX] = 255;
     890          10 :                     if (dfNearestDist <= dfMaxSearchDist)
     891             :                     {
     892           7 :                         pabyMask[iX] = 255;
     893           7 :                         pafScanline[iX] = fNearestValue;
     894             :                     }
     895             :                     else
     896           3 :                         pafScanline[iX] = fNoData;
     897             :                 }
     898             :             }
     899             :             else
     900             :             {
     901      310888 :                 double dfWeightSum = 0.0;
     902      310888 :                 double dfValueSum = 0.0;
     903             : 
     904     1554440 :                 for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
     905             :                 {
     906     1243550 :                     if (adfQuadDist[iQuad] <= dfMaxSearchDist)
     907             :                     {
     908      132924 :                         bHasSrcValues = true;
     909      132924 :                         if (!bHasNoData || afQuadValue[iQuad] != fNoData)
     910             :                         {
     911      132923 :                             const double dfWeight = 1.0 / adfQuadDist[iQuad];
     912      132923 :                             dfWeightSum += dfWeight;
     913      132923 :                             dfValueSum += afQuadValue[iQuad] * dfWeight;
     914             :                         }
     915             :                     }
     916             :                 }
     917             : 
     918      310888 :                 if (bHasSrcValues)
     919             :                 {
     920       56598 :                     pabyFiltMask[iX] = 255;
     921       56598 :                     if (dfWeightSum > 0.0)
     922             :                     {
     923       56598 :                         pabyMask[iX] = 255;
     924       56598 :                         pafScanline[iX] =
     925       56598 :                             static_cast<float>(dfValueSum / dfWeightSum);
     926             :                     }
     927             :                     else
     928           0 :                         pafScanline[iX] = fNoData;
     929             :                 }
     930             :             }
     931             :         }
     932             : 
     933             :         /* --------------------------------------------------------------------
     934             :          */
     935             :         /*      Write out the updated data and mask information. */
     936             :         /* --------------------------------------------------------------------
     937             :          */
     938        2507 :         eErr = GDALRasterIO(hTargetBand, GF_Write, 0, iY, nXSize, 1,
     939             :                             pafScanline, nXSize, 1, GDT_Float32, 0, 0);
     940             : 
     941        2507 :         if (eErr != CE_None)
     942           0 :             break;
     943             : 
     944        2507 :         if (poTmpMaskDS != nullptr)
     945             :         {
     946             :             // Update (copy of) mask band when it has been provided by the
     947             :             // user
     948           5 :             eErr = GDALRasterIO(hMaskBand, GF_Write, 0, iY, nXSize, 1, pabyMask,
     949             :                                 nXSize, 1, GDT_Byte, 0, 0);
     950             : 
     951           5 :             if (eErr != CE_None)
     952           0 :                 break;
     953             :         }
     954             : 
     955        2507 :         eErr = GDALRasterIO(hFiltMaskBand, GF_Write, 0, iY, nXSize, 1,
     956             :                             pabyFiltMask, nXSize, 1, GDT_Byte, 0, 0);
     957             : 
     958        2507 :         if (eErr != CE_None)
     959           0 :             break;
     960             : 
     961             :         /* --------------------------------------------------------------------
     962             :          */
     963             :         /*      Flip this/last buffers. */
     964             :         /* --------------------------------------------------------------------
     965             :          */
     966        2507 :         std::swap(pafThisValue, pafLastValue);
     967        2507 :         std::swap(panThisY, panLastY);
     968             : 
     969             :         /* --------------------------------------------------------------------
     970             :          */
     971             :         /*      report progress. */
     972             :         /* --------------------------------------------------------------------
     973             :          */
     974        2507 :         if (!pfnProgress(
     975             :                 dfProgressRatio *
     976        2507 :                     (0.5 + 0.5 * (nYSize - iY) / static_cast<double>(nYSize)),
     977             :                 "Filling...", pProgressArg))
     978             :         {
     979           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     980           0 :             eErr = CE_Failure;
     981             :         }
     982             :     }
     983             : 
     984             :     /* ==================================================================== */
     985             :     /*      Now we will do iterative average filters over the               */
     986             :     /*      interpolated values to smooth things out and make linear        */
     987             :     /*      artifacts less obvious.                                         */
     988             :     /* ==================================================================== */
     989         117 :     if (eErr == CE_None && nSmoothingIterations > 0)
     990             :     {
     991          92 :         if (poTmpMaskDS == nullptr)
     992             :         {
     993             :             // Force masks to be to flushed and recomputed when the user
     994             :             // didn't pass a user-provided hMaskBand, and we assigned it
     995             :             // to be the mask band of hTargetBand.
     996          91 :             GDALFlushRasterCache(hMaskBand);
     997             :         }
     998             : 
     999          92 :         void *pScaledProgress = GDALCreateScaledProgress(
    1000             :             dfProgressRatio, 1.0, pfnProgress, pProgressArg);
    1001             : 
    1002          92 :         eErr = GDALMultiFilter(hTargetBand, hMaskBand, hFiltMaskBand,
    1003             :                                nSmoothingIterations, GDALScaledProgress,
    1004             :                                pScaledProgress);
    1005             : 
    1006          92 :         GDALDestroyScaledProgress(pScaledProgress);
    1007             :     }
    1008             : 
    1009             : /* -------------------------------------------------------------------- */
    1010             : /*      Close and clean up temporary files. Free working buffers        */
    1011             : /* -------------------------------------------------------------------- */
    1012          25 : end:
    1013         117 :     CPLFree(panLastY);
    1014         117 :     CPLFree(panThisY);
    1015         117 :     CPLFree(panTopDownY);
    1016         117 :     CPLFree(pafLastValue);
    1017         117 :     CPLFree(pafThisValue);
    1018         117 :     CPLFree(pafTopDownValue);
    1019         117 :     CPLFree(pafScanline);
    1020         117 :     CPLFree(pabyMask);
    1021         117 :     CPLFree(pabyFiltMask);
    1022             : 
    1023         117 :     return eErr;
    1024             : }

Generated by: LCOV version 1.14