LCOV - code coverage report
Current view: top level - alg - gdalwarpoperation.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1097 1261 87.0 %
Date: 2025-01-18 12:42:00 Functions: 34 38 89.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  High Performance Image Reprojector
       4             :  * Purpose:  Implementation of the GDALWarpOperation class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdalwarper.h"
      16             : 
      17             : #include <climits>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : 
      23             : #include <algorithm>
      24             : #include <limits>
      25             : #include <map>
      26             : #include <memory>
      27             : #include <mutex>
      28             : 
      29             : #include "cpl_config.h"
      30             : #include "cpl_conv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_mask.h"
      33             : #include "cpl_multiproc.h"
      34             : #include "cpl_string.h"
      35             : #include "cpl_vsi.h"
      36             : #include "gdal.h"
      37             : #include "gdal_priv.h"
      38             : #include "gdal_alg_priv.h"
      39             : #include "ogr_api.h"
      40             : #include "ogr_core.h"
      41             : 
      42             : struct _GDALWarpChunk
      43             : {
      44             :     int dx, dy, dsx, dsy;
      45             :     int sx, sy, ssx, ssy;
      46             :     double sExtraSx, sExtraSy;
      47             : };
      48             : 
      49             : struct GDALWarpPrivateData
      50             : {
      51             :     int nStepCount = 0;
      52             :     std::vector<int> abSuccess{};
      53             :     std::vector<double> adfDstX{};
      54             :     std::vector<double> adfDstY{};
      55             : };
      56             : 
      57             : static std::mutex gMutex{};
      58             : static std::map<GDALWarpOperation *, std::unique_ptr<GDALWarpPrivateData>>
      59             :     gMapPrivate{};
      60             : 
      61             : static GDALWarpPrivateData *
      62         434 : GetWarpPrivateData(GDALWarpOperation *poWarpOperation)
      63             : {
      64         868 :     std::lock_guard<std::mutex> oLock(gMutex);
      65         434 :     auto oItem = gMapPrivate.find(poWarpOperation);
      66         434 :     if (oItem != gMapPrivate.end())
      67             :     {
      68         292 :         return oItem->second.get();
      69             :     }
      70             :     else
      71             :     {
      72         142 :         gMapPrivate[poWarpOperation] =
      73         284 :             std::unique_ptr<GDALWarpPrivateData>(new GDALWarpPrivateData());
      74         142 :         return gMapPrivate[poWarpOperation].get();
      75             :     }
      76             : }
      77             : 
      78             : /************************************************************************/
      79             : /* ==================================================================== */
      80             : /*                          GDALWarpOperation                           */
      81             : /* ==================================================================== */
      82             : /************************************************************************/
      83             : 
      84             : /**
      85             :  * \class GDALWarpOperation "gdalwarper.h"
      86             :  *
      87             :  * High level image warping class.
      88             : 
      89             : <h2>Warper Design</h2>
      90             : 
      91             : The overall GDAL high performance image warper is split into a few components.
      92             : 
      93             :  - The transformation between input and output file coordinates is handled
      94             : via GDALTransformerFunc() implementations such as the one returned by
      95             : GDALCreateGenImgProjTransformer().  The transformers are ultimately responsible
      96             : for translating pixel/line locations on the destination image to pixel/line
      97             : locations on the source image.
      98             : 
      99             :  - In order to handle images too large to hold in RAM, the warper needs to
     100             : segment large images.  This is the responsibility of the GDALWarpOperation
     101             : class.  The GDALWarpOperation::ChunkAndWarpImage() invokes
     102             : GDALWarpOperation::WarpRegion() on chunks of output and input image that
     103             : are small enough to hold in the amount of memory allowed by the application.
     104             : This process is described in greater detail in the <b>Image Chunking</b>
     105             : section.
     106             : 
     107             :  - The GDALWarpOperation::WarpRegion() function creates and loads an output
     108             : image buffer, and then calls WarpRegionToBuffer().
     109             : 
     110             :  - GDALWarpOperation::WarpRegionToBuffer() is responsible for loading the
     111             : source imagery corresponding to a particular output region, and generating
     112             : masks and density masks from the source and destination imagery using
     113             : the generator functions found in the GDALWarpOptions structure.  Binds this
     114             : all into an instance of GDALWarpKernel on which the
     115             : GDALWarpKernel::PerformWarp() method is called.
     116             : 
     117             :  - GDALWarpKernel does the actual image warping, but is given an input image
     118             : and an output image to operate on.  The GDALWarpKernel does no IO, and in
     119             : fact knows nothing about GDAL.  It invokes the transformation function to
     120             : get sample locations, builds output values based on the resampling algorithm
     121             : in use.  It also takes any validity and density masks into account during
     122             : this operation.
     123             : 
     124             : <h3>Chunk Size Selection</h3>
     125             : 
     126             : The GDALWarpOptions ChunkAndWarpImage() method is responsible for invoking
     127             : the WarpRegion() method on appropriate sized output chunks such that the
     128             : memory required for the output image buffer, input image buffer and any
     129             : required density and validity buffers is less than or equal to the application
     130             : defined maximum memory available for use.
     131             : 
     132             : It checks the memory required by walking the edges of the output region,
     133             : transforming the locations back into source pixel/line coordinates and
     134             : establishing a bounding rectangle of source imagery that would be required
     135             : for the output area.  This is actually accomplished by the private
     136             : GDALWarpOperation::ComputeSourceWindow() method.
     137             : 
     138             : Then memory requirements are used by totaling the memory required for all
     139             : output bands, input bands, validity masks and density masks.  If this is
     140             : greater than the GDALWarpOptions::dfWarpMemoryLimit then the destination
     141             : region is divided in two (splitting the longest dimension), and
     142             : ChunkAndWarpImage() recursively invoked on each destination subregion.
     143             : 
     144             : <h3>Validity and Density Masks Generation</h3>
     145             : 
     146             : Fill in ways in which the validity and density masks may be generated here.
     147             : Note that detailed semantics of the masks should be found in
     148             : GDALWarpKernel.
     149             : */
     150             : 
     151             : /************************************************************************/
     152             : /*                         GDALWarpOperation()                          */
     153             : /************************************************************************/
     154             : 
     155        1405 : GDALWarpOperation::GDALWarpOperation()
     156             :     : psOptions(nullptr), hIOMutex(nullptr), hWarpMutex(nullptr),
     157             :       nChunkListCount(0), nChunkListMax(0), pasChunkList(nullptr),
     158        1405 :       bReportTimings(FALSE), nLastTimeReported(0), psThreadData(nullptr)
     159             : {
     160        1405 : }
     161             : 
     162             : /************************************************************************/
     163             : /*                         ~GDALWarpOperation()                         */
     164             : /************************************************************************/
     165             : 
     166        1902 : GDALWarpOperation::~GDALWarpOperation()
     167             : 
     168             : {
     169             :     {
     170        2810 :         std::lock_guard<std::mutex> oLock(gMutex);
     171        1405 :         auto oItem = gMapPrivate.find(this);
     172        1405 :         if (oItem != gMapPrivate.end())
     173             :         {
     174         142 :             gMapPrivate.erase(oItem);
     175             :         }
     176             :     }
     177             : 
     178        1405 :     WipeOptions();
     179             : 
     180        1405 :     if (hIOMutex != nullptr)
     181             :     {
     182           5 :         CPLDestroyMutex(hIOMutex);
     183           5 :         CPLDestroyMutex(hWarpMutex);
     184             :     }
     185             : 
     186        1405 :     WipeChunkList();
     187        1405 :     if (psThreadData)
     188        1401 :         GWKThreadsEnd(psThreadData);
     189        1902 : }
     190             : 
     191             : /************************************************************************/
     192             : /*                             GetOptions()                             */
     193             : /************************************************************************/
     194             : 
     195             : /** Return warp options */
     196        1272 : const GDALWarpOptions *GDALWarpOperation::GetOptions()
     197             : 
     198             : {
     199        1272 :     return psOptions;
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                            WipeOptions()                             */
     204             : /************************************************************************/
     205             : 
     206        1409 : void GDALWarpOperation::WipeOptions()
     207             : 
     208             : {
     209        1409 :     if (psOptions != nullptr)
     210             :     {
     211        1405 :         GDALDestroyWarpOptions(psOptions);
     212        1405 :         psOptions = nullptr;
     213             :     }
     214        1409 : }
     215             : 
     216             : /************************************************************************/
     217             : /*                          ValidateOptions()                           */
     218             : /************************************************************************/
     219             : 
     220        1405 : int GDALWarpOperation::ValidateOptions()
     221             : 
     222             : {
     223        1405 :     if (psOptions == nullptr)
     224             :     {
     225           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     226             :                  "GDALWarpOptions.Validate(): "
     227             :                  "no options currently initialized.");
     228           0 :         return FALSE;
     229             :     }
     230             : 
     231        1405 :     if (psOptions->dfWarpMemoryLimit < 100000.0)
     232             :     {
     233           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     234             :                  "GDALWarpOptions.Validate(): "
     235             :                  "dfWarpMemoryLimit=%g is unreasonably small.",
     236           0 :                  psOptions->dfWarpMemoryLimit);
     237           0 :         return FALSE;
     238             :     }
     239             : 
     240        1405 :     if (psOptions->eResampleAlg != GRA_NearestNeighbour &&
     241         764 :         psOptions->eResampleAlg != GRA_Bilinear &&
     242         391 :         psOptions->eResampleAlg != GRA_Cubic &&
     243         273 :         psOptions->eResampleAlg != GRA_CubicSpline &&
     244         209 :         psOptions->eResampleAlg != GRA_Lanczos &&
     245         152 :         psOptions->eResampleAlg != GRA_Average &&
     246          81 :         psOptions->eResampleAlg != GRA_RMS &&
     247          71 :         psOptions->eResampleAlg != GRA_Mode &&
     248          47 :         psOptions->eResampleAlg != GRA_Max &&
     249          41 :         psOptions->eResampleAlg != GRA_Min &&
     250          36 :         psOptions->eResampleAlg != GRA_Med &&
     251          30 :         psOptions->eResampleAlg != GRA_Q1 &&
     252          25 :         psOptions->eResampleAlg != GRA_Q3 && psOptions->eResampleAlg != GRA_Sum)
     253             :     {
     254           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     255             :                  "GDALWarpOptions.Validate(): "
     256             :                  "eResampleArg=%d is not a supported value.",
     257           0 :                  psOptions->eResampleAlg);
     258           0 :         return FALSE;
     259             :     }
     260             : 
     261        1405 :     if (static_cast<int>(psOptions->eWorkingDataType) < 1 ||
     262        1405 :         static_cast<int>(psOptions->eWorkingDataType) >= GDT_TypeCount)
     263             :     {
     264           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     265             :                  "GDALWarpOptions.Validate(): "
     266             :                  "eWorkingDataType=%d is not a supported value.",
     267           0 :                  psOptions->eWorkingDataType);
     268           0 :         return FALSE;
     269             :     }
     270             : 
     271        1654 :     if (GDALDataTypeIsComplex(psOptions->eWorkingDataType) != 0 &&
     272         249 :         (psOptions->eResampleAlg == GRA_Mode ||
     273         249 :          psOptions->eResampleAlg == GRA_Max ||
     274         249 :          psOptions->eResampleAlg == GRA_Min ||
     275         249 :          psOptions->eResampleAlg == GRA_Med ||
     276         249 :          psOptions->eResampleAlg == GRA_Q1 ||
     277         249 :          psOptions->eResampleAlg == GRA_Q3))
     278             :     {
     279             : 
     280           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     281             :                  "GDALWarpOptions.Validate(): "
     282             :                  "min/max/qnt not supported for complex valued data.");
     283           0 :         return FALSE;
     284             :     }
     285             : 
     286        1405 :     if (psOptions->hSrcDS == nullptr)
     287             :     {
     288           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     289             :                  "GDALWarpOptions.Validate(): "
     290             :                  "hSrcDS is not set.");
     291           0 :         return FALSE;
     292             :     }
     293             : 
     294        1405 :     if (psOptions->nBandCount == 0)
     295             :     {
     296           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     297             :                  "GDALWarpOptions.Validate(): "
     298             :                  "nBandCount=0, no bands configured!");
     299           0 :         return FALSE;
     300             :     }
     301             : 
     302        1405 :     if (psOptions->panSrcBands == nullptr)
     303             :     {
     304           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     305             :                  "GDALWarpOptions.Validate(): "
     306             :                  "panSrcBands is NULL.");
     307           0 :         return FALSE;
     308             :     }
     309             : 
     310        1405 :     if (psOptions->hDstDS != nullptr && psOptions->panDstBands == nullptr)
     311             :     {
     312           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     313             :                  "GDALWarpOptions.Validate(): "
     314             :                  "panDstBands is NULL.");
     315           0 :         return FALSE;
     316             :     }
     317             : 
     318        3276 :     for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
     319             :     {
     320        3742 :         if (psOptions->panSrcBands[iBand] < 1 ||
     321        1871 :             psOptions->panSrcBands[iBand] >
     322        1871 :                 GDALGetRasterCount(psOptions->hSrcDS))
     323             :         {
     324           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     325             :                      "panSrcBands[%d] = %d ... out of range for dataset.",
     326           0 :                      iBand, psOptions->panSrcBands[iBand]);
     327           0 :             return FALSE;
     328             :         }
     329        3729 :         if (psOptions->hDstDS != nullptr &&
     330        1858 :             (psOptions->panDstBands[iBand] < 1 ||
     331        1858 :              psOptions->panDstBands[iBand] >
     332        1858 :                  GDALGetRasterCount(psOptions->hDstDS)))
     333             :         {
     334           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     335             :                      "panDstBands[%d] = %d ... out of range for dataset.",
     336           0 :                      iBand, psOptions->panDstBands[iBand]);
     337           0 :             return FALSE;
     338             :         }
     339             : 
     340        3729 :         if (psOptions->hDstDS != nullptr &&
     341        1858 :             GDALGetRasterAccess(GDALGetRasterBand(
     342        1858 :                 psOptions->hDstDS, psOptions->panDstBands[iBand])) ==
     343             :                 GA_ReadOnly)
     344             :         {
     345           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     346             :                      "Destination band %d appears to be read-only.",
     347           0 :                      psOptions->panDstBands[iBand]);
     348           0 :             return FALSE;
     349             :         }
     350             :     }
     351             : 
     352        1405 :     if (psOptions->nBandCount == 0)
     353             :     {
     354           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     355             :                  "GDALWarpOptions.Validate(): "
     356             :                  "nBandCount=0, no bands configured!");
     357           0 :         return FALSE;
     358             :     }
     359             : 
     360        1405 :     if (psOptions->pfnProgress == nullptr)
     361             :     {
     362           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     363             :                  "GDALWarpOptions.Validate(): "
     364             :                  "pfnProgress is NULL.");
     365           0 :         return FALSE;
     366             :     }
     367             : 
     368        1405 :     if (psOptions->pfnTransformer == nullptr)
     369             :     {
     370           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     371             :                  "GDALWarpOptions.Validate(): "
     372             :                  "pfnTransformer is NULL.");
     373           0 :         return FALSE;
     374             :     }
     375             : 
     376             :     const char *pszSampleSteps =
     377        1405 :         CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
     378        1405 :     if (pszSampleSteps)
     379             :     {
     380           8 :         if (!EQUAL(pszSampleSteps, "ALL") && atoi(pszSampleSteps) < 2)
     381             :         {
     382           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     383             :                      "GDALWarpOptions.Validate(): "
     384             :                      "SAMPLE_STEPS warp option has illegal value.");
     385           0 :             return FALSE;
     386             :         }
     387             :     }
     388             : 
     389        1405 :     if (psOptions->nSrcAlphaBand > 0)
     390             :     {
     391         172 :         if (psOptions->hSrcDS == nullptr ||
     392          86 :             psOptions->nSrcAlphaBand > GDALGetRasterCount(psOptions->hSrcDS))
     393             :         {
     394           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     395             :                      "nSrcAlphaBand = %d ... out of range for dataset.",
     396           0 :                      psOptions->nSrcAlphaBand);
     397           0 :             return FALSE;
     398             :         }
     399             :     }
     400             : 
     401        1405 :     if (psOptions->nDstAlphaBand > 0)
     402             :     {
     403         462 :         if (psOptions->hDstDS == nullptr ||
     404         231 :             psOptions->nDstAlphaBand > GDALGetRasterCount(psOptions->hDstDS))
     405             :         {
     406           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     407             :                      "nDstAlphaBand = %d ... out of range for dataset.",
     408           0 :                      psOptions->nDstAlphaBand);
     409           0 :             return FALSE;
     410             :         }
     411             :     }
     412             : 
     413        1405 :     if (psOptions->nSrcAlphaBand > 0 &&
     414          86 :         psOptions->pfnSrcDensityMaskFunc != nullptr)
     415             :     {
     416           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     417             :                  "GDALWarpOptions.Validate(): "
     418             :                  "pfnSrcDensityMaskFunc provided as well as a SrcAlphaBand.");
     419           0 :         return FALSE;
     420             :     }
     421             : 
     422        1405 :     if (psOptions->nDstAlphaBand > 0 &&
     423         231 :         psOptions->pfnDstDensityMaskFunc != nullptr)
     424             :     {
     425           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     426             :                  "GDALWarpOptions.Validate(): "
     427             :                  "pfnDstDensityMaskFunc provided as well as a DstAlphaBand.");
     428           0 :         return FALSE;
     429             :     }
     430             : 
     431        2810 :     const bool bErrorOutIfEmptySourceWindow = CPLFetchBool(
     432        1405 :         psOptions->papszWarpOptions, "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
     433        1753 :     if (!bErrorOutIfEmptySourceWindow &&
     434         348 :         CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST") == nullptr)
     435             :     {
     436           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     437             :                  "GDALWarpOptions.Validate(): "
     438             :                  "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW=FALSE can only be used "
     439             :                  "if INIT_DEST is set");
     440           0 :         return FALSE;
     441             :     }
     442             : 
     443        1405 :     return TRUE;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                            SetAlphaMax()                             */
     448             : /************************************************************************/
     449             : 
     450         316 : static void SetAlphaMax(GDALWarpOptions *psOptions, GDALRasterBandH hBand,
     451             :                         const char *pszKey)
     452             : {
     453             :     const char *pszNBits =
     454         316 :         GDALGetMetadataItem(hBand, "NBITS", "IMAGE_STRUCTURE");
     455         316 :     const char *pszAlphaMax = nullptr;
     456         316 :     if (pszNBits)
     457             :     {
     458           4 :         pszAlphaMax = CPLSPrintf("%u", (1U << atoi(pszNBits)) - 1U);
     459             :     }
     460         312 :     else if (GDALGetRasterDataType(hBand) == GDT_Int16)
     461             :     {
     462          20 :         pszAlphaMax = "32767";
     463             :     }
     464         292 :     else if (GDALGetRasterDataType(hBand) == GDT_UInt16)
     465             :     {
     466          20 :         pszAlphaMax = "65535";
     467             :     }
     468             : 
     469         316 :     if (pszAlphaMax != nullptr)
     470          44 :         psOptions->papszWarpOptions =
     471          44 :             CSLSetNameValue(psOptions->papszWarpOptions, pszKey, pszAlphaMax);
     472             :     else
     473         272 :         CPLDebug("WARP", "SetAlphaMax: AlphaMax not set.");
     474         316 : }
     475             : 
     476             : /************************************************************************/
     477             : /*                            SetTieStrategy()                          */
     478             : /************************************************************************/
     479             : 
     480        1405 : static void SetTieStrategy(GDALWarpOptions *psOptions, CPLErr *peErr)
     481             : {
     482        1405 :     if (const char *pszTieStrategy =
     483        1405 :             CSLFetchNameValue(psOptions->papszWarpOptions, "MODE_TIES"))
     484             :     {
     485          14 :         if (EQUAL(pszTieStrategy, "FIRST"))
     486             :         {
     487           4 :             psOptions->eTieStrategy = GWKTS_First;
     488             :         }
     489          10 :         else if (EQUAL(pszTieStrategy, "MIN"))
     490             :         {
     491           4 :             psOptions->eTieStrategy = GWKTS_Min;
     492             :         }
     493           6 :         else if (EQUAL(pszTieStrategy, "MAX"))
     494             :         {
     495           4 :             psOptions->eTieStrategy = GWKTS_Max;
     496             :         }
     497             :         else
     498             :         {
     499           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     500             :                      "Unknown value of MODE_TIES: %s", pszTieStrategy);
     501           2 :             *peErr = CE_Failure;
     502             :         }
     503             :     }
     504        1405 : }
     505             : 
     506             : /************************************************************************/
     507             : /*                             Initialize()                             */
     508             : /************************************************************************/
     509             : 
     510             : /**
     511             :  * \fn CPLErr GDALWarpOperation::Initialize( const GDALWarpOptions * );
     512             :  *
     513             :  * This method initializes the GDALWarpOperation's concept of the warp
     514             :  * options in effect.  It creates an internal copy of the GDALWarpOptions
     515             :  * structure and defaults a variety of additional fields in the internal
     516             :  * copy if not set in the provided warp options.
     517             :  *
     518             :  * Defaulting operations include:
     519             :  *  - If the nBandCount is 0, it will be set to the number of bands in the
     520             :  *    source image (which must match the output image) and the panSrcBands
     521             :  *    and panDstBands will be populated.
     522             :  *
     523             :  * @param psNewOptions input set of warp options.  These are copied and may
     524             :  * be destroyed after this call by the application.
     525             :  *
     526             :  * @return CE_None on success or CE_Failure if an error occurs.
     527             :  */
     528             : 
     529        1405 : CPLErr GDALWarpOperation::Initialize(const GDALWarpOptions *psNewOptions)
     530             : 
     531             : {
     532             :     /* -------------------------------------------------------------------- */
     533             :     /*      Copy the passed in options.                                     */
     534             :     /* -------------------------------------------------------------------- */
     535        1405 :     if (psOptions != nullptr)
     536           0 :         WipeOptions();
     537             : 
     538        1405 :     CPLErr eErr = CE_None;
     539             : 
     540        1405 :     psOptions = GDALCloneWarpOptions(psNewOptions);
     541        2810 :     psOptions->papszWarpOptions =
     542        1405 :         CSLSetNameValue(psOptions->papszWarpOptions, "EXTRA_ELTS",
     543             :                         CPLSPrintf("%d", WARP_EXTRA_ELTS));
     544             : 
     545             :     /* -------------------------------------------------------------------- */
     546             :     /*      Default band mapping if missing.                                */
     547             :     /* -------------------------------------------------------------------- */
     548           0 :     if (psOptions->nBandCount == 0 && psOptions->hSrcDS != nullptr &&
     549        1405 :         psOptions->hDstDS != nullptr &&
     550           0 :         GDALGetRasterCount(psOptions->hSrcDS) ==
     551           0 :             GDALGetRasterCount(psOptions->hDstDS))
     552             :     {
     553           0 :         GDALWarpInitDefaultBandMapping(psOptions,
     554           0 :                                        GDALGetRasterCount(psOptions->hSrcDS));
     555             :     }
     556             : 
     557        1405 :     GDALWarpResolveWorkingDataType(psOptions);
     558        1405 :     SetTieStrategy(psOptions, &eErr);
     559             : 
     560             :     /* -------------------------------------------------------------------- */
     561             :     /*      Default memory available.                                       */
     562             :     /*                                                                      */
     563             :     /*      For now we default to 64MB of RAM, but eventually we should     */
     564             :     /*      try various schemes to query physical RAM.  This can            */
     565             :     /*      certainly be done on Win32 and Linux.                           */
     566             :     /* -------------------------------------------------------------------- */
     567        1405 :     if (psOptions->dfWarpMemoryLimit == 0.0)
     568             :     {
     569        1150 :         psOptions->dfWarpMemoryLimit = 64.0 * 1024 * 1024;
     570             :     }
     571             : 
     572             :     /* -------------------------------------------------------------------- */
     573             :     /*      Are we doing timings?                                           */
     574             :     /* -------------------------------------------------------------------- */
     575        1405 :     bReportTimings =
     576        1405 :         CPLFetchBool(psOptions->papszWarpOptions, "REPORT_TIMINGS", false);
     577             : 
     578             :     /* -------------------------------------------------------------------- */
     579             :     /*      Support creating cutline from text warpoption.                  */
     580             :     /* -------------------------------------------------------------------- */
     581             :     const char *pszCutlineWKT =
     582        1405 :         CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE");
     583             : 
     584        1405 :     if (pszCutlineWKT && psOptions->hCutline == nullptr)
     585             :     {
     586          33 :         char *pszWKTTmp = const_cast<char *>(pszCutlineWKT);
     587          66 :         if (OGR_G_CreateFromWkt(&pszWKTTmp, nullptr,
     588             :                                 reinterpret_cast<OGRGeometryH *>(
     589          33 :                                     &(psOptions->hCutline))) != OGRERR_NONE)
     590             :         {
     591           2 :             eErr = CE_Failure;
     592           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     593             :                      "Failed to parse CUTLINE geometry wkt.");
     594             :         }
     595             :     }
     596             :     const char *pszBD =
     597        1405 :         CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE_BLEND_DIST");
     598        1405 :     if (pszBD)
     599           0 :         psOptions->dfCutlineBlendDist = CPLAtof(pszBD);
     600             : 
     601             :     /* -------------------------------------------------------------------- */
     602             :     /*      Set SRC_ALPHA_MAX if not provided.                              */
     603             :     /* -------------------------------------------------------------------- */
     604        1405 :     if (psOptions->hSrcDS != nullptr && psOptions->nSrcAlphaBand > 0 &&
     605        2896 :         psOptions->nSrcAlphaBand <= GDALGetRasterCount(psOptions->hSrcDS) &&
     606          86 :         CSLFetchNameValue(psOptions->papszWarpOptions, "SRC_ALPHA_MAX") ==
     607             :             nullptr)
     608             :     {
     609             :         GDALRasterBandH hSrcAlphaBand =
     610          86 :             GDALGetRasterBand(psOptions->hSrcDS, psOptions->nSrcAlphaBand);
     611          86 :         SetAlphaMax(psOptions, hSrcAlphaBand, "SRC_ALPHA_MAX");
     612             :     }
     613             : 
     614             :     /* -------------------------------------------------------------------- */
     615             :     /*      Set DST_ALPHA_MAX if not provided.                              */
     616             :     /* -------------------------------------------------------------------- */
     617        1392 :     if (psOptions->hDstDS != nullptr && psOptions->nDstAlphaBand > 0 &&
     618        3028 :         psOptions->nDstAlphaBand <= GDALGetRasterCount(psOptions->hDstDS) &&
     619         231 :         CSLFetchNameValue(psOptions->papszWarpOptions, "DST_ALPHA_MAX") ==
     620             :             nullptr)
     621             :     {
     622             :         GDALRasterBandH hDstAlphaBand =
     623         230 :             GDALGetRasterBand(psOptions->hDstDS, psOptions->nDstAlphaBand);
     624         230 :         SetAlphaMax(psOptions, hDstAlphaBand, "DST_ALPHA_MAX");
     625             :     }
     626             : 
     627             :     /* -------------------------------------------------------------------- */
     628             :     /*      If the options don't validate, then wipe them.                  */
     629             :     /* -------------------------------------------------------------------- */
     630        1405 :     if (!ValidateOptions())
     631           0 :         eErr = CE_Failure;
     632             : 
     633        1405 :     if (eErr != CE_None)
     634             :     {
     635           4 :         WipeOptions();
     636             :     }
     637             :     else
     638             :     {
     639        2802 :         psThreadData = GWKThreadsCreate(psOptions->papszWarpOptions,
     640        1401 :                                         psOptions->pfnTransformer,
     641        1401 :                                         psOptions->pTransformerArg);
     642        1401 :         if (psThreadData == nullptr)
     643           0 :             eErr = CE_Failure;
     644             : 
     645             :         /* --------------------------------------------------------------------
     646             :          */
     647             :         /*      Compute dstcoordinates of a few special points. */
     648             :         /* --------------------------------------------------------------------
     649             :          */
     650             : 
     651             :         // South and north poles. Do not exactly take +/-90 as the
     652             :         // round-tripping of the longitude value fails with some projections.
     653        4203 :         for (double dfY : {-89.9999, 89.9999})
     654             :         {
     655        2802 :             double dfX = 0;
     656        2802 :             if ((GDALIsTransformer(psOptions->pTransformerArg,
     657        2090 :                                    GDAL_APPROX_TRANSFORMER_CLASS_NAME) &&
     658        2090 :                  GDALTransformLonLatToDestApproxTransformer(
     659        5604 :                      psOptions->pTransformerArg, &dfX, &dfY)) ||
     660        1656 :                 (GDALIsTransformer(psOptions->pTransformerArg,
     661         406 :                                    GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME) &&
     662         406 :                  GDALTransformLonLatToDestGenImgProjTransformer(
     663         406 :                      psOptions->pTransformerArg, &dfX, &dfY)))
     664             :             {
     665             :                 aDstXYSpecialPoints.emplace_back(
     666        1268 :                     std::pair<double, double>(dfX, dfY));
     667             :             }
     668             :         }
     669             : 
     670        1401 :         m_bIsTranslationOnPixelBoundaries =
     671        2802 :             GDALTransformIsTranslationOnPixelBoundaries(
     672        1580 :                 psOptions->pfnTransformer, psOptions->pTransformerArg) &&
     673         179 :             CPLTestBool(
     674             :                 CPLGetConfigOption("GDAL_WARP_USE_TRANSLATION_OPTIM", "YES"));
     675        1401 :         if (m_bIsTranslationOnPixelBoundaries)
     676             :         {
     677         173 :             CPLDebug("WARP",
     678             :                      "Using translation-on-pixel-boundaries optimization");
     679             :         }
     680             :     }
     681             : 
     682        1405 :     return eErr;
     683             : }
     684             : 
     685             : /**
     686             :  * \fn void* GDALWarpOperation::CreateDestinationBuffer(
     687             :             int nDstXSize, int nDstYSize, int *pbInitialized);
     688             :  *
     689             :  * This method creates a destination buffer for use with WarpRegionToBuffer.
     690             :  * The output is initialized based on the INIT_DEST settings.
     691             :  *
     692             :  * @param nDstXSize Width of output window on destination buffer to be produced.
     693             :  * @param nDstYSize Height of output window on destination buffer to be
     694             :  produced.
     695             :  * @param pbInitialized Filled with boolean indicating if the buffer was
     696             :  initialized.
     697             :  *
     698             :  * @return Buffer capable for use as a warp operation output destination
     699             :  */
     700        2303 : void *GDALWarpOperation::CreateDestinationBuffer(int nDstXSize, int nDstYSize,
     701             :                                                  int *pbInitialized)
     702             : {
     703             : 
     704             :     /* -------------------------------------------------------------------- */
     705             :     /*      Allocate block of memory large enough to hold all the bands     */
     706             :     /*      for this block.                                                 */
     707             :     /* -------------------------------------------------------------------- */
     708        2303 :     const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
     709             : 
     710        2303 :     void *pDstBuffer = VSI_MALLOC3_VERBOSE(
     711             :         cpl::fits_on<int>(nWordSize * psOptions->nBandCount), nDstXSize,
     712             :         nDstYSize);
     713        2303 :     if (pDstBuffer)
     714             :     {
     715        2303 :         InitializeDestinationBuffer(pDstBuffer, nDstXSize, nDstYSize,
     716             :                                     pbInitialized);
     717             :     }
     718        2303 :     return pDstBuffer;
     719             : }
     720             : 
     721             : /**
     722             :  * This method initializes a destination buffer for use with WarpRegionToBuffer.
     723             :  *
     724             :  * It is initialized based on the INIT_DEST settings.
     725             :  *
     726             :  * This method is called by CreateDestinationBuffer().
     727             :  * It is meant at being used by callers that have already allocated the
     728             :  * destination buffer without using CreateDestinationBuffer().
     729             :  *
     730             :  * @param pDstBuffer Buffer of size
     731             :  *                   GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType) *
     732             :  *                   nDstXSize * nDstYSize * psOptions->nBandCount bytes.
     733             :  * @param nDstXSize Width of output window on destination buffer to be produced.
     734             :  * @param nDstYSize Height of output window on destination buffer to be
     735             :  *                  produced.
     736             :  * @param pbInitialized Filled with boolean indicating if the buffer was
     737             :  *                      initialized.
     738             :  * @since 3.10
     739             :  */
     740        2374 : void GDALWarpOperation::InitializeDestinationBuffer(void *pDstBuffer,
     741             :                                                     int nDstXSize,
     742             :                                                     int nDstYSize,
     743             :                                                     int *pbInitialized)
     744             : {
     745        2374 :     const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
     746             : 
     747        2374 :     const GPtrDiff_t nBandSize =
     748        2374 :         static_cast<GPtrDiff_t>(nWordSize) * nDstXSize * nDstYSize;
     749             : 
     750             :     /* -------------------------------------------------------------------- */
     751             :     /*      Initialize if requested in the options */
     752             :     /* -------------------------------------------------------------------- */
     753             :     const char *pszInitDest =
     754        2374 :         CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST");
     755             : 
     756        2374 :     if (pszInitDest == nullptr || EQUAL(pszInitDest, ""))
     757             :     {
     758         400 :         if (pbInitialized != nullptr)
     759             :         {
     760         400 :             *pbInitialized = FALSE;
     761             :         }
     762         400 :         return;
     763             :     }
     764             : 
     765        1974 :     if (pbInitialized != nullptr)
     766             :     {
     767        1571 :         *pbInitialized = TRUE;
     768             :     }
     769             : 
     770             :     char **papszInitValues =
     771        1974 :         CSLTokenizeStringComplex(pszInitDest, ",", FALSE, FALSE);
     772        1974 :     const int nInitCount = CSLCount(papszInitValues);
     773             : 
     774        4545 :     for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
     775             :     {
     776        2571 :         double adfInitRealImag[2] = {0.0, 0.0};
     777             :         const char *pszBandInit =
     778        2571 :             papszInitValues[std::min(iBand, nInitCount - 1)];
     779             : 
     780        2571 :         if (EQUAL(pszBandInit, "NO_DATA") &&
     781         456 :             psOptions->padfDstNoDataReal != nullptr)
     782             :         {
     783         432 :             adfInitRealImag[0] = psOptions->padfDstNoDataReal[iBand];
     784         432 :             if (psOptions->padfDstNoDataImag != nullptr)
     785             :             {
     786         349 :                 adfInitRealImag[1] = psOptions->padfDstNoDataImag[iBand];
     787             :             }
     788             :         }
     789             :         else
     790             :         {
     791        2139 :             CPLStringToComplex(pszBandInit, adfInitRealImag + 0,
     792             :                                adfInitRealImag + 1);
     793             :         }
     794             : 
     795        2571 :         GByte *pBandData = static_cast<GByte *>(pDstBuffer) + iBand * nBandSize;
     796             : 
     797        2571 :         if (psOptions->eWorkingDataType == GDT_Byte)
     798             :         {
     799        4286 :             memset(pBandData,
     800             :                    std::max(
     801        2143 :                        0, std::min(255, static_cast<int>(adfInitRealImag[0]))),
     802             :                    nBandSize);
     803             :         }
     804         853 :         else if (!std::isnan(adfInitRealImag[0]) && adfInitRealImag[0] == 0.0 &&
     805         853 :                  !std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
     806             :         {
     807         364 :             memset(pBandData, 0, nBandSize);
     808             :         }
     809          64 :         else if (!std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
     810             :         {
     811          64 :             GDALCopyWords64(&adfInitRealImag, GDT_Float64, 0, pBandData,
     812          64 :                             psOptions->eWorkingDataType, nWordSize,
     813          64 :                             static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
     814             :         }
     815             :         else
     816             :         {
     817           0 :             GDALCopyWords64(&adfInitRealImag, GDT_CFloat64, 0, pBandData,
     818           0 :                             psOptions->eWorkingDataType, nWordSize,
     819           0 :                             static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
     820             :         }
     821             :     }
     822             : 
     823        1974 :     CSLDestroy(papszInitValues);
     824             : }
     825             : 
     826             : /**
     827             :  * \fn void GDALWarpOperation::DestroyDestinationBuffer( void *pDstBuffer )
     828             :  *
     829             :  * This method destroys a buffer previously retrieved from
     830             :  * CreateDestinationBuffer
     831             :  *
     832             :  * @param pDstBuffer destination buffer to be destroyed
     833             :  *
     834             :  */
     835        2303 : void GDALWarpOperation::DestroyDestinationBuffer(void *pDstBuffer)
     836             : {
     837        2303 :     VSIFree(pDstBuffer);
     838        2303 : }
     839             : 
     840             : /************************************************************************/
     841             : /*                         GDALCreateWarpOperation()                    */
     842             : /************************************************************************/
     843             : 
     844             : /**
     845             :  * @see GDALWarpOperation::Initialize()
     846             :  */
     847             : 
     848         149 : GDALWarpOperationH GDALCreateWarpOperation(const GDALWarpOptions *psNewOptions)
     849             : {
     850         149 :     GDALWarpOperation *poOperation = new GDALWarpOperation;
     851         149 :     if (poOperation->Initialize(psNewOptions) != CE_None)
     852             :     {
     853           0 :         delete poOperation;
     854           0 :         return nullptr;
     855             :     }
     856             : 
     857         149 :     return reinterpret_cast<GDALWarpOperationH>(poOperation);
     858             : }
     859             : 
     860             : /************************************************************************/
     861             : /*                         GDALDestroyWarpOperation()                   */
     862             : /************************************************************************/
     863             : 
     864             : /**
     865             :  * @see GDALWarpOperation::~GDALWarpOperation()
     866             :  */
     867             : 
     868         149 : void GDALDestroyWarpOperation(GDALWarpOperationH hOperation)
     869             : {
     870         149 :     if (hOperation)
     871         149 :         delete static_cast<GDALWarpOperation *>(hOperation);
     872         149 : }
     873             : 
     874             : /************************************************************************/
     875             : /*                          CollectChunkList()                          */
     876             : /************************************************************************/
     877             : 
     878        1054 : void GDALWarpOperation::CollectChunkList(int nDstXOff, int nDstYOff,
     879             :                                          int nDstXSize, int nDstYSize)
     880             : 
     881             : {
     882             :     /* -------------------------------------------------------------------- */
     883             :     /*      Collect the list of chunks to operate on.                       */
     884             :     /* -------------------------------------------------------------------- */
     885        1054 :     WipeChunkList();
     886        1054 :     CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
     887             : 
     888             :     // Sort chunks from top to bottom, and for equal y, from left to right.
     889        1054 :     if (nChunkListCount > 1)
     890             :     {
     891          50 :         std::sort(pasChunkList, pasChunkList + nChunkListCount,
     892        5822 :                   [](const GDALWarpChunk &a, const GDALWarpChunk &b)
     893             :                   {
     894        5822 :                       if (a.dy < b.dy)
     895        2610 :                           return true;
     896        3212 :                       if (a.dy > b.dy)
     897        1161 :                           return false;
     898        2051 :                       return a.dx < b.dx;
     899             :                   });
     900             :     }
     901             : 
     902             :     /* -------------------------------------------------------------------- */
     903             :     /*      Find the global source window.                                  */
     904             :     /* -------------------------------------------------------------------- */
     905             : 
     906        1054 :     const int knIntMax = std::numeric_limits<int>::max();
     907        1054 :     const int knIntMin = std::numeric_limits<int>::min();
     908        1054 :     int nSrcXOff = knIntMax;
     909        1054 :     int nSrcYOff = knIntMax;
     910        1054 :     int nSrcX2Off = knIntMin;
     911        1054 :     int nSrcY2Off = knIntMin;
     912        1054 :     double dfApproxAccArea = 0;
     913        3025 :     for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
     914             :          iChunk++)
     915             :     {
     916        1971 :         GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
     917        1971 :         nSrcXOff = std::min(nSrcXOff, pasThisChunk->sx);
     918        1971 :         nSrcYOff = std::min(nSrcYOff, pasThisChunk->sy);
     919        1971 :         nSrcX2Off = std::max(nSrcX2Off, pasThisChunk->sx + pasThisChunk->ssx);
     920        1971 :         nSrcY2Off = std::max(nSrcY2Off, pasThisChunk->sy + pasThisChunk->ssy);
     921        1971 :         dfApproxAccArea +=
     922        1971 :             static_cast<double>(pasThisChunk->ssx) * pasThisChunk->ssy;
     923             :     }
     924        1054 :     if (nSrcXOff < nSrcX2Off)
     925             :     {
     926        1048 :         const double dfTotalArea =
     927        1048 :             static_cast<double>(nSrcX2Off - nSrcXOff) * (nSrcY2Off - nSrcYOff);
     928             :         // This is really a gross heuristics, but should work in most cases
     929        1048 :         if (dfApproxAccArea >= dfTotalArea * 0.80)
     930             :         {
     931        1048 :             GDALDataset::FromHandle(psOptions->hSrcDS)
     932        1048 :                 ->AdviseRead(nSrcXOff, nSrcYOff, nSrcX2Off - nSrcXOff,
     933             :                              nSrcY2Off - nSrcYOff, nDstXSize, nDstYSize,
     934        1048 :                              psOptions->eWorkingDataType, psOptions->nBandCount,
     935        1048 :                              psOptions->panSrcBands, nullptr);
     936             :         }
     937             :     }
     938        1054 : }
     939             : 
     940             : /************************************************************************/
     941             : /*                         ChunkAndWarpImage()                          */
     942             : /************************************************************************/
     943             : 
     944             : /**
     945             :  * \fn CPLErr GDALWarpOperation::ChunkAndWarpImage(
     946             :                 int nDstXOff, int nDstYOff,  int nDstXSize, int nDstYSize );
     947             :  *
     948             :  * This method does a complete warp of the source image to the destination
     949             :  * image for the indicated region with the current warp options in effect.
     950             :  * Progress is reported to the installed progress monitor, if any.
     951             :  *
     952             :  * This function will subdivide the region and recursively call itself
     953             :  * until the total memory required to process a region chunk will all fit
     954             :  * in the memory pool defined by GDALWarpOptions::dfWarpMemoryLimit.
     955             :  *
     956             :  * Once an appropriate region is selected GDALWarpOperation::WarpRegion()
     957             :  * is invoked to do the actual work.
     958             :  *
     959             :  * @param nDstXOff X offset to window of destination data to be produced.
     960             :  * @param nDstYOff Y offset to window of destination data to be produced.
     961             :  * @param nDstXSize Width of output window on destination file to be produced.
     962             :  * @param nDstYSize Height of output window on destination file to be produced.
     963             :  *
     964             :  * @return CE_None on success or CE_Failure if an error occurs.
     965             :  */
     966             : 
     967        1049 : CPLErr GDALWarpOperation::ChunkAndWarpImage(int nDstXOff, int nDstYOff,
     968             :                                             int nDstXSize, int nDstYSize)
     969             : 
     970             : {
     971             :     /* -------------------------------------------------------------------- */
     972             :     /*      Collect the list of chunks to operate on.                       */
     973             :     /* -------------------------------------------------------------------- */
     974        1049 :     CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
     975             : 
     976             :     /* -------------------------------------------------------------------- */
     977             :     /*      Total up output pixels to process.                              */
     978             :     /* -------------------------------------------------------------------- */
     979        1049 :     double dfTotalPixels = 0.0;
     980             : 
     981        3010 :     for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
     982             :          iChunk++)
     983             :     {
     984        1961 :         GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
     985        1961 :         const double dfChunkPixels =
     986        1961 :             pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
     987             : 
     988        1961 :         dfTotalPixels += dfChunkPixels;
     989             :     }
     990             : 
     991             :     /* -------------------------------------------------------------------- */
     992             :     /*      Process them one at a time, updating the progress               */
     993             :     /*      information for each region.                                    */
     994             :     /* -------------------------------------------------------------------- */
     995        1049 :     double dfPixelsProcessed = 0.0;
     996             : 
     997        3005 :     for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
     998             :          iChunk++)
     999             :     {
    1000        1961 :         GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
    1001        1961 :         const double dfChunkPixels =
    1002        1961 :             pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
    1003             : 
    1004        1961 :         const double dfProgressBase = dfPixelsProcessed / dfTotalPixels;
    1005        1961 :         const double dfProgressScale = dfChunkPixels / dfTotalPixels;
    1006             : 
    1007        1961 :         CPLErr eErr = WarpRegion(
    1008             :             pasThisChunk->dx, pasThisChunk->dy, pasThisChunk->dsx,
    1009             :             pasThisChunk->dsy, pasThisChunk->sx, pasThisChunk->sy,
    1010             :             pasThisChunk->ssx, pasThisChunk->ssy, pasThisChunk->sExtraSx,
    1011             :             pasThisChunk->sExtraSy, dfProgressBase, dfProgressScale);
    1012             : 
    1013        1961 :         if (eErr != CE_None)
    1014           5 :             return eErr;
    1015             : 
    1016        1956 :         dfPixelsProcessed += dfChunkPixels;
    1017             :     }
    1018             : 
    1019        1044 :     WipeChunkList();
    1020             : 
    1021        1044 :     psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
    1022             : 
    1023        1044 :     return CE_None;
    1024             : }
    1025             : 
    1026             : /************************************************************************/
    1027             : /*                         GDALChunkAndWarpImage()                      */
    1028             : /************************************************************************/
    1029             : 
    1030             : /**
    1031             :  * @see GDALWarpOperation::ChunkAndWarpImage()
    1032             :  */
    1033             : 
    1034         149 : CPLErr GDALChunkAndWarpImage(GDALWarpOperationH hOperation, int nDstXOff,
    1035             :                              int nDstYOff, int nDstXSize, int nDstYSize)
    1036             : {
    1037         149 :     VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpImage", CE_Failure);
    1038             : 
    1039             :     return reinterpret_cast<GDALWarpOperation *>(hOperation)
    1040         149 :         ->ChunkAndWarpImage(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
    1041             : }
    1042             : 
    1043             : /************************************************************************/
    1044             : /*                          ChunkThreadMain()                           */
    1045             : /************************************************************************/
    1046             : 
    1047             : typedef struct
    1048             : {
    1049             :     GDALWarpOperation *poOperation;
    1050             :     GDALWarpChunk *pasChunkInfo;
    1051             :     CPLJoinableThread *hThreadHandle;
    1052             :     CPLErr eErr;
    1053             :     double dfProgressBase;
    1054             :     double dfProgressScale;
    1055             :     CPLMutex *hIOMutex;
    1056             : 
    1057             :     CPLMutex *hCondMutex;
    1058             :     volatile int bIOMutexTaken;
    1059             :     CPLCond *hCond;
    1060             : } ChunkThreadData;
    1061             : 
    1062          10 : static void ChunkThreadMain(void *pThreadData)
    1063             : 
    1064             : {
    1065          10 :     volatile ChunkThreadData *psData =
    1066             :         static_cast<volatile ChunkThreadData *>(pThreadData);
    1067             : 
    1068          10 :     GDALWarpChunk *pasChunkInfo = psData->pasChunkInfo;
    1069             : 
    1070             :     /* -------------------------------------------------------------------- */
    1071             :     /*      Acquire IO mutex.                                               */
    1072             :     /* -------------------------------------------------------------------- */
    1073          10 :     if (!CPLAcquireMutex(psData->hIOMutex, 600.0))
    1074             :     {
    1075           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1076             :                  "Failed to acquire IOMutex in WarpRegion().");
    1077           0 :         psData->eErr = CE_Failure;
    1078             :     }
    1079             :     else
    1080             :     {
    1081          10 :         if (psData->hCond != nullptr)
    1082             :         {
    1083           5 :             CPLAcquireMutex(psData->hCondMutex, 1.0);
    1084           5 :             psData->bIOMutexTaken = TRUE;
    1085           5 :             CPLCondSignal(psData->hCond);
    1086           5 :             CPLReleaseMutex(psData->hCondMutex);
    1087             :         }
    1088             : 
    1089          20 :         psData->eErr = psData->poOperation->WarpRegion(
    1090             :             pasChunkInfo->dx, pasChunkInfo->dy, pasChunkInfo->dsx,
    1091             :             pasChunkInfo->dsy, pasChunkInfo->sx, pasChunkInfo->sy,
    1092             :             pasChunkInfo->ssx, pasChunkInfo->ssy, pasChunkInfo->sExtraSx,
    1093          10 :             pasChunkInfo->sExtraSy, psData->dfProgressBase,
    1094          10 :             psData->dfProgressScale);
    1095             : 
    1096             :         /* --------------------------------------------------------------------
    1097             :          */
    1098             :         /*      Release the IO mutex. */
    1099             :         /* --------------------------------------------------------------------
    1100             :          */
    1101          10 :         CPLReleaseMutex(psData->hIOMutex);
    1102             :     }
    1103          10 : }
    1104             : 
    1105             : /************************************************************************/
    1106             : /*                         ChunkAndWarpMulti()                          */
    1107             : /************************************************************************/
    1108             : 
    1109             : /**
    1110             :  * \fn CPLErr GDALWarpOperation::ChunkAndWarpMulti(
    1111             :                 int nDstXOff, int nDstYOff,  int nDstXSize, int nDstYSize );
    1112             :  *
    1113             :  * This method does a complete warp of the source image to the destination
    1114             :  * image for the indicated region with the current warp options in effect.
    1115             :  * Progress is reported to the installed progress monitor, if any.
    1116             :  *
    1117             :  * Externally this method operates the same as ChunkAndWarpImage(), but
    1118             :  * internally this method uses multiple threads to interleave input/output
    1119             :  * for one region while the processing is being done for another.
    1120             :  *
    1121             :  * @param nDstXOff X offset to window of destination data to be produced.
    1122             :  * @param nDstYOff Y offset to window of destination data to be produced.
    1123             :  * @param nDstXSize Width of output window on destination file to be produced.
    1124             :  * @param nDstYSize Height of output window on destination file to be produced.
    1125             :  *
    1126             :  * @return CE_None on success or CE_Failure if an error occurs.
    1127             :  */
    1128             : 
    1129           5 : CPLErr GDALWarpOperation::ChunkAndWarpMulti(int nDstXOff, int nDstYOff,
    1130             :                                             int nDstXSize, int nDstYSize)
    1131             : 
    1132             : {
    1133           5 :     hIOMutex = CPLCreateMutex();
    1134           5 :     hWarpMutex = CPLCreateMutex();
    1135             : 
    1136           5 :     CPLReleaseMutex(hIOMutex);
    1137           5 :     CPLReleaseMutex(hWarpMutex);
    1138             : 
    1139           5 :     CPLCond *hCond = CPLCreateCond();
    1140           5 :     CPLMutex *hCondMutex = CPLCreateMutex();
    1141           5 :     CPLReleaseMutex(hCondMutex);
    1142             : 
    1143             :     /* -------------------------------------------------------------------- */
    1144             :     /*      Collect the list of chunks to operate on.                       */
    1145             :     /* -------------------------------------------------------------------- */
    1146           5 :     CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
    1147             : 
    1148             :     /* -------------------------------------------------------------------- */
    1149             :     /*      Process them one at a time, updating the progress               */
    1150             :     /*      information for each region.                                    */
    1151             :     /* -------------------------------------------------------------------- */
    1152           5 :     ChunkThreadData volatile asThreadData[2] = {};
    1153           5 :     memset(reinterpret_cast<void *>(
    1154             :                const_cast<ChunkThreadData(*)[2]>(&asThreadData)),
    1155             :            0, sizeof(asThreadData));
    1156           5 :     asThreadData[0].poOperation = this;
    1157           5 :     asThreadData[0].hIOMutex = hIOMutex;
    1158           5 :     asThreadData[1].poOperation = this;
    1159           5 :     asThreadData[1].hIOMutex = hIOMutex;
    1160             : 
    1161           5 :     double dfPixelsProcessed = 0.0;
    1162           5 :     double dfTotalPixels = static_cast<double>(nDstXSize) * nDstYSize;
    1163             : 
    1164           5 :     CPLErr eErr = CE_None;
    1165          20 :     for (int iChunk = 0; iChunk < nChunkListCount + 1; iChunk++)
    1166             :     {
    1167          15 :         int iThread = iChunk % 2;
    1168             : 
    1169             :         /* --------------------------------------------------------------------
    1170             :          */
    1171             :         /*      Launch thread for this chunk. */
    1172             :         /* --------------------------------------------------------------------
    1173             :          */
    1174          15 :         if (pasChunkList != nullptr && iChunk < nChunkListCount)
    1175             :         {
    1176          10 :             GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
    1177          10 :             const double dfChunkPixels =
    1178          10 :                 pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
    1179             : 
    1180          10 :             asThreadData[iThread].dfProgressBase =
    1181          10 :                 dfPixelsProcessed / dfTotalPixels;
    1182          10 :             asThreadData[iThread].dfProgressScale =
    1183          10 :                 dfChunkPixels / dfTotalPixels;
    1184             : 
    1185          10 :             dfPixelsProcessed += dfChunkPixels;
    1186             : 
    1187          10 :             asThreadData[iThread].pasChunkInfo = pasThisChunk;
    1188             : 
    1189          10 :             if (iChunk == 0)
    1190             :             {
    1191           5 :                 asThreadData[iThread].hCond = hCond;
    1192           5 :                 asThreadData[iThread].hCondMutex = hCondMutex;
    1193             :             }
    1194             :             else
    1195             :             {
    1196           5 :                 asThreadData[iThread].hCond = nullptr;
    1197           5 :                 asThreadData[iThread].hCondMutex = nullptr;
    1198             :             }
    1199          10 :             asThreadData[iThread].bIOMutexTaken = FALSE;
    1200             : 
    1201          10 :             CPLDebug("GDAL", "Start chunk %d / %d.", iChunk, nChunkListCount);
    1202          20 :             asThreadData[iThread].hThreadHandle = CPLCreateJoinableThread(
    1203             :                 ChunkThreadMain,
    1204          10 :                 const_cast<ChunkThreadData *>(&asThreadData[iThread]));
    1205          10 :             if (asThreadData[iThread].hThreadHandle == nullptr)
    1206             :             {
    1207           0 :                 CPLError(
    1208             :                     CE_Failure, CPLE_AppDefined,
    1209             :                     "CPLCreateJoinableThread() failed in ChunkAndWarpMulti()");
    1210           0 :                 eErr = CE_Failure;
    1211           0 :                 break;
    1212             :             }
    1213             : 
    1214             :             // Wait that the first thread has acquired the IO mutex before
    1215             :             // proceeding.  This will ensure that the first thread will run
    1216             :             // before the second one.
    1217          10 :             if (iChunk == 0)
    1218             :             {
    1219           5 :                 CPLAcquireMutex(hCondMutex, 1.0);
    1220          10 :                 while (asThreadData[iThread].bIOMutexTaken == FALSE)
    1221           5 :                     CPLCondWait(hCond, hCondMutex);
    1222           5 :                 CPLReleaseMutex(hCondMutex);
    1223             :             }
    1224             :         }
    1225             : 
    1226             :         /* --------------------------------------------------------------------
    1227             :          */
    1228             :         /*      Wait for previous chunks thread to complete. */
    1229             :         /* --------------------------------------------------------------------
    1230             :          */
    1231          15 :         if (iChunk > 0)
    1232             :         {
    1233          10 :             iThread = (iChunk - 1) % 2;
    1234             : 
    1235             :             // Wait for thread to finish.
    1236          10 :             CPLJoinThread(asThreadData[iThread].hThreadHandle);
    1237          10 :             asThreadData[iThread].hThreadHandle = nullptr;
    1238             : 
    1239          10 :             CPLDebug("GDAL", "Finished chunk %d / %d.", iChunk - 1,
    1240             :                      nChunkListCount);
    1241             : 
    1242          10 :             eErr = asThreadData[iThread].eErr;
    1243             : 
    1244          10 :             if (eErr != CE_None)
    1245           0 :                 break;
    1246             :         }
    1247             :     }
    1248             : 
    1249             :     /* -------------------------------------------------------------------- */
    1250             :     /*      Wait for all threads to complete.                               */
    1251             :     /* -------------------------------------------------------------------- */
    1252          15 :     for (int iThread = 0; iThread < 2; iThread++)
    1253             :     {
    1254          10 :         if (asThreadData[iThread].hThreadHandle)
    1255           0 :             CPLJoinThread(asThreadData[iThread].hThreadHandle);
    1256             :     }
    1257             : 
    1258           5 :     CPLDestroyCond(hCond);
    1259           5 :     CPLDestroyMutex(hCondMutex);
    1260             : 
    1261           5 :     WipeChunkList();
    1262             : 
    1263           5 :     psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
    1264             : 
    1265           5 :     return eErr;
    1266             : }
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                         GDALChunkAndWarpMulti()                      */
    1270             : /************************************************************************/
    1271             : 
    1272             : /**
    1273             :  * @see GDALWarpOperation::ChunkAndWarpMulti()
    1274             :  */
    1275             : 
    1276           0 : CPLErr GDALChunkAndWarpMulti(GDALWarpOperationH hOperation, int nDstXOff,
    1277             :                              int nDstYOff, int nDstXSize, int nDstYSize)
    1278             : {
    1279           0 :     VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpMulti", CE_Failure);
    1280             : 
    1281             :     return reinterpret_cast<GDALWarpOperation *>(hOperation)
    1282           0 :         ->ChunkAndWarpMulti(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
    1283             : }
    1284             : 
    1285             : /************************************************************************/
    1286             : /*                           WipeChunkList()                            */
    1287             : /************************************************************************/
    1288             : 
    1289        3508 : void GDALWarpOperation::WipeChunkList()
    1290             : 
    1291             : {
    1292        3508 :     CPLFree(pasChunkList);
    1293        3508 :     pasChunkList = nullptr;
    1294        3508 :     nChunkListCount = 0;
    1295        3508 :     nChunkListMax = 0;
    1296        3508 : }
    1297             : 
    1298             : /************************************************************************/
    1299             : /*                       GetWorkingMemoryForWindow()                    */
    1300             : /************************************************************************/
    1301             : 
    1302             : /** Returns the amount of working memory, in bytes, required to process
    1303             :  * a warped window of source dimensions nSrcXSize x nSrcYSize and target
    1304             :  * dimensions nDstXSize x nDstYSize.
    1305             :  */
    1306        3471 : double GDALWarpOperation::GetWorkingMemoryForWindow(int nSrcXSize,
    1307             :                                                     int nSrcYSize,
    1308             :                                                     int nDstXSize,
    1309             :                                                     int nDstYSize) const
    1310             : {
    1311             :     /* -------------------------------------------------------------------- */
    1312             :     /*      Based on the types of masks in use, how many bits will each     */
    1313             :     /*      source pixel cost us?                                           */
    1314             :     /* -------------------------------------------------------------------- */
    1315        3471 :     int nSrcPixelCostInBits = GDALGetDataTypeSize(psOptions->eWorkingDataType) *
    1316        3471 :                               psOptions->nBandCount;
    1317             : 
    1318        3471 :     if (psOptions->pfnSrcDensityMaskFunc != nullptr)
    1319           0 :         nSrcPixelCostInBits += 32;  // Float mask?
    1320             : 
    1321        3471 :     GDALRasterBandH hSrcBand = nullptr;
    1322        3471 :     if (psOptions->nBandCount > 0)
    1323             :         hSrcBand =
    1324        3471 :             GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
    1325             : 
    1326        3471 :     if (psOptions->nSrcAlphaBand > 0 || psOptions->hCutline != nullptr)
    1327         125 :         nSrcPixelCostInBits += 32;  // UnifiedSrcDensity float mask.
    1328        6692 :     else if (hSrcBand != nullptr &&
    1329        3346 :              (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET))
    1330           5 :         nSrcPixelCostInBits += 1;  // UnifiedSrcValid bit mask.
    1331             : 
    1332        3471 :     if (psOptions->papfnSrcPerBandValidityMaskFunc != nullptr ||
    1333        3471 :         psOptions->padfSrcNoDataReal != nullptr)
    1334         159 :         nSrcPixelCostInBits += psOptions->nBandCount;  // Bit/band mask.
    1335             : 
    1336        3471 :     if (psOptions->pfnSrcValidityMaskFunc != nullptr)
    1337           0 :         nSrcPixelCostInBits += 1;  // Bit mask.
    1338             : 
    1339             :     /* -------------------------------------------------------------------- */
    1340             :     /*      What about the cost for the destination.                        */
    1341             :     /* -------------------------------------------------------------------- */
    1342        3471 :     int nDstPixelCostInBits = GDALGetDataTypeSize(psOptions->eWorkingDataType) *
    1343        3471 :                               psOptions->nBandCount;
    1344             : 
    1345        3471 :     if (psOptions->pfnDstDensityMaskFunc != nullptr)
    1346           0 :         nDstPixelCostInBits += 32;
    1347             : 
    1348        3471 :     if (psOptions->padfDstNoDataReal != nullptr ||
    1349        2257 :         psOptions->pfnDstValidityMaskFunc != nullptr)
    1350        1214 :         nDstPixelCostInBits += psOptions->nBandCount;
    1351             : 
    1352        3471 :     if (psOptions->nDstAlphaBand > 0)
    1353         269 :         nDstPixelCostInBits += 32;  // DstDensity float mask.
    1354             : 
    1355        3471 :     const double dfTotalMemoryUse =
    1356        3471 :         (static_cast<double>(nSrcPixelCostInBits) * nSrcXSize * nSrcYSize +
    1357        3471 :          static_cast<double>(nDstPixelCostInBits) * nDstXSize * nDstYSize) /
    1358             :         8.0;
    1359        3471 :     return dfTotalMemoryUse;
    1360             : }
    1361             : 
    1362             : /************************************************************************/
    1363             : /*                       CollectChunkListInternal()                     */
    1364             : /************************************************************************/
    1365             : 
    1366        3712 : CPLErr GDALWarpOperation::CollectChunkListInternal(int nDstXOff, int nDstYOff,
    1367             :                                                    int nDstXSize, int nDstYSize)
    1368             : 
    1369             : {
    1370             :     /* -------------------------------------------------------------------- */
    1371             :     /*      Compute the bounds of the input area corresponding to the       */
    1372             :     /*      output area.                                                    */
    1373             :     /* -------------------------------------------------------------------- */
    1374        3712 :     int nSrcXOff = 0;
    1375        3712 :     int nSrcYOff = 0;
    1376        3712 :     int nSrcXSize = 0;
    1377        3712 :     int nSrcYSize = 0;
    1378        3712 :     double dfSrcXExtraSize = 0.0;
    1379        3712 :     double dfSrcYExtraSize = 0.0;
    1380        3712 :     double dfSrcFillRatio = 0.0;
    1381             :     CPLErr eErr =
    1382        3712 :         ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize, &nSrcXOff,
    1383             :                             &nSrcYOff, &nSrcXSize, &nSrcYSize, &dfSrcXExtraSize,
    1384             :                             &dfSrcYExtraSize, &dfSrcFillRatio);
    1385             : 
    1386        3712 :     if (eErr != CE_None)
    1387             :     {
    1388           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    1389             :                  "Unable to compute source region for "
    1390             :                  "output window %d,%d,%d,%d, skipping.",
    1391             :                  nDstXOff, nDstYOff, nDstXSize, nDstYSize);
    1392           2 :         return eErr;
    1393             :     }
    1394             : 
    1395             :     /* -------------------------------------------------------------------- */
    1396             :     /*      If we are allowed to drop no-source regions, do so now if       */
    1397             :     /*      appropriate.                                                    */
    1398             :     /* -------------------------------------------------------------------- */
    1399        4592 :     if ((nSrcXSize == 0 || nSrcYSize == 0) &&
    1400         882 :         CPLFetchBool(psOptions->papszWarpOptions, "SKIP_NOSOURCE", false))
    1401         410 :         return CE_None;
    1402             : 
    1403             :     /* -------------------------------------------------------------------- */
    1404             :     /*      Does the cost of the current rectangle exceed our memory        */
    1405             :     /*      limit? If so, split the destination along the longest           */
    1406             :     /*      dimension and recurse.                                          */
    1407             :     /* -------------------------------------------------------------------- */
    1408             :     const double dfTotalMemoryUse =
    1409        3300 :         GetWorkingMemoryForWindow(nSrcXSize, nSrcYSize, nDstXSize, nDstYSize);
    1410             : 
    1411             :     // If size of working buffers need exceed the allow limit, then divide
    1412             :     // the target area
    1413             :     // Do it also if the "fill ratio" of the source is too low (#3120), but
    1414             :     // only if there's at least some source pixel intersecting. The
    1415             :     // SRC_FILL_RATIO_HEURISTICS warping option is undocumented and only here
    1416             :     // in case the heuristics would cause issues.
    1417             : #if DEBUG_VERBOSE
    1418             :     CPLDebug("WARP",
    1419             :              "dst=(%d,%d,%d,%d) src=(%d,%d,%d,%d) srcfillratio=%.17g, "
    1420             :              "dfTotalMemoryUse=%.1f MB",
    1421             :              nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff, nSrcYOff,
    1422             :              nSrcXSize, nSrcYSize, dfSrcFillRatio,
    1423             :              dfTotalMemoryUse / (1024 * 1024));
    1424             : #endif
    1425         745 :     if ((dfTotalMemoryUse > psOptions->dfWarpMemoryLimit &&
    1426        6600 :          (nDstXSize > 2 || nDstYSize > 2)) ||
    1427        2555 :         (dfSrcFillRatio > 0 && dfSrcFillRatio < 0.5 &&
    1428         274 :          (nDstXSize > 100 || nDstYSize > 100) &&
    1429         586 :          CPLFetchBool(psOptions->papszWarpOptions, "SRC_FILL_RATIO_HEURISTICS",
    1430             :                       true)))
    1431             :     {
    1432        1330 :         int nBlockXSize = 1;
    1433        1330 :         int nBlockYSize = 1;
    1434        1330 :         if (psOptions->hDstDS)
    1435             :         {
    1436        1330 :             GDALGetBlockSize(GDALGetRasterBand(psOptions->hDstDS, 1),
    1437             :                              &nBlockXSize, &nBlockYSize);
    1438             :         }
    1439             : 
    1440        1330 :         int bStreamableOutput = CPLFetchBool(psOptions->papszWarpOptions,
    1441        1330 :                                              "STREAMABLE_OUTPUT", false);
    1442             :         const char *pszOptimizeSize =
    1443        1330 :             CSLFetchNameValue(psOptions->papszWarpOptions, "OPTIMIZE_SIZE");
    1444        1330 :         const bool bOptimizeSizeAuto =
    1445        1330 :             !pszOptimizeSize || EQUAL(pszOptimizeSize, "AUTO");
    1446             :         const bool bOptimizeSize =
    1447        3891 :             !bStreamableOutput &&
    1448          97 :             ((pszOptimizeSize && !bOptimizeSizeAuto &&
    1449        1329 :               CPLTestBool(pszOptimizeSize)) ||
    1450             :              // Auto-enable optimize-size mode if output region is at least
    1451             :              // 2x2 blocks large and the shapes of the source and target regions
    1452             :              // are not excessively different. All those thresholds are a bit
    1453             :              // arbitrary
    1454         485 :              (bOptimizeSizeAuto &&
    1455        1232 :               (nDstXSize > nDstYSize ? fabs(double(nDstXSize) / nDstYSize -
    1456         485 :                                             double(nSrcXSize) / nSrcYSize) <
    1457         485 :                                            5 * double(nDstXSize) / nDstYSize
    1458         747 :                                      : fabs(double(nDstYSize) / nDstXSize -
    1459         747 :                                             double(nSrcYSize) / nSrcXSize) <
    1460         747 :                                            5 * double(nDstYSize) / nDstXSize) &&
    1461        1073 :               nDstXSize / 2 >= nBlockXSize && nDstYSize / 2 >= nBlockYSize));
    1462             : 
    1463             :         // If the region width is greater than the region height,
    1464             :         // cut in half in the width. When we want to optimize the size
    1465             :         // of a compressed output dataset, do this only if each half part
    1466             :         // is at least as wide as the block width.
    1467        1330 :         bool bHasDivided = false;
    1468        1330 :         CPLErr eErr2 = CE_None;
    1469        1330 :         if (nDstXSize > nDstYSize &&
    1470         553 :             ((!bOptimizeSize && !bStreamableOutput) ||
    1471          88 :              (bOptimizeSize &&
    1472          89 :               (nDstXSize / 2 >= nBlockXSize || nDstYSize == 1)) ||
    1473           1 :              (bStreamableOutput && nDstXSize / 2 >= nBlockXSize &&
    1474           1 :               nDstYSize == nBlockYSize)))
    1475             :         {
    1476         506 :             bHasDivided = true;
    1477         506 :             int nChunk1 = nDstXSize / 2;
    1478             : 
    1479             :             // In the optimize size case, try to stick on target block
    1480             :             // boundaries.
    1481         506 :             if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockXSize)
    1482          42 :                 nChunk1 = (nChunk1 / nBlockXSize) * nBlockXSize;
    1483             : 
    1484         506 :             int nChunk2 = nDstXSize - nChunk1;
    1485             : 
    1486         506 :             eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nChunk1,
    1487             :                                             nDstYSize);
    1488             : 
    1489         506 :             eErr2 = CollectChunkListInternal(nDstXOff + nChunk1, nDstYOff,
    1490         506 :                                              nChunk2, nDstYSize);
    1491             :         }
    1492         824 :         else if (!(bStreamableOutput && nDstYSize / 2 < nBlockYSize))
    1493             :         {
    1494         823 :             bHasDivided = true;
    1495         823 :             int nChunk1 = nDstYSize / 2;
    1496             : 
    1497             :             // In the optimize size case, try to stick on target block
    1498             :             // boundaries.
    1499         823 :             if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockYSize)
    1500          77 :                 nChunk1 = (nChunk1 / nBlockYSize) * nBlockYSize;
    1501             : 
    1502         823 :             const int nChunk2 = nDstYSize - nChunk1;
    1503             : 
    1504         823 :             eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize,
    1505             :                                             nChunk1);
    1506             : 
    1507         823 :             eErr2 = CollectChunkListInternal(nDstXOff, nDstYOff + nChunk1,
    1508             :                                              nDstXSize, nChunk2);
    1509             :         }
    1510             : 
    1511        1330 :         if (bHasDivided)
    1512             :         {
    1513        1329 :             if (eErr == CE_None)
    1514        1329 :                 return eErr2;
    1515             :             else
    1516           0 :                 return eErr;
    1517             :         }
    1518             :     }
    1519             : 
    1520             :     /* -------------------------------------------------------------------- */
    1521             :     /*      OK, everything fits, so add to the chunk list.                  */
    1522             :     /* -------------------------------------------------------------------- */
    1523        1971 :     if (nChunkListCount == nChunkListMax)
    1524             :     {
    1525        1195 :         nChunkListMax = nChunkListMax * 2 + 1;
    1526        1195 :         pasChunkList = static_cast<GDALWarpChunk *>(
    1527        1195 :             CPLRealloc(pasChunkList, sizeof(GDALWarpChunk) * nChunkListMax));
    1528             :     }
    1529             : 
    1530        1971 :     pasChunkList[nChunkListCount].dx = nDstXOff;
    1531        1971 :     pasChunkList[nChunkListCount].dy = nDstYOff;
    1532        1971 :     pasChunkList[nChunkListCount].dsx = nDstXSize;
    1533        1971 :     pasChunkList[nChunkListCount].dsy = nDstYSize;
    1534        1971 :     pasChunkList[nChunkListCount].sx = nSrcXOff;
    1535        1971 :     pasChunkList[nChunkListCount].sy = nSrcYOff;
    1536        1971 :     pasChunkList[nChunkListCount].ssx = nSrcXSize;
    1537        1971 :     pasChunkList[nChunkListCount].ssy = nSrcYSize;
    1538        1971 :     pasChunkList[nChunkListCount].sExtraSx = dfSrcXExtraSize;
    1539        1971 :     pasChunkList[nChunkListCount].sExtraSy = dfSrcYExtraSize;
    1540             : 
    1541        1971 :     nChunkListCount++;
    1542             : 
    1543        1971 :     return CE_None;
    1544             : }
    1545             : 
    1546             : /************************************************************************/
    1547             : /*                             WarpRegion()                             */
    1548             : /************************************************************************/
    1549             : 
    1550             : /**
    1551             :  * This method requests the indicated region of the output file be generated.
    1552             :  *
    1553             :  * Note that WarpRegion() will produce the requested area in one low level warp
    1554             :  * operation without verifying that this does not exceed the stated memory
    1555             :  * limits for the warp operation.  Applications should take care not to call
    1556             :  * WarpRegion() on too large a region!  This function
    1557             :  * is normally called by ChunkAndWarpImage(), the normal entry point for
    1558             :  * applications.  Use it instead if staying within memory constraints is
    1559             :  * desired.
    1560             :  *
    1561             :  * Progress is reported from dfProgressBase to dfProgressBase + dfProgressScale
    1562             :  * for the indicated region.
    1563             :  *
    1564             :  * @param nDstXOff X offset to window of destination data to be produced.
    1565             :  * @param nDstYOff Y offset to window of destination data to be produced.
    1566             :  * @param nDstXSize Width of output window on destination file to be produced.
    1567             :  * @param nDstYSize Height of output window on destination file to be produced.
    1568             :  * @param nSrcXOff source window X offset (computed if window all zero)
    1569             :  * @param nSrcYOff source window Y offset (computed if window all zero)
    1570             :  * @param nSrcXSize source window X size (computed if window all zero)
    1571             :  * @param nSrcYSize source window Y size (computed if window all zero)
    1572             :  * @param dfProgressBase minimum progress value reported
    1573             :  * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
    1574             :  *                        maximum progress value reported
    1575             :  *
    1576             :  * @return CE_None on success or CE_Failure if an error occurs.
    1577             :  */
    1578             : 
    1579           0 : CPLErr GDALWarpOperation::WarpRegion(int nDstXOff, int nDstYOff, int nDstXSize,
    1580             :                                      int nDstYSize, int nSrcXOff, int nSrcYOff,
    1581             :                                      int nSrcXSize, int nSrcYSize,
    1582             :                                      double dfProgressBase,
    1583             :                                      double dfProgressScale)
    1584             : {
    1585           0 :     return WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
    1586             :                       nSrcYOff, nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
    1587           0 :                       dfProgressScale);
    1588             : }
    1589             : 
    1590             : /**
    1591             :  * This method requests the indicated region of the output file be generated.
    1592             :  *
    1593             :  * Note that WarpRegion() will produce the requested area in one low level warp
    1594             :  * operation without verifying that this does not exceed the stated memory
    1595             :  * limits for the warp operation.  Applications should take care not to call
    1596             :  * WarpRegion() on too large a region!  This function
    1597             :  * is normally called by ChunkAndWarpImage(), the normal entry point for
    1598             :  * applications.  Use it instead if staying within memory constraints is
    1599             :  * desired.
    1600             :  *
    1601             :  * Progress is reported from dfProgressBase to dfProgressBase + dfProgressScale
    1602             :  * for the indicated region.
    1603             :  *
    1604             :  * @param nDstXOff X offset to window of destination data to be produced.
    1605             :  * @param nDstYOff Y offset to window of destination data to be produced.
    1606             :  * @param nDstXSize Width of output window on destination file to be produced.
    1607             :  * @param nDstYSize Height of output window on destination file to be produced.
    1608             :  * @param nSrcXOff source window X offset (computed if window all zero)
    1609             :  * @param nSrcYOff source window Y offset (computed if window all zero)
    1610             :  * @param nSrcXSize source window X size (computed if window all zero)
    1611             :  * @param nSrcYSize source window Y size (computed if window all zero)
    1612             :  * @param dfSrcXExtraSize Extra pixels (included in nSrcXSize) reserved
    1613             :  * for filter window. Should be ignored in scale computation
    1614             :  * @param dfSrcYExtraSize Extra pixels (included in nSrcYSize) reserved
    1615             :  * for filter window. Should be ignored in scale computation
    1616             :  * @param dfProgressBase minimum progress value reported
    1617             :  * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
    1618             :  *                        maximum progress value reported
    1619             :  *
    1620             :  * @return CE_None on success or CE_Failure if an error occurs.
    1621             :  */
    1622             : 
    1623        1971 : CPLErr GDALWarpOperation::WarpRegion(
    1624             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, int nSrcXOff,
    1625             :     int nSrcYOff, int nSrcXSize, int nSrcYSize, double dfSrcXExtraSize,
    1626             :     double dfSrcYExtraSize, double dfProgressBase, double dfProgressScale)
    1627             : 
    1628             : {
    1629        1971 :     ReportTiming(nullptr);
    1630             : 
    1631             :     /* -------------------------------------------------------------------- */
    1632             :     /*      Allocate the output buffer.                                     */
    1633             :     /* -------------------------------------------------------------------- */
    1634        1971 :     int bDstBufferInitialized = FALSE;
    1635             :     void *pDstBuffer =
    1636        1971 :         CreateDestinationBuffer(nDstXSize, nDstYSize, &bDstBufferInitialized);
    1637        1971 :     if (pDstBuffer == nullptr)
    1638             :     {
    1639           0 :         return CE_Failure;
    1640             :     }
    1641             : 
    1642             :     /* -------------------------------------------------------------------- */
    1643             :     /*      If we aren't doing fixed initialization of the output buffer    */
    1644             :     /*      then read it from disk so we can overlay on existing imagery.   */
    1645             :     /* -------------------------------------------------------------------- */
    1646        1971 :     GDALDataset *poDstDS = GDALDataset::FromHandle(psOptions->hDstDS);
    1647        1971 :     if (!bDstBufferInitialized)
    1648             :     {
    1649         400 :         CPLErr eErr = CE_None;
    1650         400 :         if (psOptions->nBandCount == 1)
    1651             :         {
    1652             :             // Particular case to simplify the stack a bit.
    1653             :             // TODO(rouault): Need an explanation of what and why r34502 helps.
    1654         370 :             eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
    1655         740 :                        ->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
    1656             :                                   nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
    1657         370 :                                   psOptions->eWorkingDataType, 0, 0, nullptr);
    1658             :         }
    1659             :         else
    1660             :         {
    1661          30 :             eErr = poDstDS->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
    1662             :                                      nDstYSize, pDstBuffer, nDstXSize,
    1663          30 :                                      nDstYSize, psOptions->eWorkingDataType,
    1664          30 :                                      psOptions->nBandCount,
    1665          30 :                                      psOptions->panDstBands, 0, 0, 0, nullptr);
    1666             :         }
    1667             : 
    1668         400 :         if (eErr != CE_None)
    1669             :         {
    1670           0 :             DestroyDestinationBuffer(pDstBuffer);
    1671           0 :             return eErr;
    1672             :         }
    1673             : 
    1674         400 :         ReportTiming("Output buffer read");
    1675             :     }
    1676             : 
    1677             :     /* -------------------------------------------------------------------- */
    1678             :     /*      Perform the warp.                                               */
    1679             :     /* -------------------------------------------------------------------- */
    1680        3942 :     CPLErr eErr = WarpRegionToBuffer(
    1681             :         nDstXOff, nDstYOff, nDstXSize, nDstYSize, pDstBuffer,
    1682        1971 :         psOptions->eWorkingDataType, nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
    1683             :         dfSrcXExtraSize, dfSrcYExtraSize, dfProgressBase, dfProgressScale);
    1684             : 
    1685             :     /* -------------------------------------------------------------------- */
    1686             :     /*      Write the output data back to disk if all went well.            */
    1687             :     /* -------------------------------------------------------------------- */
    1688        1971 :     if (eErr == CE_None)
    1689             :     {
    1690        1966 :         if (psOptions->nBandCount == 1)
    1691             :         {
    1692             :             // Particular case to simplify the stack a bit.
    1693        1833 :             eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
    1694        3666 :                        ->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
    1695             :                                   nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
    1696        1833 :                                   psOptions->eWorkingDataType, 0, 0, nullptr);
    1697             :         }
    1698             :         else
    1699             :         {
    1700         133 :             eErr = poDstDS->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
    1701             :                                      nDstYSize, pDstBuffer, nDstXSize,
    1702         133 :                                      nDstYSize, psOptions->eWorkingDataType,
    1703         133 :                                      psOptions->nBandCount,
    1704         133 :                                      psOptions->panDstBands, 0, 0, 0, nullptr);
    1705             :         }
    1706             : 
    1707        3932 :         if (eErr == CE_None &&
    1708        1966 :             CPLFetchBool(psOptions->papszWarpOptions, "WRITE_FLUSH", false))
    1709             :         {
    1710           0 :             const CPLErr eOldErr = CPLGetLastErrorType();
    1711           0 :             const CPLString osLastErrMsg = CPLGetLastErrorMsg();
    1712           0 :             GDALFlushCache(psOptions->hDstDS);
    1713           0 :             const CPLErr eNewErr = CPLGetLastErrorType();
    1714           0 :             if (eNewErr != eOldErr ||
    1715           0 :                 osLastErrMsg.compare(CPLGetLastErrorMsg()) != 0)
    1716           0 :                 eErr = CE_Failure;
    1717             :         }
    1718        1966 :         ReportTiming("Output buffer write");
    1719             :     }
    1720             : 
    1721             :     /* -------------------------------------------------------------------- */
    1722             :     /*      Cleanup and return.                                             */
    1723             :     /* -------------------------------------------------------------------- */
    1724        1971 :     DestroyDestinationBuffer(pDstBuffer);
    1725             : 
    1726        1971 :     return eErr;
    1727             : }
    1728             : 
    1729             : /************************************************************************/
    1730             : /*                             GDALWarpRegion()                         */
    1731             : /************************************************************************/
    1732             : 
    1733             : /**
    1734             :  * @see GDALWarpOperation::WarpRegion()
    1735             :  */
    1736             : 
    1737           0 : CPLErr GDALWarpRegion(GDALWarpOperationH hOperation, int nDstXOff, int nDstYOff,
    1738             :                       int nDstXSize, int nDstYSize, int nSrcXOff, int nSrcYOff,
    1739             :                       int nSrcXSize, int nSrcYSize)
    1740             : 
    1741             : {
    1742           0 :     VALIDATE_POINTER1(hOperation, "GDALWarpRegion", CE_Failure);
    1743             : 
    1744             :     return reinterpret_cast<GDALWarpOperation *>(hOperation)
    1745           0 :         ->WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
    1746           0 :                      nSrcYOff, nSrcXSize, nSrcYSize);
    1747             : }
    1748             : 
    1749             : /************************************************************************/
    1750             : /*                            WarpRegionToBuffer()                      */
    1751             : /************************************************************************/
    1752             : 
    1753             : /**
    1754             :  * This method requests that a particular window of the output dataset
    1755             :  * be warped and the result put into the provided data buffer.  The output
    1756             :  * dataset doesn't even really have to exist to use this method as long as
    1757             :  * the transformation function in the GDALWarpOptions is setup to map to
    1758             :  * a virtual pixel/line space.
    1759             :  *
    1760             :  * This method will do the whole region in one chunk, so be wary of the
    1761             :  * amount of memory that might be used.
    1762             :  *
    1763             :  * @param nDstXOff X offset to window of destination data to be produced.
    1764             :  * @param nDstYOff Y offset to window of destination data to be produced.
    1765             :  * @param nDstXSize Width of output window on destination file to be produced.
    1766             :  * @param nDstYSize Height of output window on destination file to be produced.
    1767             :  * @param pDataBuf the data buffer to place result in, of type eBufDataType.
    1768             :  * @param eBufDataType the type of the output data buffer.  For now this
    1769             :  * must match GDALWarpOptions::eWorkingDataType.
    1770             :  * @param nSrcXOff source window X offset (computed if window all zero)
    1771             :  * @param nSrcYOff source window Y offset (computed if window all zero)
    1772             :  * @param nSrcXSize source window X size (computed if window all zero)
    1773             :  * @param nSrcYSize source window Y size (computed if window all zero)
    1774             :  * @param dfProgressBase minimum progress value reported
    1775             :  * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
    1776             :  *                        maximum progress value reported
    1777             :  *
    1778             :  * @return CE_None on success or CE_Failure if an error occurs.
    1779             :  */
    1780             : 
    1781         403 : CPLErr GDALWarpOperation::WarpRegionToBuffer(
    1782             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, void *pDataBuf,
    1783             :     GDALDataType eBufDataType, int nSrcXOff, int nSrcYOff, int nSrcXSize,
    1784             :     int nSrcYSize, double dfProgressBase, double dfProgressScale)
    1785             : {
    1786         403 :     return WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
    1787             :                               pDataBuf, eBufDataType, nSrcXOff, nSrcYOff,
    1788             :                               nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
    1789         403 :                               dfProgressScale);
    1790             : }
    1791             : 
    1792             : /**
    1793             :  * This method requests that a particular window of the output dataset
    1794             :  * be warped and the result put into the provided data buffer.  The output
    1795             :  * dataset doesn't even really have to exist to use this method as long as
    1796             :  * the transformation function in the GDALWarpOptions is setup to map to
    1797             :  * a virtual pixel/line space.
    1798             :  *
    1799             :  * This method will do the whole region in one chunk, so be wary of the
    1800             :  * amount of memory that might be used.
    1801             :  *
    1802             :  * @param nDstXOff X offset to window of destination data to be produced.
    1803             :  * @param nDstYOff Y offset to window of destination data to be produced.
    1804             :  * @param nDstXSize Width of output window on destination file to be produced.
    1805             :  * @param nDstYSize Height of output window on destination file to be produced.
    1806             :  * @param pDataBuf the data buffer to place result in, of type eBufDataType.
    1807             :  * @param eBufDataType the type of the output data buffer.  For now this
    1808             :  * must match GDALWarpOptions::eWorkingDataType.
    1809             :  * @param nSrcXOff source window X offset (computed if window all zero)
    1810             :  * @param nSrcYOff source window Y offset (computed if window all zero)
    1811             :  * @param nSrcXSize source window X size (computed if window all zero)
    1812             :  * @param nSrcYSize source window Y size (computed if window all zero)
    1813             :  * @param dfSrcXExtraSize Extra pixels (included in nSrcXSize) reserved
    1814             :  * for filter window. Should be ignored in scale computation
    1815             :  * @param dfSrcYExtraSize Extra pixels (included in nSrcYSize) reserved
    1816             :  * for filter window. Should be ignored in scale computation
    1817             :  * @param dfProgressBase minimum progress value reported
    1818             :  * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
    1819             :  *                        maximum progress value reported
    1820             :  *
    1821             :  * @return CE_None on success or CE_Failure if an error occurs.
    1822             :  */
    1823             : 
    1824        2374 : CPLErr GDALWarpOperation::WarpRegionToBuffer(
    1825             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, void *pDataBuf,
    1826             :     // Only in a CPLAssert.
    1827             :     CPL_UNUSED GDALDataType eBufDataType, int nSrcXOff, int nSrcYOff,
    1828             :     int nSrcXSize, int nSrcYSize, double dfSrcXExtraSize,
    1829             :     double dfSrcYExtraSize, double dfProgressBase, double dfProgressScale)
    1830             : 
    1831             : {
    1832        2374 :     const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
    1833             : 
    1834        2374 :     CPLAssert(eBufDataType == psOptions->eWorkingDataType);
    1835             : 
    1836             :     /* -------------------------------------------------------------------- */
    1837             :     /*      If not given a corresponding source window compute one now.     */
    1838             :     /* -------------------------------------------------------------------- */
    1839        2374 :     if (nSrcXSize == 0 && nSrcYSize == 0)
    1840             :     {
    1841             :         // TODO: This taking of the warp mutex is suboptimal. We could get rid
    1842             :         // of it, but that would require making sure ComputeSourceWindow()
    1843             :         // uses a different pTransformerArg than the warp kernel.
    1844         496 :         if (hWarpMutex != nullptr && !CPLAcquireMutex(hWarpMutex, 600.0))
    1845             :         {
    1846           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1847             :                      "Failed to acquire WarpMutex in WarpRegion().");
    1848           0 :             return CE_Failure;
    1849             :         }
    1850             :         const CPLErr eErr =
    1851         496 :             ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
    1852             :                                 &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
    1853             :                                 &dfSrcXExtraSize, &dfSrcYExtraSize, nullptr);
    1854         496 :         if (hWarpMutex != nullptr)
    1855           0 :             CPLReleaseMutex(hWarpMutex);
    1856         496 :         if (eErr != CE_None)
    1857             :         {
    1858             :             const bool bErrorOutIfEmptySourceWindow =
    1859          36 :                 CPLFetchBool(psOptions->papszWarpOptions,
    1860             :                              "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
    1861          36 :             if (!bErrorOutIfEmptySourceWindow)
    1862          36 :                 return CE_None;
    1863           0 :             return eErr;
    1864             :         }
    1865             :     }
    1866             : 
    1867             :     /* -------------------------------------------------------------------- */
    1868             :     /*      Prepare a WarpKernel object to match this operation.            */
    1869             :     /* -------------------------------------------------------------------- */
    1870        4676 :     GDALWarpKernel oWK;
    1871             : 
    1872        2338 :     oWK.eResample = m_bIsTranslationOnPixelBoundaries ? GRA_NearestNeighbour
    1873        2161 :                                                       : psOptions->eResampleAlg;
    1874        2338 :     oWK.eTieStrategy = psOptions->eTieStrategy;
    1875        2338 :     oWK.nBands = psOptions->nBandCount;
    1876        2338 :     oWK.eWorkingDataType = psOptions->eWorkingDataType;
    1877             : 
    1878        2338 :     oWK.pfnTransformer = psOptions->pfnTransformer;
    1879        2338 :     oWK.pTransformerArg = psOptions->pTransformerArg;
    1880             : 
    1881        2338 :     oWK.pfnProgress = psOptions->pfnProgress;
    1882        2338 :     oWK.pProgress = psOptions->pProgressArg;
    1883        2338 :     oWK.dfProgressBase = dfProgressBase;
    1884        2338 :     oWK.dfProgressScale = dfProgressScale;
    1885             : 
    1886        2338 :     oWK.papszWarpOptions = psOptions->papszWarpOptions;
    1887        2338 :     oWK.psThreadData = psThreadData;
    1888             : 
    1889        2338 :     oWK.padfDstNoDataReal = psOptions->padfDstNoDataReal;
    1890             : 
    1891             :     /* -------------------------------------------------------------------- */
    1892             :     /*      Setup the source buffer.                                        */
    1893             :     /*                                                                      */
    1894             :     /*      Eventually we may need to take advantage of pixel               */
    1895             :     /*      interleaved reading here.                                       */
    1896             :     /* -------------------------------------------------------------------- */
    1897        2338 :     oWK.nSrcXOff = nSrcXOff;
    1898        2338 :     oWK.nSrcYOff = nSrcYOff;
    1899        2338 :     oWK.nSrcXSize = nSrcXSize;
    1900        2338 :     oWK.nSrcYSize = nSrcYSize;
    1901        2338 :     oWK.dfSrcXExtraSize = dfSrcXExtraSize;
    1902        2338 :     oWK.dfSrcYExtraSize = dfSrcYExtraSize;
    1903             : 
    1904        2338 :     GInt64 nAlloc64 =
    1905        2338 :         nWordSize *
    1906        2338 :         (static_cast<GInt64>(nSrcXSize) * nSrcYSize + WARP_EXTRA_ELTS) *
    1907        2338 :         psOptions->nBandCount;
    1908             : #if SIZEOF_VOIDP == 4
    1909             :     if (nAlloc64 != static_cast<GInt64>(static_cast<size_t>(nAlloc64)))
    1910             :     {
    1911             :         CPLError(CE_Failure, CPLE_AppDefined,
    1912             :                  "Integer overflow : nSrcXSize=%d, nSrcYSize=%d", nSrcXSize,
    1913             :                  nSrcYSize);
    1914             :         return CE_Failure;
    1915             :     }
    1916             : #endif
    1917             : 
    1918        2338 :     oWK.papabySrcImage = static_cast<GByte **>(
    1919        2338 :         CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
    1920        4676 :     oWK.papabySrcImage[0] =
    1921        2338 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(static_cast<size_t>(nAlloc64)));
    1922             : 
    1923        2338 :     CPLErr eErr =
    1924        2035 :         nSrcXSize != 0 && nSrcYSize != 0 && oWK.papabySrcImage[0] == nullptr
    1925        4373 :             ? CE_Failure
    1926             :             : CE_None;
    1927             : 
    1928        5270 :     for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
    1929        2932 :         oWK.papabySrcImage[i] =
    1930        2932 :             reinterpret_cast<GByte *>(oWK.papabySrcImage[0]) +
    1931        2932 :             nWordSize *
    1932        2932 :                 (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
    1933        2932 :                  WARP_EXTRA_ELTS) *
    1934        2932 :                 i;
    1935             : 
    1936        2338 :     if (eErr == CE_None && nSrcXSize > 0 && nSrcYSize > 0)
    1937             :     {
    1938        2035 :         GDALDataset *poSrcDS = GDALDataset::FromHandle(psOptions->hSrcDS);
    1939        2035 :         if (psOptions->nBandCount == 1)
    1940             :         {
    1941             :             // Particular case to simplify the stack a bit.
    1942        1749 :             eErr = poSrcDS->GetRasterBand(psOptions->panSrcBands[0])
    1943        3498 :                        ->RasterIO(GF_Read, nSrcXOff, nSrcYOff, nSrcXSize,
    1944        1749 :                                   nSrcYSize, oWK.papabySrcImage[0], nSrcXSize,
    1945        1749 :                                   nSrcYSize, psOptions->eWorkingDataType, 0, 0,
    1946             :                                   nullptr);
    1947             :         }
    1948             :         else
    1949             :         {
    1950         286 :             eErr = poSrcDS->RasterIO(
    1951             :                 GF_Read, nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
    1952         286 :                 oWK.papabySrcImage[0], nSrcXSize, nSrcYSize,
    1953         286 :                 psOptions->eWorkingDataType, psOptions->nBandCount,
    1954         286 :                 psOptions->panSrcBands, 0, 0,
    1955         286 :                 nWordSize * (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
    1956             :                              WARP_EXTRA_ELTS),
    1957             :                 nullptr);
    1958             :         }
    1959             :     }
    1960             : 
    1961        2338 :     ReportTiming("Input buffer read");
    1962             : 
    1963             :     /* -------------------------------------------------------------------- */
    1964             :     /*      Initialize destination buffer.                                  */
    1965             :     /* -------------------------------------------------------------------- */
    1966        2338 :     oWK.nDstXOff = nDstXOff;
    1967        2338 :     oWK.nDstYOff = nDstYOff;
    1968        2338 :     oWK.nDstXSize = nDstXSize;
    1969        2338 :     oWK.nDstYSize = nDstYSize;
    1970             : 
    1971        2338 :     oWK.papabyDstImage = reinterpret_cast<GByte **>(
    1972        2338 :         CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
    1973             : 
    1974        5268 :     for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
    1975             :     {
    1976        2930 :         oWK.papabyDstImage[i] =
    1977        2930 :             static_cast<GByte *>(pDataBuf) +
    1978        2930 :             i * static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize * nWordSize;
    1979             :     }
    1980             : 
    1981             :     /* -------------------------------------------------------------------- */
    1982             :     /*      Eventually we need handling for a whole bunch of the            */
    1983             :     /*      validity and density masks here.                                */
    1984             :     /* -------------------------------------------------------------------- */
    1985             : 
    1986             :     // TODO
    1987             : 
    1988             :     /* -------------------------------------------------------------------- */
    1989             :     /*      Generate a source density mask if we have a source alpha band   */
    1990             :     /* -------------------------------------------------------------------- */
    1991        2338 :     if (eErr == CE_None && psOptions->nSrcAlphaBand > 0 && nSrcXSize > 0 &&
    1992          79 :         nSrcYSize > 0)
    1993             :     {
    1994          79 :         CPLAssert(oWK.pafUnifiedSrcDensity == nullptr);
    1995             : 
    1996          79 :         eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
    1997             : 
    1998          79 :         if (eErr == CE_None)
    1999             :         {
    2000          79 :             int bOutAllOpaque = FALSE;
    2001         158 :             eErr = GDALWarpSrcAlphaMasker(
    2002          79 :                 psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
    2003             :                 oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
    2004          79 :                 oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
    2005             :                 &bOutAllOpaque);
    2006          79 :             if (bOutAllOpaque)
    2007             :             {
    2008             : #if DEBUG_VERBOSE
    2009             :                 CPLDebug("WARP",
    2010             :                          "No need for a source density mask as all values "
    2011             :                          "are opaque");
    2012             : #endif
    2013          24 :                 CPLFree(oWK.pafUnifiedSrcDensity);
    2014          24 :                 oWK.pafUnifiedSrcDensity = nullptr;
    2015             :             }
    2016             :         }
    2017             :     }
    2018             : 
    2019             :     /* -------------------------------------------------------------------- */
    2020             :     /*      Generate a source density mask if we have a source cutline.     */
    2021             :     /* -------------------------------------------------------------------- */
    2022        2338 :     if (eErr == CE_None && psOptions->hCutline != nullptr && nSrcXSize > 0 &&
    2023          33 :         nSrcYSize > 0)
    2024             :     {
    2025          33 :         const bool bUnifiedSrcDensityJustCreated =
    2026          33 :             (oWK.pafUnifiedSrcDensity == nullptr);
    2027          33 :         if (bUnifiedSrcDensityJustCreated)
    2028             :         {
    2029             :             eErr =
    2030          33 :                 CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
    2031             : 
    2032          33 :             if (eErr == CE_None)
    2033             :             {
    2034          33 :                 for (GPtrDiff_t j = 0;
    2035     2877470 :                      j < static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
    2036             :                      j++)
    2037     2877440 :                     oWK.pafUnifiedSrcDensity[j] = 1.0;
    2038             :             }
    2039             :         }
    2040             : 
    2041          33 :         int nValidityFlag = 0;
    2042          33 :         if (eErr == CE_None)
    2043          33 :             eErr = GDALWarpCutlineMaskerEx(
    2044          33 :                 psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
    2045             :                 oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
    2046          33 :                 oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
    2047             :                 &nValidityFlag);
    2048          33 :         if (nValidityFlag == GCMVF_CHUNK_FULLY_WITHIN_CUTLINE &&
    2049             :             bUnifiedSrcDensityJustCreated)
    2050             :         {
    2051           8 :             VSIFree(oWK.pafUnifiedSrcDensity);
    2052           8 :             oWK.pafUnifiedSrcDensity = nullptr;
    2053             :         }
    2054             :     }
    2055             : 
    2056             :     /* -------------------------------------------------------------------- */
    2057             :     /*      Generate a destination density mask if we have a destination    */
    2058             :     /*      alpha band.                                                     */
    2059             :     /* -------------------------------------------------------------------- */
    2060        2338 :     if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
    2061             :     {
    2062         245 :         CPLAssert(oWK.pafDstDensity == nullptr);
    2063             : 
    2064         245 :         eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstDensity");
    2065             : 
    2066         245 :         if (eErr == CE_None)
    2067         245 :             eErr = GDALWarpDstAlphaMasker(
    2068         245 :                 psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
    2069             :                 oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
    2070         245 :                 oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
    2071             :     }
    2072             : 
    2073             :     /* -------------------------------------------------------------------- */
    2074             :     /*      If we have source nodata values create the validity mask.       */
    2075             :     /* -------------------------------------------------------------------- */
    2076        2338 :     if (eErr == CE_None && psOptions->padfSrcNoDataReal != nullptr &&
    2077         125 :         nSrcXSize > 0 && nSrcYSize > 0)
    2078             :     {
    2079         125 :         CPLAssert(oWK.papanBandSrcValid == nullptr);
    2080             : 
    2081         125 :         bool bAllBandsAllValid = true;
    2082         302 :         for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
    2083             :         {
    2084         177 :             eErr = CreateKernelMask(&oWK, i, "BandSrcValid");
    2085         177 :             if (eErr == CE_None)
    2086             :             {
    2087         177 :                 double adfNoData[2] = {psOptions->padfSrcNoDataReal[i],
    2088         177 :                                        psOptions->padfSrcNoDataImag != nullptr
    2089         177 :                                            ? psOptions->padfSrcNoDataImag[i]
    2090         177 :                                            : 0.0};
    2091             : 
    2092         177 :                 int bAllValid = FALSE;
    2093         354 :                 eErr = GDALWarpNoDataMasker(
    2094         177 :                     adfNoData, 1, psOptions->eWorkingDataType, oWK.nSrcXOff,
    2095             :                     oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
    2096         177 :                     &(oWK.papabySrcImage[i]), FALSE, oWK.papanBandSrcValid[i],
    2097             :                     &bAllValid);
    2098         177 :                 if (!bAllValid)
    2099         109 :                     bAllBandsAllValid = false;
    2100             :             }
    2101             :         }
    2102             : 
    2103             :         // Optimization: if all pixels in all bands are valid,
    2104             :         // we don't need a mask.
    2105         125 :         if (bAllBandsAllValid)
    2106             :         {
    2107             : #if DEBUG_VERBOSE
    2108             :             CPLDebug(
    2109             :                 "WARP",
    2110             :                 "No need for a source nodata mask as all values are valid");
    2111             : #endif
    2112         121 :             for (int k = 0; k < psOptions->nBandCount; k++)
    2113          68 :                 CPLFree(oWK.papanBandSrcValid[k]);
    2114          53 :             CPLFree(oWK.papanBandSrcValid);
    2115          53 :             oWK.papanBandSrcValid = nullptr;
    2116             :         }
    2117             : 
    2118             :         /* --------------------------------------------------------------------
    2119             :          */
    2120             :         /*      If there's just a single band, then transfer */
    2121             :         /*      papanBandSrcValid[0] as panUnifiedSrcValid. */
    2122             :         /* --------------------------------------------------------------------
    2123             :          */
    2124         125 :         if (oWK.papanBandSrcValid != nullptr && psOptions->nBandCount == 1)
    2125             :         {
    2126          53 :             oWK.panUnifiedSrcValid = oWK.papanBandSrcValid[0];
    2127          53 :             CPLFree(oWK.papanBandSrcValid);
    2128          53 :             oWK.papanBandSrcValid = nullptr;
    2129             :         }
    2130             : 
    2131             :         /* --------------------------------------------------------------------
    2132             :          */
    2133             :         /*      Compute a unified input pixel mask if and only if all bands */
    2134             :         /*      nodata is true.  That is, we only treat a pixel as nodata if */
    2135             :         /*      all bands match their respective nodata values. */
    2136             :         /* --------------------------------------------------------------------
    2137             :          */
    2138          72 :         else if (oWK.papanBandSrcValid != nullptr && eErr == CE_None)
    2139             :         {
    2140          19 :             bool bAtLeastOneBandAllValid = false;
    2141          75 :             for (int k = 0; k < psOptions->nBandCount; k++)
    2142             :             {
    2143          56 :                 if (oWK.papanBandSrcValid[k] == nullptr)
    2144             :                 {
    2145           0 :                     bAtLeastOneBandAllValid = true;
    2146           0 :                     break;
    2147             :                 }
    2148             :             }
    2149             : 
    2150          38 :             const char *pszUnifiedSrcNoData = CSLFetchNameValue(
    2151          19 :                 psOptions->papszWarpOptions, "UNIFIED_SRC_NODATA");
    2152          36 :             if (!bAtLeastOneBandAllValid && (pszUnifiedSrcNoData == nullptr ||
    2153          17 :                                              CPLTestBool(pszUnifiedSrcNoData)))
    2154             :             {
    2155          18 :                 auto nMaskBits =
    2156          18 :                     static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
    2157             : 
    2158             :                 eErr =
    2159          18 :                     CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
    2160             : 
    2161          18 :                 if (eErr == CE_None)
    2162             :                 {
    2163          18 :                     CPLMaskClearAll(oWK.panUnifiedSrcValid, nMaskBits);
    2164             : 
    2165          71 :                     for (int k = 0; k < psOptions->nBandCount; k++)
    2166             :                     {
    2167          53 :                         CPLMaskMerge(oWK.panUnifiedSrcValid,
    2168          53 :                                      oWK.papanBandSrcValid[k], nMaskBits);
    2169             :                     }
    2170             : 
    2171             :                     // If UNIFIED_SRC_NODATA is set, then we will ignore the
    2172             :                     // individual nodata status of each band. If it is not set,
    2173             :                     // both mechanism apply:
    2174             :                     // - if panUnifiedSrcValid[] indicates a pixel is invalid
    2175             :                     //   (that is all its bands are at nodata), then the output
    2176             :                     //   pixel will be invalid
    2177             :                     // - otherwise, the status band per band will be check with
    2178             :                     //   papanBandSrcValid[iBand][], and the output pixel will
    2179             :                     //   be valid
    2180          18 :                     if (pszUnifiedSrcNoData != nullptr &&
    2181          16 :                         !EQUAL(pszUnifiedSrcNoData, "PARTIAL"))
    2182             :                     {
    2183          60 :                         for (int k = 0; k < psOptions->nBandCount; k++)
    2184          45 :                             CPLFree(oWK.papanBandSrcValid[k]);
    2185          15 :                         CPLFree(oWK.papanBandSrcValid);
    2186          15 :                         oWK.papanBandSrcValid = nullptr;
    2187             :                     }
    2188             :                 }
    2189             :             }
    2190             :         }
    2191             :     }
    2192             : 
    2193             :     /* -------------------------------------------------------------------- */
    2194             :     /*      Generate a source validity mask if we have a source mask for    */
    2195             :     /*      the whole input dataset (and didn't already treat it as         */
    2196             :     /*      alpha band).                                                    */
    2197             :     /* -------------------------------------------------------------------- */
    2198             :     GDALRasterBandH hSrcBand =
    2199        2338 :         psOptions->nBandCount < 1
    2200        2338 :             ? nullptr
    2201        2338 :             : GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
    2202             : 
    2203        2336 :     if (eErr == CE_None && oWK.pafUnifiedSrcDensity == nullptr &&
    2204        2256 :         oWK.panUnifiedSrcValid == nullptr && psOptions->nSrcAlphaBand <= 0 &&
    2205        2161 :         (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET)
    2206             :         // Need to double check for -nosrcalpha case.
    2207        4676 :         && !(GDALGetMaskFlags(hSrcBand) & GMF_ALPHA) && nSrcXSize > 0 &&
    2208           2 :         nSrcYSize > 0)
    2209             : 
    2210             :     {
    2211           2 :         eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
    2212             : 
    2213           2 :         if (eErr == CE_None)
    2214           2 :             eErr = GDALWarpSrcMaskMasker(
    2215           2 :                 psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
    2216             :                 oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
    2217           2 :                 oWK.papabySrcImage, FALSE, oWK.panUnifiedSrcValid);
    2218             :     }
    2219             : 
    2220             :     /* -------------------------------------------------------------------- */
    2221             :     /*      If we have destination nodata values create the                 */
    2222             :     /*      validity mask.  We set the DstValid for any pixel that we       */
    2223             :     /*      do no have valid data in *any* of the source bands.             */
    2224             :     /*                                                                      */
    2225             :     /*      Note that we don't support any concept of unified nodata on     */
    2226             :     /*      the destination image.  At some point that should be added      */
    2227             :     /*      and then this logic will be significantly different.            */
    2228             :     /* -------------------------------------------------------------------- */
    2229        2338 :     if (eErr == CE_None && psOptions->padfDstNoDataReal != nullptr)
    2230             :     {
    2231         610 :         CPLAssert(oWK.panDstValid == nullptr);
    2232             : 
    2233         610 :         const GPtrDiff_t nMaskBits =
    2234         610 :             static_cast<GPtrDiff_t>(oWK.nDstXSize) * oWK.nDstYSize;
    2235             : 
    2236         610 :         eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstValid");
    2237             :         GUInt32 *panBandMask =
    2238         610 :             eErr == CE_None ? CPLMaskCreate(nMaskBits, true) : nullptr;
    2239             : 
    2240         610 :         if (eErr == CE_None && panBandMask != nullptr)
    2241             :         {
    2242        1172 :             for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
    2243             :             {
    2244         660 :                 CPLMaskSetAll(panBandMask, nMaskBits);
    2245             : 
    2246         660 :                 double adfNoData[2] = {psOptions->padfDstNoDataReal[iBand],
    2247         660 :                                        psOptions->padfDstNoDataImag != nullptr
    2248         660 :                                            ? psOptions->padfDstNoDataImag[iBand]
    2249         660 :                                            : 0.0};
    2250             : 
    2251         660 :                 int bAllValid = FALSE;
    2252        1320 :                 eErr = GDALWarpNoDataMasker(
    2253         660 :                     adfNoData, 1, psOptions->eWorkingDataType, oWK.nDstXOff,
    2254             :                     oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
    2255         660 :                     oWK.papabyDstImage + iBand, FALSE, panBandMask, &bAllValid);
    2256             : 
    2257             :                 // Optimization: if there's a single band and all pixels are
    2258             :                 // valid then we don't need a mask.
    2259         660 :                 if (bAllValid && psOptions->nBandCount == 1)
    2260             :                 {
    2261             : #if DEBUG_VERBOSE
    2262             :                     CPLDebug("WARP", "No need for a destination nodata mask as "
    2263             :                                      "all values are valid");
    2264             : #endif
    2265          98 :                     CPLFree(oWK.panDstValid);
    2266          98 :                     oWK.panDstValid = nullptr;
    2267          98 :                     break;
    2268             :                 }
    2269             : 
    2270         562 :                 CPLMaskMerge(oWK.panDstValid, panBandMask, nMaskBits);
    2271             :             }
    2272         610 :             CPLFree(panBandMask);
    2273             :         }
    2274             :     }
    2275             : 
    2276             :     /* -------------------------------------------------------------------- */
    2277             :     /*      Release IO Mutex, and acquire warper mutex.                     */
    2278             :     /* -------------------------------------------------------------------- */
    2279        2338 :     if (hIOMutex != nullptr)
    2280             :     {
    2281          10 :         CPLReleaseMutex(hIOMutex);
    2282          10 :         if (!CPLAcquireMutex(hWarpMutex, 600.0))
    2283             :         {
    2284           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2285             :                      "Failed to acquire WarpMutex in WarpRegion().");
    2286           0 :             return CE_Failure;
    2287             :         }
    2288             :     }
    2289             : 
    2290             :     /* -------------------------------------------------------------------- */
    2291             :     /*      Optional application provided prewarp chunk processor.          */
    2292             :     /* -------------------------------------------------------------------- */
    2293        2338 :     if (eErr == CE_None && psOptions->pfnPreWarpChunkProcessor != nullptr)
    2294           0 :         eErr = psOptions->pfnPreWarpChunkProcessor(
    2295           0 :             &oWK, psOptions->pPreWarpProcessorArg);
    2296             : 
    2297             :     /* -------------------------------------------------------------------- */
    2298             :     /*      Perform the warp.                                               */
    2299             :     /* -------------------------------------------------------------------- */
    2300        2338 :     if (eErr == CE_None)
    2301             :     {
    2302        2336 :         eErr = oWK.PerformWarp();
    2303        2336 :         ReportTiming("In memory warp operation");
    2304             :     }
    2305             : 
    2306             :     /* -------------------------------------------------------------------- */
    2307             :     /*      Optional application provided postwarp chunk processor.         */
    2308             :     /* -------------------------------------------------------------------- */
    2309        2338 :     if (eErr == CE_None && psOptions->pfnPostWarpChunkProcessor != nullptr)
    2310           0 :         eErr = psOptions->pfnPostWarpChunkProcessor(
    2311           0 :             &oWK, psOptions->pPostWarpProcessorArg);
    2312             : 
    2313             :     /* -------------------------------------------------------------------- */
    2314             :     /*      Release Warp Mutex, and acquire io mutex.                       */
    2315             :     /* -------------------------------------------------------------------- */
    2316        2338 :     if (hIOMutex != nullptr)
    2317             :     {
    2318          10 :         CPLReleaseMutex(hWarpMutex);
    2319          10 :         if (!CPLAcquireMutex(hIOMutex, 600.0))
    2320             :         {
    2321           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2322             :                      "Failed to acquire IOMutex in WarpRegion().");
    2323           0 :             return CE_Failure;
    2324             :         }
    2325             :     }
    2326             : 
    2327             :     /* -------------------------------------------------------------------- */
    2328             :     /*      Write destination alpha if available.                           */
    2329             :     /* -------------------------------------------------------------------- */
    2330        2338 :     if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
    2331             :     {
    2332         245 :         eErr = GDALWarpDstAlphaMasker(
    2333         245 :             psOptions, -psOptions->nBandCount, psOptions->eWorkingDataType,
    2334             :             oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
    2335         245 :             oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
    2336             :     }
    2337             : 
    2338             :     /* -------------------------------------------------------------------- */
    2339             :     /*      Cleanup.                                                        */
    2340             :     /* -------------------------------------------------------------------- */
    2341        2338 :     CPLFree(oWK.papabySrcImage[0]);
    2342        2338 :     CPLFree(oWK.papabySrcImage);
    2343        2338 :     CPLFree(oWK.papabyDstImage);
    2344             : 
    2345        2338 :     if (oWK.papanBandSrcValid != nullptr)
    2346             :     {
    2347          15 :         for (int i = 0; i < oWK.nBands; i++)
    2348          11 :             CPLFree(oWK.papanBandSrcValid[i]);
    2349           4 :         CPLFree(oWK.papanBandSrcValid);
    2350             :     }
    2351        2338 :     CPLFree(oWK.panUnifiedSrcValid);
    2352        2338 :     CPLFree(oWK.pafUnifiedSrcDensity);
    2353        2338 :     CPLFree(oWK.panDstValid);
    2354        2338 :     CPLFree(oWK.pafDstDensity);
    2355             : 
    2356        2338 :     return eErr;
    2357             : }
    2358             : 
    2359             : /************************************************************************/
    2360             : /*                            GDALWarpRegionToBuffer()                  */
    2361             : /************************************************************************/
    2362             : 
    2363             : /**
    2364             :  * @see GDALWarpOperation::WarpRegionToBuffer()
    2365             :  */
    2366             : 
    2367           0 : CPLErr GDALWarpRegionToBuffer(GDALWarpOperationH hOperation, int nDstXOff,
    2368             :                               int nDstYOff, int nDstXSize, int nDstYSize,
    2369             :                               void *pDataBuf, GDALDataType eBufDataType,
    2370             :                               int nSrcXOff, int nSrcYOff, int nSrcXSize,
    2371             :                               int nSrcYSize)
    2372             : 
    2373             : {
    2374           0 :     VALIDATE_POINTER1(hOperation, "GDALWarpRegionToBuffer", CE_Failure);
    2375             : 
    2376             :     return reinterpret_cast<GDALWarpOperation *>(hOperation)
    2377           0 :         ->WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize, pDataBuf,
    2378             :                              eBufDataType, nSrcXOff, nSrcYOff, nSrcXSize,
    2379           0 :                              nSrcYSize);
    2380             : }
    2381             : 
    2382             : /************************************************************************/
    2383             : /*                          CreateKernelMask()                          */
    2384             : /*                                                                      */
    2385             : /*      If mask does not yet exist, create it.  Supported types are     */
    2386             : /*      the name of the variable in question.  That is                  */
    2387             : /*      "BandSrcValid", "UnifiedSrcValid", "UnifiedSrcDensity",         */
    2388             : /*      "DstValid", and "DstDensity".                                   */
    2389             : /************************************************************************/
    2390             : 
    2391        1164 : CPLErr GDALWarpOperation::CreateKernelMask(GDALWarpKernel *poKernel, int iBand,
    2392             :                                            const char *pszType)
    2393             : 
    2394             : {
    2395        1164 :     void **ppMask = nullptr;
    2396        1164 :     int nXSize = 0;
    2397        1164 :     int nYSize = 0;
    2398        1164 :     int nBitsPerPixel = 0;
    2399        1164 :     int nDefault = 0;
    2400        1164 :     int nExtraElts = 0;
    2401        1164 :     bool bDoMemset = true;
    2402             : 
    2403             :     /* -------------------------------------------------------------------- */
    2404             :     /*      Get particulars of mask to be updated.                          */
    2405             :     /* -------------------------------------------------------------------- */
    2406        1164 :     if (EQUAL(pszType, "BandSrcValid"))
    2407             :     {
    2408         177 :         if (poKernel->papanBandSrcValid == nullptr)
    2409         125 :             poKernel->papanBandSrcValid = static_cast<GUInt32 **>(
    2410         125 :                 CPLCalloc(sizeof(void *), poKernel->nBands));
    2411             : 
    2412         177 :         ppMask =
    2413         177 :             reinterpret_cast<void **>(&(poKernel->papanBandSrcValid[iBand]));
    2414         177 :         nExtraElts = WARP_EXTRA_ELTS;
    2415         177 :         nXSize = poKernel->nSrcXSize;
    2416         177 :         nYSize = poKernel->nSrcYSize;
    2417         177 :         nBitsPerPixel = 1;
    2418         177 :         nDefault = 0xff;
    2419             :     }
    2420         987 :     else if (EQUAL(pszType, "UnifiedSrcValid"))
    2421             :     {
    2422          20 :         ppMask = reinterpret_cast<void **>(&(poKernel->panUnifiedSrcValid));
    2423          20 :         nExtraElts = WARP_EXTRA_ELTS;
    2424          20 :         nXSize = poKernel->nSrcXSize;
    2425          20 :         nYSize = poKernel->nSrcYSize;
    2426          20 :         nBitsPerPixel = 1;
    2427          20 :         nDefault = 0xff;
    2428             :     }
    2429         967 :     else if (EQUAL(pszType, "UnifiedSrcDensity"))
    2430             :     {
    2431         112 :         ppMask = reinterpret_cast<void **>(&(poKernel->pafUnifiedSrcDensity));
    2432         112 :         nExtraElts = WARP_EXTRA_ELTS;
    2433         112 :         nXSize = poKernel->nSrcXSize;
    2434         112 :         nYSize = poKernel->nSrcYSize;
    2435         112 :         nBitsPerPixel = 32;
    2436         112 :         nDefault = 0;
    2437         112 :         bDoMemset = false;
    2438             :     }
    2439         855 :     else if (EQUAL(pszType, "DstValid"))
    2440             :     {
    2441         610 :         ppMask = reinterpret_cast<void **>(&(poKernel->panDstValid));
    2442         610 :         nXSize = poKernel->nDstXSize;
    2443         610 :         nYSize = poKernel->nDstYSize;
    2444         610 :         nBitsPerPixel = 1;
    2445         610 :         nDefault = 0;
    2446             :     }
    2447         245 :     else if (EQUAL(pszType, "DstDensity"))
    2448             :     {
    2449         245 :         ppMask = reinterpret_cast<void **>(&(poKernel->pafDstDensity));
    2450         245 :         nXSize = poKernel->nDstXSize;
    2451         245 :         nYSize = poKernel->nDstYSize;
    2452         245 :         nBitsPerPixel = 32;
    2453         245 :         nDefault = 0;
    2454         245 :         bDoMemset = false;
    2455             :     }
    2456             :     else
    2457             :     {
    2458           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2459             :                  "Internal error in CreateKernelMask(%s).", pszType);
    2460           0 :         return CE_Failure;
    2461             :     }
    2462             : 
    2463             :     /* -------------------------------------------------------------------- */
    2464             :     /*      Allocate if needed.                                             */
    2465             :     /* -------------------------------------------------------------------- */
    2466        1164 :     if (*ppMask == nullptr)
    2467             :     {
    2468        1164 :         const GIntBig nBytes =
    2469             :             nBitsPerPixel == 32
    2470        1164 :                 ? (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts) * 4
    2471         807 :                 : (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts + 31) / 8;
    2472             : 
    2473        1164 :         const size_t nByteSize_t = static_cast<size_t>(nBytes);
    2474             : #if SIZEOF_VOIDP == 4
    2475             :         if (static_cast<GIntBig>(nByteSize_t) != nBytes)
    2476             :         {
    2477             :             CPLError(CE_Failure, CPLE_OutOfMemory,
    2478             :                      "Cannot allocate " CPL_FRMT_GIB " bytes", nBytes);
    2479             :             return CE_Failure;
    2480             :         }
    2481             : #endif
    2482             : 
    2483        1164 :         *ppMask = VSI_MALLOC_VERBOSE(nByteSize_t);
    2484             : 
    2485        1164 :         if (*ppMask == nullptr)
    2486             :         {
    2487           0 :             return CE_Failure;
    2488             :         }
    2489             : 
    2490        1164 :         if (bDoMemset)
    2491         807 :             memset(*ppMask, nDefault, nByteSize_t);
    2492             :     }
    2493             : 
    2494        1164 :     return CE_None;
    2495             : }
    2496             : 
    2497             : /************************************************************************/
    2498             : /*               ComputeSourceWindowStartingFromSource()                */
    2499             : /************************************************************************/
    2500             : 
    2501             : constexpr int DEFAULT_STEP_COUNT = 21;
    2502             : 
    2503         434 : void GDALWarpOperation::ComputeSourceWindowStartingFromSource(
    2504             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize,
    2505             :     double *padfSrcMinX, double *padfSrcMinY, double *padfSrcMaxX,
    2506             :     double *padfSrcMaxY)
    2507             : {
    2508         434 :     const int nSrcRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
    2509         434 :     const int nSrcRasterYSize = GDALGetRasterYSize(psOptions->hSrcDS);
    2510         434 :     if (nSrcRasterXSize == 0 || nSrcRasterYSize == 0)
    2511           0 :         return;
    2512             : 
    2513         434 :     GDALWarpPrivateData *privateData = GetWarpPrivateData(this);
    2514         434 :     if (privateData->nStepCount == 0)
    2515             :     {
    2516         142 :         int nStepCount = DEFAULT_STEP_COUNT;
    2517         142 :         std::vector<double> adfDstZ{};
    2518             : 
    2519             :         const char *pszSampleSteps =
    2520         142 :             CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
    2521         142 :         constexpr int knIntMax = std::numeric_limits<int>::max();
    2522         142 :         if (pszSampleSteps && !EQUAL(pszSampleSteps, "ALL"))
    2523             :         {
    2524           0 :             nStepCount = atoi(
    2525           0 :                 CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS"));
    2526           0 :             nStepCount = std::max(2, nStepCount);
    2527             :         }
    2528             : 
    2529         142 :         const double dfStepSize = 1.0 / (nStepCount - 1);
    2530         142 :         if (nStepCount > knIntMax - 2 ||
    2531         142 :             (nStepCount + 2) > knIntMax / (nStepCount + 2))
    2532             :         {
    2533           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
    2534             :                      nStepCount);
    2535           0 :             return;
    2536             :         }
    2537         142 :         const int nSampleMax = (nStepCount + 2) * (nStepCount + 2);
    2538             : 
    2539             :         try
    2540             :         {
    2541         142 :             privateData->abSuccess.resize(nSampleMax);
    2542         142 :             privateData->adfDstX.resize(nSampleMax);
    2543         142 :             privateData->adfDstY.resize(nSampleMax);
    2544         142 :             adfDstZ.resize(nSampleMax);
    2545             :         }
    2546           0 :         catch (const std::exception &)
    2547             :         {
    2548           0 :             return;
    2549             :         }
    2550             : 
    2551             :         /* --------------------------------------------------------------------
    2552             :          */
    2553             :         /*      Setup sample points on a grid pattern throughout the source */
    2554             :         /*      raster. */
    2555             :         /* --------------------------------------------------------------------
    2556             :          */
    2557         142 :         int iPoint = 0;
    2558        3408 :         for (int iY = 0; iY < nStepCount + 2; iY++)
    2559             :         {
    2560        6390 :             const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
    2561        3124 :                                     : (iY <= nStepCount)
    2562        3124 :                                         ? (iY - 1) * dfStepSize
    2563         142 :                                         : 1 - 0.5 / nSrcRasterYSize;
    2564       78384 :             for (int iX = 0; iX < nStepCount + 2; iX++)
    2565             :             {
    2566      146970 :                 const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
    2567       71852 :                                         : (iX <= nStepCount)
    2568       71852 :                                             ? (iX - 1) * dfStepSize
    2569        3266 :                                             : 1 - 0.5 / nSrcRasterXSize;
    2570       75118 :                 privateData->adfDstX[iPoint] = dfRatioX * nSrcRasterXSize;
    2571       75118 :                 privateData->adfDstY[iPoint] = dfRatioY * nSrcRasterYSize;
    2572       75118 :                 iPoint++;
    2573             :             }
    2574             :         }
    2575         142 :         CPLAssert(iPoint == nSampleMax);
    2576             : 
    2577             :         /* --------------------------------------------------------------------
    2578             :          */
    2579             :         /*      Transform them to the output pixel coordinate space */
    2580             :         /* --------------------------------------------------------------------
    2581             :          */
    2582         284 :         if (!psOptions->pfnTransformer(
    2583         142 :                 psOptions->pTransformerArg, FALSE, nSampleMax,
    2584             :                 privateData->adfDstX.data(), privateData->adfDstY.data(),
    2585             :                 adfDstZ.data(), privateData->abSuccess.data()))
    2586             :         {
    2587           0 :             return;
    2588             :         }
    2589             : 
    2590         142 :         privateData->nStepCount = nStepCount;
    2591             :     }
    2592             : 
    2593             :     /* -------------------------------------------------------------------- */
    2594             :     /*      Collect the bounds, ignoring any failed points.                 */
    2595             :     /* -------------------------------------------------------------------- */
    2596         434 :     const int nStepCount = privateData->nStepCount;
    2597         434 :     const double dfStepSize = 1.0 / (nStepCount - 1);
    2598         434 :     int iPoint = 0;
    2599             : #ifdef DEBUG
    2600         434 :     const size_t nSampleMax =
    2601         434 :         static_cast<size_t>(nStepCount + 2) * (nStepCount + 2);
    2602         434 :     CPL_IGNORE_RET_VAL(nSampleMax);
    2603         434 :     CPLAssert(privateData->adfDstX.size() == nSampleMax);
    2604         434 :     CPLAssert(privateData->adfDstY.size() == nSampleMax);
    2605         434 :     CPLAssert(privateData->abSuccess.size() == nSampleMax);
    2606             : #endif
    2607       10416 :     for (int iY = 0; iY < nStepCount + 2; iY++)
    2608             :     {
    2609       19530 :         const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
    2610             :                                 : (iY <= nStepCount)
    2611        9548 :                                     ? (iY - 1) * dfStepSize
    2612         434 :                                     : 1 - 0.5 / nSrcRasterYSize;
    2613      239568 :         for (int iX = 0; iX < nStepCount + 2; iX++)
    2614             :         {
    2615      229586 :             if (privateData->abSuccess[iPoint] &&
    2616      220032 :                 privateData->adfDstX[iPoint] >= nDstXOff &&
    2617      198208 :                 privateData->adfDstX[iPoint] <= nDstXOff + nDstXSize &&
    2618      588116 :                 privateData->adfDstY[iPoint] >= nDstYOff &&
    2619      138498 :                 privateData->adfDstY[iPoint] <= nDstYOff + nDstYSize)
    2620             :             {
    2621      208886 :                 const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
    2622             :                                         : (iX <= nStepCount)
    2623      102142 :                                             ? (iX - 1) * dfStepSize
    2624        4618 :                                             : 1 - 0.5 / nSrcRasterXSize;
    2625      106744 :                 double dfSrcX = dfRatioX * nSrcRasterXSize;
    2626      106744 :                 double dfSrcY = dfRatioY * nSrcRasterYSize;
    2627      106744 :                 *padfSrcMinX = std::min(*padfSrcMinX, dfSrcX);
    2628      106744 :                 *padfSrcMinY = std::min(*padfSrcMinY, dfSrcY);
    2629      106744 :                 *padfSrcMaxX = std::max(*padfSrcMaxX, dfSrcX);
    2630      106744 :                 *padfSrcMaxY = std::max(*padfSrcMaxY, dfSrcY);
    2631             :             }
    2632      229586 :             iPoint++;
    2633             :         }
    2634             :     }
    2635             : }
    2636             : 
    2637             : /************************************************************************/
    2638             : /*                    ComputeSourceWindowTransformPoints()              */
    2639             : /************************************************************************/
    2640             : 
    2641        4685 : bool GDALWarpOperation::ComputeSourceWindowTransformPoints(
    2642             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, bool bUseGrid,
    2643             :     bool bAll, int nStepCount, bool bTryWithCheckWithInvertProj,
    2644             :     double &dfMinXOut, double &dfMinYOut, double &dfMaxXOut, double &dfMaxYOut,
    2645             :     int &nSamplePoints, int &nFailedCount)
    2646             : {
    2647        4685 :     nSamplePoints = 0;
    2648        4685 :     nFailedCount = 0;
    2649             : 
    2650        4685 :     const double dfStepSize = bAll ? 0 : 1.0 / (nStepCount - 1);
    2651        4685 :     constexpr int knIntMax = std::numeric_limits<int>::max();
    2652        4685 :     int nSampleMax = 0;
    2653        4685 :     if (bUseGrid)
    2654             :     {
    2655         494 :         if (bAll)
    2656             :         {
    2657           0 :             if (nDstYSize > knIntMax / (nDstXSize + 1) - 1)
    2658             :             {
    2659           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
    2660           0 :                 return false;
    2661             :             }
    2662           0 :             nSampleMax = (nDstXSize + 1) * (nDstYSize + 1);
    2663             :         }
    2664             :         else
    2665             :         {
    2666         494 :             if (nStepCount > knIntMax - 2 ||
    2667         494 :                 (nStepCount + 2) > knIntMax / (nStepCount + 2))
    2668             :             {
    2669           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
    2670             :                          nStepCount);
    2671           0 :                 return false;
    2672             :             }
    2673         494 :             nSampleMax = (nStepCount + 2) * (nStepCount + 2);
    2674             :         }
    2675             :     }
    2676             :     else
    2677             :     {
    2678        4191 :         if (bAll)
    2679             :         {
    2680         151 :             if (nDstXSize > (knIntMax - 2 * nDstYSize) / 2)
    2681             :             {
    2682             :                 // Extremely unlikely !
    2683           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
    2684           0 :                 return false;
    2685             :             }
    2686         151 :             nSampleMax = 2 * (nDstXSize + nDstYSize);
    2687             :         }
    2688             :         else
    2689             :         {
    2690        4040 :             if (nStepCount > knIntMax / 4)
    2691             :             {
    2692           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d * 4",
    2693             :                          nStepCount);
    2694           0 :                 return false;
    2695             :             }
    2696        4040 :             nSampleMax = nStepCount * 4;
    2697             :         }
    2698             :     }
    2699             : 
    2700             :     int *pabSuccess =
    2701        4685 :         static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nSampleMax));
    2702             :     double *padfX = static_cast<double *>(
    2703        4685 :         VSI_MALLOC2_VERBOSE(sizeof(double) * 3, nSampleMax));
    2704        4685 :     if (pabSuccess == nullptr || padfX == nullptr)
    2705             :     {
    2706           0 :         CPLFree(padfX);
    2707           0 :         CPLFree(pabSuccess);
    2708           0 :         return false;
    2709             :     }
    2710        4685 :     double *padfY = padfX + nSampleMax;
    2711        4685 :     double *padfZ = padfX + nSampleMax * 2;
    2712             : 
    2713             :     /* -------------------------------------------------------------------- */
    2714             :     /*      Setup sample points on a grid pattern throughout the area.      */
    2715             :     /* -------------------------------------------------------------------- */
    2716        4685 :     if (bUseGrid)
    2717             :     {
    2718         494 :         if (bAll)
    2719             :         {
    2720           0 :             for (int iY = 0; iY <= nDstYSize; ++iY)
    2721             :             {
    2722           0 :                 for (int iX = 0; iX <= nDstXSize; ++iX)
    2723             :                 {
    2724           0 :                     padfX[nSamplePoints] = nDstXOff + iX;
    2725           0 :                     padfY[nSamplePoints] = nDstYOff + iY;
    2726           0 :                     padfZ[nSamplePoints++] = 0.0;
    2727             :                 }
    2728             :             }
    2729             :         }
    2730             :         else
    2731             :         {
    2732       11856 :             for (int iY = 0; iY < nStepCount + 2; iY++)
    2733             :             {
    2734       22230 :                 const double dfRatioY = (iY == 0) ? 0.5 / nDstXSize
    2735             :                                         : (iY <= nStepCount)
    2736       10868 :                                             ? (iY - 1) * dfStepSize
    2737         494 :                                             : 1 - 0.5 / nDstXSize;
    2738      272688 :                 for (int iX = 0; iX < nStepCount + 2; iX++)
    2739             :                 {
    2740      511290 :                     const double dfRatioX = (iX == 0) ? 0.5 / nDstXSize
    2741             :                                             : (iX <= nStepCount)
    2742      249964 :                                                 ? (iX - 1) * dfStepSize
    2743       11362 :                                                 : 1 - 0.5 / nDstXSize;
    2744      261326 :                     padfX[nSamplePoints] = dfRatioX * nDstXSize + nDstXOff;
    2745      261326 :                     padfY[nSamplePoints] = dfRatioY * nDstYSize + nDstYOff;
    2746      261326 :                     padfZ[nSamplePoints++] = 0.0;
    2747             :                 }
    2748             :             }
    2749             :         }
    2750             :     }
    2751             :     /* -------------------------------------------------------------------- */
    2752             :     /*      Setup sample points all around the edge of the output raster.   */
    2753             :     /* -------------------------------------------------------------------- */
    2754             :     else
    2755             :     {
    2756        4191 :         if (bAll)
    2757             :         {
    2758       68258 :             for (int iX = 0; iX <= nDstXSize; ++iX)
    2759             :             {
    2760             :                 // Along top
    2761       68107 :                 padfX[nSamplePoints] = nDstXOff + iX;
    2762       68107 :                 padfY[nSamplePoints] = nDstYOff;
    2763       68107 :                 padfZ[nSamplePoints++] = 0.0;
    2764             : 
    2765             :                 // Along bottom
    2766       68107 :                 padfX[nSamplePoints] = nDstXOff + iX;
    2767       68107 :                 padfY[nSamplePoints] = nDstYOff + nDstYSize;
    2768       68107 :                 padfZ[nSamplePoints++] = 0.0;
    2769             :             }
    2770             : 
    2771       43329 :             for (int iY = 1; iY < nDstYSize; ++iY)
    2772             :             {
    2773             :                 // Along left
    2774       43178 :                 padfX[nSamplePoints] = nDstXOff;
    2775       43178 :                 padfY[nSamplePoints] = nDstYOff + iY;
    2776       43178 :                 padfZ[nSamplePoints++] = 0.0;
    2777             : 
    2778             :                 // Along right
    2779       43178 :                 padfX[nSamplePoints] = nDstXOff + nDstXSize;
    2780       43178 :                 padfY[nSamplePoints] = nDstYOff + iY;
    2781       43178 :                 padfZ[nSamplePoints++] = 0.0;
    2782             :             }
    2783             :         }
    2784             :         else
    2785             :         {
    2786       88880 :             for (double dfRatio = 0.0; dfRatio <= 1.0 + dfStepSize * 0.5;
    2787       84840 :                  dfRatio += dfStepSize)
    2788             :             {
    2789             :                 // Along top
    2790       84840 :                 padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
    2791       84840 :                 padfY[nSamplePoints] = nDstYOff;
    2792       84840 :                 padfZ[nSamplePoints++] = 0.0;
    2793             : 
    2794             :                 // Along bottom
    2795       84840 :                 padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
    2796       84840 :                 padfY[nSamplePoints] = nDstYOff + nDstYSize;
    2797       84840 :                 padfZ[nSamplePoints++] = 0.0;
    2798             : 
    2799             :                 // Along left
    2800       84840 :                 padfX[nSamplePoints] = nDstXOff;
    2801       84840 :                 padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
    2802       84840 :                 padfZ[nSamplePoints++] = 0.0;
    2803             : 
    2804             :                 // Along right
    2805       84840 :                 padfX[nSamplePoints] = nDstXSize + nDstXOff;
    2806       84840 :                 padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
    2807       84840 :                 padfZ[nSamplePoints++] = 0.0;
    2808             :             }
    2809             :         }
    2810             :     }
    2811             : 
    2812        4685 :     CPLAssert(nSamplePoints == nSampleMax);
    2813             : 
    2814             :     /* -------------------------------------------------------------------- */
    2815             :     /*      Transform them to the input pixel coordinate space              */
    2816             :     /* -------------------------------------------------------------------- */
    2817             : 
    2818         132 :     const auto RefreshTransformer = [this]()
    2819             :     {
    2820          44 :         if (GDALIsTransformer(psOptions->pTransformerArg,
    2821             :                               GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
    2822             :         {
    2823           0 :             GDALRefreshGenImgProjTransformer(psOptions->pTransformerArg);
    2824             :         }
    2825          44 :         else if (GDALIsTransformer(psOptions->pTransformerArg,
    2826             :                                    GDAL_APPROX_TRANSFORMER_CLASS_NAME))
    2827             :         {
    2828          44 :             GDALRefreshApproxTransformer(psOptions->pTransformerArg);
    2829             :         }
    2830        4729 :     };
    2831             : 
    2832        4685 :     if (bTryWithCheckWithInvertProj)
    2833             :     {
    2834          22 :         CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", "YES");
    2835          22 :         RefreshTransformer();
    2836             :     }
    2837        4685 :     int ret = psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE,
    2838             :                                         nSamplePoints, padfX, padfY, padfZ,
    2839             :                                         pabSuccess);
    2840        4685 :     if (bTryWithCheckWithInvertProj)
    2841             :     {
    2842          22 :         CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", nullptr);
    2843          22 :         RefreshTransformer();
    2844             :     }
    2845             : 
    2846        4685 :     if (!ret)
    2847             :     {
    2848           0 :         CPLFree(padfX);
    2849           0 :         CPLFree(pabSuccess);
    2850             : 
    2851           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2852             :                  "GDALWarperOperation::ComputeSourceWindow() failed because "
    2853             :                  "the pfnTransformer failed.");
    2854           0 :         return false;
    2855             :     }
    2856             : 
    2857             :     /* -------------------------------------------------------------------- */
    2858             :     /*      Collect the bounds, ignoring any failed points.                 */
    2859             :     /* -------------------------------------------------------------------- */
    2860      827941 :     for (int i = 0; i < nSamplePoints; i++)
    2861             :     {
    2862      823256 :         if (!pabSuccess[i])
    2863             :         {
    2864      107923 :             nFailedCount++;
    2865      107923 :             continue;
    2866             :         }
    2867             : 
    2868             :         // If this happens this is likely the symptom of a bug somewhere.
    2869      715333 :         if (std::isnan(padfX[i]) || std::isnan(padfY[i]))
    2870             :         {
    2871             :             static bool bNanCoordFound = false;
    2872           0 :             if (!bNanCoordFound)
    2873             :             {
    2874           0 :                 CPLDebug("WARP",
    2875             :                          "ComputeSourceWindow(): "
    2876             :                          "NaN coordinate found on point %d.",
    2877             :                          i);
    2878           0 :                 bNanCoordFound = true;
    2879             :             }
    2880           0 :             nFailedCount++;
    2881           0 :             continue;
    2882             :         }
    2883             : 
    2884      715333 :         dfMinXOut = std::min(dfMinXOut, padfX[i]);
    2885      715333 :         dfMinYOut = std::min(dfMinYOut, padfY[i]);
    2886      715333 :         dfMaxXOut = std::max(dfMaxXOut, padfX[i]);
    2887      715333 :         dfMaxYOut = std::max(dfMaxYOut, padfY[i]);
    2888             :     }
    2889             : 
    2890        4685 :     CPLFree(padfX);
    2891        4685 :     CPLFree(pabSuccess);
    2892        4685 :     return true;
    2893             : }
    2894             : 
    2895             : /************************************************************************/
    2896             : /*                        ComputeSourceWindow()                         */
    2897             : /************************************************************************/
    2898             : 
    2899             : /** Given a target window starting at pixel (nDstOff, nDstYOff) and of
    2900             :  * dimension (nDstXSize, nDstYSize), compute the corresponding window in
    2901             :  * the source raster, and return the source position in (*pnSrcXOff, *pnSrcYOff),
    2902             :  * the source dimension in (*pnSrcXSize, *pnSrcYSize).
    2903             :  * If pdfSrcXExtraSize is not null, its pointed value will be filled with the
    2904             :  * number of extra source pixels in X dimension to acquire to take into account
    2905             :  * the size of the resampling kernel. Similarly for pdfSrcYExtraSize for the
    2906             :  * Y dimension.
    2907             :  * If pdfSrcFillRatio is not null, its pointed value will be filled with the
    2908             :  * the ratio of the clamped source raster window size over the unclamped source
    2909             :  * raster window size. When this ratio is too low, this might be an indication
    2910             :  * that it might be beneficial to split the target window to avoid requesting
    2911             :  * too many source pixels.
    2912             :  */
    2913        4379 : CPLErr GDALWarpOperation::ComputeSourceWindow(
    2914             :     int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, int *pnSrcXOff,
    2915             :     int *pnSrcYOff, int *pnSrcXSize, int *pnSrcYSize, double *pdfSrcXExtraSize,
    2916             :     double *pdfSrcYExtraSize, double *pdfSrcFillRatio)
    2917             : 
    2918             : {
    2919             :     /* -------------------------------------------------------------------- */
    2920             :     /*      Figure out whether we just want to do the usual "along the      */
    2921             :     /*      edge" sampling, or using a grid.  The grid usage is             */
    2922             :     /*      important in some weird "inside out" cases like WGS84 to        */
    2923             :     /*      polar stereographic around the pole.   Also figure out the      */
    2924             :     /*      sampling rate.                                                  */
    2925             :     /* -------------------------------------------------------------------- */
    2926        4379 :     int nStepCount = DEFAULT_STEP_COUNT;
    2927        4379 :     bool bAll = false;
    2928             : 
    2929             :     bool bUseGrid =
    2930        4379 :         CPLFetchBool(psOptions->papszWarpOptions, "SAMPLE_GRID", false);
    2931             : 
    2932             :     const char *pszSampleSteps =
    2933        4379 :         CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
    2934        4379 :     if (pszSampleSteps)
    2935             :     {
    2936          94 :         if (EQUAL(pszSampleSteps, "ALL"))
    2937             :         {
    2938          94 :             bAll = true;
    2939             :         }
    2940             :         else
    2941             :         {
    2942           0 :             nStepCount = atoi(pszSampleSteps);
    2943           0 :             nStepCount = std::max(2, nStepCount);
    2944             :         }
    2945             :     }
    2946        4285 :     else if (!bUseGrid)
    2947             :     {
    2948             :         // Detect if at least one of the 4 corner in destination raster fails
    2949             :         // to project back to source.
    2950             :         // Helps for long-lat to orthographic on areas that are partly in
    2951             :         // space / partly on Earth. Cf https://github.com/OSGeo/gdal/issues/9056
    2952             :         double adfCornerX[4];
    2953             :         double adfCornerY[4];
    2954        4097 :         double adfCornerZ[4] = {0, 0, 0, 0};
    2955        4097 :         int anCornerSuccess[4] = {FALSE, FALSE, FALSE, FALSE};
    2956        4097 :         adfCornerX[0] = nDstXOff;
    2957        4097 :         adfCornerY[0] = nDstYOff;
    2958        4097 :         adfCornerX[1] = nDstXOff + nDstXSize;
    2959        4097 :         adfCornerY[1] = nDstYOff;
    2960        4097 :         adfCornerX[2] = nDstXOff;
    2961        4097 :         adfCornerY[2] = nDstYOff + nDstYSize;
    2962        4097 :         adfCornerX[3] = nDstXOff + nDstXSize;
    2963        4097 :         adfCornerY[3] = nDstYOff + nDstYSize;
    2964        4097 :         if (!psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE, 4,
    2965             :                                        adfCornerX, adfCornerY, adfCornerZ,
    2966        4097 :                                        anCornerSuccess) ||
    2967        8137 :             !anCornerSuccess[0] || !anCornerSuccess[1] || !anCornerSuccess[2] ||
    2968        4040 :             !anCornerSuccess[3])
    2969             :         {
    2970          57 :             bAll = true;
    2971             :         }
    2972             :     }
    2973             : 
    2974        4379 :     bool bTryWithCheckWithInvertProj = false;
    2975        4379 :     double dfMinXOut = std::numeric_limits<double>::infinity();
    2976        4379 :     double dfMinYOut = std::numeric_limits<double>::infinity();
    2977        4379 :     double dfMaxXOut = -std::numeric_limits<double>::infinity();
    2978        4379 :     double dfMaxYOut = -std::numeric_limits<double>::infinity();
    2979             : 
    2980        4379 :     int nSamplePoints = 0;
    2981        4379 :     int nFailedCount = 0;
    2982        4379 :     if (!ComputeSourceWindowTransformPoints(
    2983             :             nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
    2984             :             nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
    2985             :             dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
    2986             :     {
    2987           0 :         return CE_Failure;
    2988             :     }
    2989             : 
    2990             :     // Use grid sampling as soon as a special point falls into the extent of
    2991             :     // the target raster.
    2992        4379 :     if (!bUseGrid && psOptions->hDstDS)
    2993             :     {
    2994       10584 :         for (const auto &xy : aDstXYSpecialPoints)
    2995             :         {
    2996       13984 :             if (0 <= xy.first &&
    2997         718 :                 GDALGetRasterXSize(psOptions->hDstDS) >= xy.first &&
    2998        7651 :                 0 <= xy.second &&
    2999         300 :                 GDALGetRasterYSize(psOptions->hDstDS) >= xy.second)
    3000             :             {
    3001         227 :                 bUseGrid = true;
    3002         227 :                 bAll = false;
    3003         227 :                 if (!ComputeSourceWindowTransformPoints(
    3004             :                         nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid,
    3005             :                         bAll, nStepCount, bTryWithCheckWithInvertProj,
    3006             :                         dfMinXOut, dfMinYOut, dfMaxXOut, dfMaxYOut,
    3007             :                         nSamplePoints, nFailedCount))
    3008             :                 {
    3009           0 :                     return CE_Failure;
    3010             :                 }
    3011         227 :                 break;
    3012             :             }
    3013             :         }
    3014             :     }
    3015             : 
    3016        4379 :     const int nRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
    3017        4379 :     const int nRasterYSize = GDALGetRasterYSize(psOptions->hSrcDS);
    3018             : 
    3019             :     // Try to detect crazy values coming from reprojection that would not
    3020             :     // have resulted in a PROJ error. Could happen for example with PROJ
    3021             :     // <= 4.9.2 with inverse UTM/tmerc (Snyder approximation without sanity
    3022             :     // check) when being far away from the central meridian. But might be worth
    3023             :     // keeping that even for later versions in case some exotic projection isn't
    3024             :     // properly sanitized.
    3025        4314 :     if (nFailedCount == 0 && !bTryWithCheckWithInvertProj &&
    3026        4314 :         (dfMinXOut < -1e6 || dfMinYOut < -1e6 ||
    3027        8697 :          dfMaxXOut > nRasterXSize + 1e6 || dfMaxYOut > nRasterYSize + 1e6) &&
    3028          22 :         !CPLTestBool(CPLGetConfigOption("CHECK_WITH_INVERT_PROJ", "NO")))
    3029             :     {
    3030          22 :         CPLDebug("WARP",
    3031             :                  "ComputeSourceWindow(): bogus source dataset window "
    3032             :                  "returned. Trying again with CHECK_WITH_INVERT_PROJ=YES");
    3033          22 :         bTryWithCheckWithInvertProj = true;
    3034             : 
    3035             :         // We should probably perform the coordinate transformation in the
    3036             :         // warp kernel under CHECK_WITH_INVERT_PROJ too...
    3037          22 :         if (!ComputeSourceWindowTransformPoints(
    3038             :                 nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
    3039             :                 nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
    3040             :                 dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
    3041             :         {
    3042           0 :             return CE_Failure;
    3043             :         }
    3044             :     }
    3045             : 
    3046             :     /* -------------------------------------------------------------------- */
    3047             :     /*      If we got any failures when not using a grid, we should         */
    3048             :     /*      really go back and try again with the grid.  Sorry for the      */
    3049             :     /*      goto.                                                           */
    3050             :     /* -------------------------------------------------------------------- */
    3051        4379 :     if (!bUseGrid && nFailedCount > 0)
    3052             :     {
    3053          57 :         bUseGrid = true;
    3054          57 :         bAll = false;
    3055          57 :         if (!ComputeSourceWindowTransformPoints(
    3056             :                 nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
    3057             :                 nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
    3058             :                 dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
    3059             :         {
    3060           0 :             return CE_Failure;
    3061             :         }
    3062             :     }
    3063             : 
    3064             :     /* -------------------------------------------------------------------- */
    3065             :     /*      If we get hardly any points (or none) transforming, we give     */
    3066             :     /*      up.                                                             */
    3067             :     /* -------------------------------------------------------------------- */
    3068        4379 :     if (nFailedCount > nSamplePoints - 5)
    3069             :     {
    3070             :         const bool bErrorOutIfEmptySourceWindow =
    3071          38 :             CPLFetchBool(psOptions->papszWarpOptions,
    3072             :                          "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
    3073          38 :         if (bErrorOutIfEmptySourceWindow)
    3074             :         {
    3075           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3076             :                      "Too many points (%d out of %d) failed to transform, "
    3077             :                      "unable to compute output bounds.",
    3078             :                      nFailedCount, nSamplePoints);
    3079             :         }
    3080             :         else
    3081             :         {
    3082          36 :             CPLDebug("WARP", "Cannot determine source window for %d,%d,%d,%d",
    3083             :                      nDstXOff, nDstYOff, nDstXSize, nDstYSize);
    3084             :         }
    3085          38 :         return CE_Failure;
    3086             :     }
    3087             : 
    3088        4341 :     if (nFailedCount > 0)
    3089          31 :         CPLDebug("GDAL",
    3090             :                  "GDALWarpOperation::ComputeSourceWindow() %d out of %d "
    3091             :                  "points failed to transform.",
    3092             :                  nFailedCount, nSamplePoints);
    3093             : 
    3094             :     /* -------------------------------------------------------------------- */
    3095             :     /*   In some cases (see https://github.com/OSGeo/gdal/issues/862)       */
    3096             :     /*   the reverse transform does not work at some points, so try by      */
    3097             :     /*   transforming from source raster space to target raster space and   */
    3098             :     /*   see which source coordinates end up being in the AOI in the target */
    3099             :     /*   raster space.                                                      */
    3100             :     /* -------------------------------------------------------------------- */
    3101        4341 :     if (bUseGrid)
    3102             :     {
    3103         434 :         ComputeSourceWindowStartingFromSource(nDstXOff, nDstYOff, nDstXSize,
    3104             :                                               nDstYSize, &dfMinXOut, &dfMinYOut,
    3105             :                                               &dfMaxXOut, &dfMaxYOut);
    3106             :     }
    3107             : 
    3108             :     /* -------------------------------------------------------------------- */
    3109             :     /*   Early exit to avoid crazy values to cause a huge nResWinSize that  */
    3110             :     /*   would result in a result window wrongly covering the whole raster. */
    3111             :     /* -------------------------------------------------------------------- */
    3112        4341 :     if (dfMinXOut > nRasterXSize || dfMaxXOut < 0 || dfMinYOut > nRasterYSize ||
    3113        3732 :         dfMaxYOut < 0)
    3114             :     {
    3115        1012 :         *pnSrcXOff = 0;
    3116        1012 :         *pnSrcYOff = 0;
    3117        1012 :         *pnSrcXSize = 0;
    3118        1012 :         *pnSrcYSize = 0;
    3119        1012 :         if (pdfSrcXExtraSize)
    3120        1012 :             *pdfSrcXExtraSize = 0.0;
    3121        1012 :         if (pdfSrcYExtraSize)
    3122        1012 :             *pdfSrcYExtraSize = 0.0;
    3123        1012 :         if (pdfSrcFillRatio)
    3124         745 :             *pdfSrcFillRatio = 0.0;
    3125        1012 :         return CE_None;
    3126             :     }
    3127             : 
    3128             :     // For scenarios where warping is used as a "decoration", try to clamp
    3129             :     // source pixel coordinates to integer when very close.
    3130       13316 :     const auto roundIfCloseEnough = [](double dfVal)
    3131             :     {
    3132       13316 :         const double dfRounded = std::round(dfVal);
    3133       13316 :         if (std::fabs(dfRounded - dfVal) < 1e-6)
    3134       11061 :             return dfRounded;
    3135        2255 :         return dfVal;
    3136             :     };
    3137             : 
    3138        3329 :     dfMinXOut = roundIfCloseEnough(dfMinXOut);
    3139        3329 :     dfMinYOut = roundIfCloseEnough(dfMinYOut);
    3140        3329 :     dfMaxXOut = roundIfCloseEnough(dfMaxXOut);
    3141        3329 :     dfMaxYOut = roundIfCloseEnough(dfMaxYOut);
    3142             : 
    3143        3329 :     if (m_bIsTranslationOnPixelBoundaries)
    3144             :     {
    3145         182 :         CPLAssert(dfMinXOut == std::round(dfMinXOut));
    3146         182 :         CPLAssert(dfMinYOut == std::round(dfMinYOut));
    3147         182 :         CPLAssert(dfMaxXOut == std::round(dfMaxXOut));
    3148         182 :         CPLAssert(dfMaxYOut == std::round(dfMaxYOut));
    3149         182 :         CPLAssert(std::round(dfMaxXOut - dfMinXOut) == nDstXSize);
    3150         182 :         CPLAssert(std::round(dfMaxYOut - dfMinYOut) == nDstYSize);
    3151             :     }
    3152             : 
    3153             :     /* -------------------------------------------------------------------- */
    3154             :     /*      How much of a window around our source pixel might we need      */
    3155             :     /*      to collect data from based on the resampling kernel?  Even      */
    3156             :     /*      if the requested central pixel falls off the source image,      */
    3157             :     /*      we may need to collect data if some portion of the              */
    3158             :     /*      resampling kernel could be on-image.                            */
    3159             :     /* -------------------------------------------------------------------- */
    3160        3329 :     const int nResWinSize = m_bIsTranslationOnPixelBoundaries
    3161        3329 :                                 ? 0
    3162        3147 :                                 : GWKGetFilterRadius(psOptions->eResampleAlg);
    3163             : 
    3164             :     // Take scaling into account.
    3165             :     // Avoid ridiculous small scaling factors to avoid potential further integer
    3166             :     // overflows
    3167        6658 :     const double dfXScale = std::max(1e-3, static_cast<double>(nDstXSize) /
    3168        3329 :                                                (dfMaxXOut - dfMinXOut));
    3169        6658 :     const double dfYScale = std::max(1e-3, static_cast<double>(nDstYSize) /
    3170        3329 :                                                (dfMaxYOut - dfMinYOut));
    3171        3329 :     int nXRadius = dfXScale < 0.95
    3172        3329 :                        ? static_cast<int>(ceil(nResWinSize / dfXScale))
    3173             :                        : nResWinSize;
    3174        3329 :     int nYRadius = dfYScale < 0.95
    3175        3329 :                        ? static_cast<int>(ceil(nResWinSize / dfYScale))
    3176             :                        : nResWinSize;
    3177             : 
    3178             :     /* -------------------------------------------------------------------- */
    3179             :     /*      Allow addition of extra sample pixels to source window to       */
    3180             :     /*      avoid missing pixels due to sampling error.  In fact,           */
    3181             :     /*      fallback to adding a bit to the window if any points failed     */
    3182             :     /*      to transform.                                                   */
    3183             :     /* -------------------------------------------------------------------- */
    3184        3329 :     if (CSLFetchNameValue(psOptions->papszWarpOptions, "SOURCE_EXTRA") !=
    3185             :         nullptr)
    3186             :     {
    3187         160 :         const int nSrcExtra = atoi(
    3188          80 :             CSLFetchNameValue(psOptions->papszWarpOptions, "SOURCE_EXTRA"));
    3189          80 :         nXRadius += nSrcExtra;
    3190          80 :         nYRadius += nSrcExtra;
    3191             :     }
    3192        3249 :     else if (nFailedCount > 0)
    3193             :     {
    3194          25 :         nXRadius += 10;
    3195          25 :         nYRadius += 10;
    3196             :     }
    3197             : 
    3198             : /* -------------------------------------------------------------------- */
    3199             : /*      return bounds.                                                  */
    3200             : /* -------------------------------------------------------------------- */
    3201             : #if DEBUG_VERBOSE
    3202             :     CPLDebug("WARP",
    3203             :              "dst=(%d,%d,%d,%d) raw "
    3204             :              "src=(minx=%.17g,miny=%.17g,maxx=%.17g,maxy=%.17g)",
    3205             :              nDstXOff, nDstYOff, nDstXSize, nDstYSize, dfMinXOut, dfMinYOut,
    3206             :              dfMaxXOut, dfMaxYOut);
    3207             : #endif
    3208        3329 :     const int nMinXOutClamped = static_cast<int>(std::max(0.0, dfMinXOut));
    3209        3329 :     const int nMinYOutClamped = static_cast<int>(std::max(0.0, dfMinYOut));
    3210             :     const int nMaxXOutClamped = static_cast<int>(
    3211        3329 :         std::min(ceil(dfMaxXOut), static_cast<double>(nRasterXSize)));
    3212             :     const int nMaxYOutClamped = static_cast<int>(
    3213        3329 :         std::min(ceil(dfMaxYOut), static_cast<double>(nRasterYSize)));
    3214             : 
    3215             :     const double dfSrcXSizeRaw = std::max(
    3216        9987 :         0.0, std::min(static_cast<double>(nRasterXSize - nMinXOutClamped),
    3217        3329 :                       dfMaxXOut - dfMinXOut));
    3218             :     const double dfSrcYSizeRaw = std::max(
    3219        9987 :         0.0, std::min(static_cast<double>(nRasterYSize - nMinYOutClamped),
    3220        3329 :                       dfMaxYOut - dfMinYOut));
    3221             : 
    3222             :     // If we cover more than 90% of the width, then use it fully (helps for
    3223             :     // anti-meridian discontinuities)
    3224        3329 :     if (nMaxXOutClamped - nMinXOutClamped > 0.9 * nRasterXSize)
    3225             :     {
    3226        1288 :         *pnSrcXOff = 0;
    3227        1288 :         *pnSrcXSize = nRasterXSize;
    3228             :     }
    3229             :     else
    3230             :     {
    3231        2041 :         *pnSrcXOff =
    3232        2041 :             std::max(0, std::min(nMinXOutClamped - nXRadius, nRasterXSize));
    3233        2041 :         *pnSrcXSize =
    3234        6123 :             std::max(0, std::min(nRasterXSize - *pnSrcXOff,
    3235        2041 :                                  nMaxXOutClamped - *pnSrcXOff + nXRadius));
    3236             :     }
    3237             : 
    3238        3329 :     if (nMaxYOutClamped - nMinYOutClamped > 0.9 * nRasterYSize)
    3239             :     {
    3240        1170 :         *pnSrcYOff = 0;
    3241        1170 :         *pnSrcYSize = nRasterYSize;
    3242             :     }
    3243             :     else
    3244             :     {
    3245        2159 :         *pnSrcYOff =
    3246        2159 :             std::max(0, std::min(nMinYOutClamped - nYRadius, nRasterYSize));
    3247        2159 :         *pnSrcYSize =
    3248        6477 :             std::max(0, std::min(nRasterYSize - *pnSrcYOff,
    3249        2159 :                                  nMaxYOutClamped - *pnSrcYOff + nYRadius));
    3250             :     }
    3251             : 
    3252        3329 :     if (pdfSrcXExtraSize)
    3253        3329 :         *pdfSrcXExtraSize = *pnSrcXSize - dfSrcXSizeRaw;
    3254        3329 :     if (pdfSrcYExtraSize)
    3255        3329 :         *pdfSrcYExtraSize = *pnSrcYSize - dfSrcYSizeRaw;
    3256             : 
    3257             :     // Computed the ratio of the clamped source raster window size over
    3258             :     // the unclamped source raster window size.
    3259        3329 :     if (pdfSrcFillRatio)
    3260        3136 :         *pdfSrcFillRatio =
    3261        6272 :             static_cast<double>(*pnSrcXSize) * (*pnSrcYSize) /
    3262        3136 :             std::max(1.0, (dfMaxXOut - dfMinXOut + 2 * nXRadius) *
    3263        3136 :                               (dfMaxYOut - dfMinYOut + 2 * nYRadius));
    3264             : 
    3265        3329 :     return CE_None;
    3266             : }
    3267             : 
    3268             : /************************************************************************/
    3269             : /*                            ReportTiming()                            */
    3270             : /************************************************************************/
    3271             : 
    3272        9011 : void GDALWarpOperation::ReportTiming(const char *pszMessage)
    3273             : 
    3274             : {
    3275        9011 :     if (!bReportTimings)
    3276        9011 :         return;
    3277             : 
    3278           0 :     const unsigned long nNewTime = VSITime(nullptr);
    3279             : 
    3280           0 :     if (pszMessage != nullptr)
    3281             :     {
    3282           0 :         CPLDebug("WARP_TIMING", "%s: %lds", pszMessage,
    3283           0 :                  static_cast<long>(nNewTime - nLastTimeReported));
    3284             :     }
    3285             : 
    3286           0 :     nLastTimeReported = nNewTime;
    3287             : }

Generated by: LCOV version 1.14