LCOV - code coverage report
Current view: top level - alg - gdalwarpoperation.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1083 1250 86.6 %
Date: 2024-05-04 12:52:34 Functions: 32 36 88.9 %

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

Generated by: LCOV version 1.14