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

Generated by: LCOV version 1.14