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

Generated by: LCOV version 1.14