Coverage Report

Created: 2025-06-22 06:59

/src/gdal/alg/gdalwarpoperation.cpp
Line
Count
Source (jump to first uncovered line)
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
GetWarpPrivateData(GDALWarpOperation *poWarpOperation)
65
0
{
66
0
    std::lock_guard<std::mutex> oLock(gMutex);
67
0
    auto oItem = gMapPrivate.find(poWarpOperation);
68
0
    if (oItem != gMapPrivate.end())
69
0
    {
70
0
        return oItem->second.get();
71
0
    }
72
0
    else
73
0
    {
74
0
        gMapPrivate[poWarpOperation] =
75
0
            std::unique_ptr<GDALWarpPrivateData>(new GDALWarpPrivateData());
76
0
        return gMapPrivate[poWarpOperation].get();
77
0
    }
78
0
}
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
0
GDALWarpOperation::GDALWarpOperation() = default;
158
159
/************************************************************************/
160
/*                         ~GDALWarpOperation()                         */
161
/************************************************************************/
162
163
GDALWarpOperation::~GDALWarpOperation()
164
165
0
{
166
0
    {
167
0
        std::lock_guard<std::mutex> oLock(gMutex);
168
0
        auto oItem = gMapPrivate.find(this);
169
0
        if (oItem != gMapPrivate.end())
170
0
        {
171
0
            gMapPrivate.erase(oItem);
172
0
        }
173
0
    }
174
175
0
    WipeOptions();
176
177
0
    if (hIOMutex != nullptr)
178
0
    {
179
0
        CPLDestroyMutex(hIOMutex);
180
0
        CPLDestroyMutex(hWarpMutex);
181
0
    }
182
183
0
    WipeChunkList();
184
0
    if (psThreadData)
185
0
        GWKThreadsEnd(psThreadData);
186
0
}
187
188
/************************************************************************/
189
/*                             GetOptions()                             */
190
/************************************************************************/
191
192
/** Return warp options */
193
const GDALWarpOptions *GDALWarpOperation::GetOptions()
194
195
0
{
196
0
    return psOptions;
197
0
}
198
199
/************************************************************************/
200
/*                            WipeOptions()                             */
201
/************************************************************************/
202
203
void GDALWarpOperation::WipeOptions()
204
205
0
{
206
0
    if (psOptions != nullptr)
207
0
    {
208
0
        GDALDestroyWarpOptions(psOptions);
209
0
        psOptions = nullptr;
210
0
    }
211
0
}
212
213
/************************************************************************/
214
/*                          ValidateOptions()                           */
215
/************************************************************************/
216
217
int GDALWarpOperation::ValidateOptions()
218
219
0
{
220
0
    if (psOptions == nullptr)
221
0
    {
222
0
        CPLError(CE_Failure, CPLE_IllegalArg,
223
0
                 "GDALWarpOptions.Validate(): "
224
0
                 "no options currently initialized.");
225
0
        return FALSE;
226
0
    }
227
228
0
    if (psOptions->dfWarpMemoryLimit < 100000.0)
229
0
    {
230
0
        CPLError(CE_Failure, CPLE_IllegalArg,
231
0
                 "GDALWarpOptions.Validate(): "
232
0
                 "dfWarpMemoryLimit=%g is unreasonably small.",
233
0
                 psOptions->dfWarpMemoryLimit);
234
0
        return FALSE;
235
0
    }
236
237
0
    if (psOptions->eResampleAlg != GRA_NearestNeighbour &&
238
0
        psOptions->eResampleAlg != GRA_Bilinear &&
239
0
        psOptions->eResampleAlg != GRA_Cubic &&
240
0
        psOptions->eResampleAlg != GRA_CubicSpline &&
241
0
        psOptions->eResampleAlg != GRA_Lanczos &&
242
0
        psOptions->eResampleAlg != GRA_Average &&
243
0
        psOptions->eResampleAlg != GRA_RMS &&
244
0
        psOptions->eResampleAlg != GRA_Mode &&
245
0
        psOptions->eResampleAlg != GRA_Max &&
246
0
        psOptions->eResampleAlg != GRA_Min &&
247
0
        psOptions->eResampleAlg != GRA_Med &&
248
0
        psOptions->eResampleAlg != GRA_Q1 &&
249
0
        psOptions->eResampleAlg != GRA_Q3 && psOptions->eResampleAlg != GRA_Sum)
250
0
    {
251
0
        CPLError(CE_Failure, CPLE_IllegalArg,
252
0
                 "GDALWarpOptions.Validate(): "
253
0
                 "eResampleArg=%d is not a supported value.",
254
0
                 psOptions->eResampleAlg);
255
0
        return FALSE;
256
0
    }
257
258
0
    if (static_cast<int>(psOptions->eWorkingDataType) < 1 ||
259
0
        static_cast<int>(psOptions->eWorkingDataType) >= GDT_TypeCount)
260
0
    {
261
0
        CPLError(CE_Failure, CPLE_IllegalArg,
262
0
                 "GDALWarpOptions.Validate(): "
263
0
                 "eWorkingDataType=%d is not a supported value.",
264
0
                 psOptions->eWorkingDataType);
265
0
        return FALSE;
266
0
    }
267
268
0
    if (GDALDataTypeIsComplex(psOptions->eWorkingDataType) != 0 &&
269
0
        (psOptions->eResampleAlg == GRA_Mode ||
270
0
         psOptions->eResampleAlg == GRA_Max ||
271
0
         psOptions->eResampleAlg == GRA_Min ||
272
0
         psOptions->eResampleAlg == GRA_Med ||
273
0
         psOptions->eResampleAlg == GRA_Q1 ||
274
0
         psOptions->eResampleAlg == GRA_Q3))
275
0
    {
276
277
0
        CPLError(CE_Failure, CPLE_NotSupported,
278
0
                 "GDALWarpOptions.Validate(): "
279
0
                 "min/max/qnt not supported for complex valued data.");
280
0
        return FALSE;
281
0
    }
282
283
0
    if (psOptions->hSrcDS == nullptr)
284
0
    {
285
0
        CPLError(CE_Failure, CPLE_IllegalArg,
286
0
                 "GDALWarpOptions.Validate(): "
287
0
                 "hSrcDS is not set.");
288
0
        return FALSE;
289
0
    }
290
291
0
    if (psOptions->nBandCount == 0)
292
0
    {
293
0
        CPLError(CE_Failure, CPLE_IllegalArg,
294
0
                 "GDALWarpOptions.Validate(): "
295
0
                 "nBandCount=0, no bands configured!");
296
0
        return FALSE;
297
0
    }
298
299
0
    if (psOptions->panSrcBands == nullptr)
300
0
    {
301
0
        CPLError(CE_Failure, CPLE_IllegalArg,
302
0
                 "GDALWarpOptions.Validate(): "
303
0
                 "panSrcBands is NULL.");
304
0
        return FALSE;
305
0
    }
306
307
0
    if (psOptions->hDstDS != nullptr && psOptions->panDstBands == nullptr)
308
0
    {
309
0
        CPLError(CE_Failure, CPLE_IllegalArg,
310
0
                 "GDALWarpOptions.Validate(): "
311
0
                 "panDstBands is NULL.");
312
0
        return FALSE;
313
0
    }
314
315
0
    for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
316
0
    {
317
0
        if (psOptions->panSrcBands[iBand] < 1 ||
318
0
            psOptions->panSrcBands[iBand] >
319
0
                GDALGetRasterCount(psOptions->hSrcDS))
320
0
        {
321
0
            CPLError(CE_Failure, CPLE_IllegalArg,
322
0
                     "panSrcBands[%d] = %d ... out of range for dataset.",
323
0
                     iBand, psOptions->panSrcBands[iBand]);
324
0
            return FALSE;
325
0
        }
326
0
        if (psOptions->hDstDS != nullptr &&
327
0
            (psOptions->panDstBands[iBand] < 1 ||
328
0
             psOptions->panDstBands[iBand] >
329
0
                 GDALGetRasterCount(psOptions->hDstDS)))
330
0
        {
331
0
            CPLError(CE_Failure, CPLE_IllegalArg,
332
0
                     "panDstBands[%d] = %d ... out of range for dataset.",
333
0
                     iBand, psOptions->panDstBands[iBand]);
334
0
            return FALSE;
335
0
        }
336
337
0
        if (psOptions->hDstDS != nullptr &&
338
0
            GDALGetRasterAccess(GDALGetRasterBand(
339
0
                psOptions->hDstDS, psOptions->panDstBands[iBand])) ==
340
0
                GA_ReadOnly)
341
0
        {
342
0
            CPLError(CE_Failure, CPLE_IllegalArg,
343
0
                     "Destination band %d appears to be read-only.",
344
0
                     psOptions->panDstBands[iBand]);
345
0
            return FALSE;
346
0
        }
347
0
    }
348
349
0
    if (psOptions->nBandCount == 0)
350
0
    {
351
0
        CPLError(CE_Failure, CPLE_IllegalArg,
352
0
                 "GDALWarpOptions.Validate(): "
353
0
                 "nBandCount=0, no bands configured!");
354
0
        return FALSE;
355
0
    }
356
357
0
    if (psOptions->pfnProgress == nullptr)
358
0
    {
359
0
        CPLError(CE_Failure, CPLE_IllegalArg,
360
0
                 "GDALWarpOptions.Validate(): "
361
0
                 "pfnProgress is NULL.");
362
0
        return FALSE;
363
0
    }
364
365
0
    if (psOptions->pfnTransformer == nullptr)
366
0
    {
367
0
        CPLError(CE_Failure, CPLE_IllegalArg,
368
0
                 "GDALWarpOptions.Validate(): "
369
0
                 "pfnTransformer is NULL.");
370
0
        return FALSE;
371
0
    }
372
373
0
    {
374
0
        CPLStringList aosWO(CSLDuplicate(psOptions->papszWarpOptions));
375
        // A few internal/undocumented options
376
0
        aosWO.SetNameValue("EXTRA_ELTS", nullptr);
377
0
        aosWO.SetNameValue("USE_GENERAL_CASE", nullptr);
378
0
        aosWO.SetNameValue("ERROR_THRESHOLD", nullptr);
379
0
        aosWO.SetNameValue("ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", nullptr);
380
0
        aosWO.SetNameValue("MULT_FACTOR_VERTICAL_SHIFT_PIPELINE", nullptr);
381
0
        aosWO.SetNameValue("SRC_FILL_RATIO_HEURISTICS", nullptr);
382
0
        GDALValidateOptions(GDALWarpGetOptionList(), aosWO.List(), "option",
383
0
                            "warp options");
384
0
    }
385
386
0
    const char *pszSampleSteps =
387
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
388
0
    if (pszSampleSteps)
389
0
    {
390
0
        if (!EQUAL(pszSampleSteps, "ALL") && atoi(pszSampleSteps) < 2)
391
0
        {
392
0
            CPLError(CE_Failure, CPLE_IllegalArg,
393
0
                     "GDALWarpOptions.Validate(): "
394
0
                     "SAMPLE_STEPS warp option has illegal value.");
395
0
            return FALSE;
396
0
        }
397
0
    }
398
399
0
    if (psOptions->nSrcAlphaBand > 0)
400
0
    {
401
0
        if (psOptions->hSrcDS == nullptr ||
402
0
            psOptions->nSrcAlphaBand > GDALGetRasterCount(psOptions->hSrcDS))
403
0
        {
404
0
            CPLError(CE_Failure, CPLE_IllegalArg,
405
0
                     "nSrcAlphaBand = %d ... out of range for dataset.",
406
0
                     psOptions->nSrcAlphaBand);
407
0
            return FALSE;
408
0
        }
409
0
    }
410
411
0
    if (psOptions->nDstAlphaBand > 0)
412
0
    {
413
0
        if (psOptions->hDstDS == nullptr ||
414
0
            psOptions->nDstAlphaBand > GDALGetRasterCount(psOptions->hDstDS))
415
0
        {
416
0
            CPLError(CE_Failure, CPLE_IllegalArg,
417
0
                     "nDstAlphaBand = %d ... out of range for dataset.",
418
0
                     psOptions->nDstAlphaBand);
419
0
            return FALSE;
420
0
        }
421
0
    }
422
423
0
    if (psOptions->nSrcAlphaBand > 0 &&
424
0
        psOptions->pfnSrcDensityMaskFunc != nullptr)
425
0
    {
426
0
        CPLError(CE_Failure, CPLE_IllegalArg,
427
0
                 "GDALWarpOptions.Validate(): "
428
0
                 "pfnSrcDensityMaskFunc provided as well as a SrcAlphaBand.");
429
0
        return FALSE;
430
0
    }
431
432
0
    if (psOptions->nDstAlphaBand > 0 &&
433
0
        psOptions->pfnDstDensityMaskFunc != nullptr)
434
0
    {
435
0
        CPLError(CE_Failure, CPLE_IllegalArg,
436
0
                 "GDALWarpOptions.Validate(): "
437
0
                 "pfnDstDensityMaskFunc provided as well as a DstAlphaBand.");
438
0
        return FALSE;
439
0
    }
440
441
0
    const bool bErrorOutIfEmptySourceWindow = CPLFetchBool(
442
0
        psOptions->papszWarpOptions, "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
443
0
    if (!bErrorOutIfEmptySourceWindow &&
444
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST") == nullptr)
445
0
    {
446
0
        CPLError(CE_Failure, CPLE_IllegalArg,
447
0
                 "GDALWarpOptions.Validate(): "
448
0
                 "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW=FALSE can only be used "
449
0
                 "if INIT_DEST is set");
450
0
        return FALSE;
451
0
    }
452
453
0
    return TRUE;
454
0
}
455
456
/************************************************************************/
457
/*                            SetAlphaMax()                             */
458
/************************************************************************/
459
460
static void SetAlphaMax(GDALWarpOptions *psOptions, GDALRasterBandH hBand,
461
                        const char *pszKey)
462
0
{
463
0
    const char *pszNBits =
464
0
        GDALGetMetadataItem(hBand, "NBITS", "IMAGE_STRUCTURE");
465
0
    const char *pszAlphaMax = nullptr;
466
0
    if (pszNBits)
467
0
    {
468
0
        pszAlphaMax = CPLSPrintf("%u", (1U << atoi(pszNBits)) - 1U);
469
0
    }
470
0
    else if (GDALGetRasterDataType(hBand) == GDT_Int16)
471
0
    {
472
0
        pszAlphaMax = "32767";
473
0
    }
474
0
    else if (GDALGetRasterDataType(hBand) == GDT_UInt16)
475
0
    {
476
0
        pszAlphaMax = "65535";
477
0
    }
478
479
0
    if (pszAlphaMax != nullptr)
480
0
        psOptions->papszWarpOptions =
481
0
            CSLSetNameValue(psOptions->papszWarpOptions, pszKey, pszAlphaMax);
482
0
    else
483
0
        CPLDebug("WARP", "SetAlphaMax: AlphaMax not set.");
484
0
}
485
486
/************************************************************************/
487
/*                            SetTieStrategy()                          */
488
/************************************************************************/
489
490
static void SetTieStrategy(GDALWarpOptions *psOptions, CPLErr *peErr)
491
0
{
492
0
    if (const char *pszTieStrategy =
493
0
            CSLFetchNameValue(psOptions->papszWarpOptions, "MODE_TIES"))
494
0
    {
495
0
        if (EQUAL(pszTieStrategy, "FIRST"))
496
0
        {
497
0
            psOptions->eTieStrategy = GWKTS_First;
498
0
        }
499
0
        else if (EQUAL(pszTieStrategy, "MIN"))
500
0
        {
501
0
            psOptions->eTieStrategy = GWKTS_Min;
502
0
        }
503
0
        else if (EQUAL(pszTieStrategy, "MAX"))
504
0
        {
505
0
            psOptions->eTieStrategy = GWKTS_Max;
506
0
        }
507
0
        else
508
0
        {
509
0
            CPLError(CE_Failure, CPLE_IllegalArg,
510
0
                     "Unknown value of MODE_TIES: %s", pszTieStrategy);
511
0
            *peErr = CE_Failure;
512
0
        }
513
0
    }
514
0
}
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
GDALWarpOperation::Initialize(const GDALWarpOptions *psNewOptions,
547
                              GDALTransformerFunc pfnTransformer,
548
                              GDALTransformerArgUniquePtr psOwnedTransformerArg)
549
550
0
{
551
    /* -------------------------------------------------------------------- */
552
    /*      Copy the passed in options.                                     */
553
    /* -------------------------------------------------------------------- */
554
0
    if (psOptions != nullptr)
555
0
        WipeOptions();
556
557
0
    CPLErr eErr = CE_None;
558
559
0
    psOptions = GDALCloneWarpOptions(psNewOptions);
560
561
0
    if (psOptions->pfnTransformer)
562
0
    {
563
0
        CPLAssert(pfnTransformer == nullptr);
564
0
        CPLAssert(psOwnedTransformerArg.get() == nullptr);
565
0
    }
566
0
    else
567
0
    {
568
0
        m_psOwnedTransformerArg = std::move(psOwnedTransformerArg);
569
0
        psOptions->pfnTransformer = pfnTransformer;
570
0
        psOptions->pTransformerArg = m_psOwnedTransformerArg.get();
571
0
    }
572
573
0
    psOptions->papszWarpOptions =
574
0
        CSLSetNameValue(psOptions->papszWarpOptions, "EXTRA_ELTS",
575
0
                        CPLSPrintf("%d", WARP_EXTRA_ELTS));
576
577
    /* -------------------------------------------------------------------- */
578
    /*      Default band mapping if missing.                                */
579
    /* -------------------------------------------------------------------- */
580
0
    if (psOptions->nBandCount == 0 && psOptions->hSrcDS != nullptr &&
581
0
        psOptions->hDstDS != nullptr &&
582
0
        GDALGetRasterCount(psOptions->hSrcDS) ==
583
0
            GDALGetRasterCount(psOptions->hDstDS))
584
0
    {
585
0
        GDALWarpInitDefaultBandMapping(psOptions,
586
0
                                       GDALGetRasterCount(psOptions->hSrcDS));
587
0
    }
588
589
0
    GDALWarpResolveWorkingDataType(psOptions);
590
0
    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
0
    if (psOptions->dfWarpMemoryLimit == 0.0)
600
0
    {
601
0
        psOptions->dfWarpMemoryLimit = 64.0 * 1024 * 1024;
602
0
    }
603
604
    /* -------------------------------------------------------------------- */
605
    /*      Are we doing timings?                                           */
606
    /* -------------------------------------------------------------------- */
607
0
    bReportTimings =
608
0
        CPLFetchBool(psOptions->papszWarpOptions, "REPORT_TIMINGS", false);
609
610
    /* -------------------------------------------------------------------- */
611
    /*      Support creating cutline from text warpoption.                  */
612
    /* -------------------------------------------------------------------- */
613
0
    const char *pszCutlineWKT =
614
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE");
615
616
0
    if (pszCutlineWKT && psOptions->hCutline == nullptr)
617
0
    {
618
0
        char *pszWKTTmp = const_cast<char *>(pszCutlineWKT);
619
0
        if (OGR_G_CreateFromWkt(&pszWKTTmp, nullptr,
620
0
                                reinterpret_cast<OGRGeometryH *>(
621
0
                                    &(psOptions->hCutline))) != OGRERR_NONE)
622
0
        {
623
0
            eErr = CE_Failure;
624
0
            CPLError(CE_Failure, CPLE_AppDefined,
625
0
                     "Failed to parse CUTLINE geometry wkt.");
626
0
        }
627
0
    }
628
0
    const char *pszBD =
629
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE_BLEND_DIST");
630
0
    if (pszBD)
631
0
        psOptions->dfCutlineBlendDist = CPLAtof(pszBD);
632
633
    /* -------------------------------------------------------------------- */
634
    /*      Set SRC_ALPHA_MAX if not provided.                              */
635
    /* -------------------------------------------------------------------- */
636
0
    if (psOptions->hSrcDS != nullptr && psOptions->nSrcAlphaBand > 0 &&
637
0
        psOptions->nSrcAlphaBand <= GDALGetRasterCount(psOptions->hSrcDS) &&
638
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "SRC_ALPHA_MAX") ==
639
0
            nullptr)
640
0
    {
641
0
        GDALRasterBandH hSrcAlphaBand =
642
0
            GDALGetRasterBand(psOptions->hSrcDS, psOptions->nSrcAlphaBand);
643
0
        SetAlphaMax(psOptions, hSrcAlphaBand, "SRC_ALPHA_MAX");
644
0
    }
645
646
    /* -------------------------------------------------------------------- */
647
    /*      Set DST_ALPHA_MAX if not provided.                              */
648
    /* -------------------------------------------------------------------- */
649
0
    if (psOptions->hDstDS != nullptr && psOptions->nDstAlphaBand > 0 &&
650
0
        psOptions->nDstAlphaBand <= GDALGetRasterCount(psOptions->hDstDS) &&
651
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "DST_ALPHA_MAX") ==
652
0
            nullptr)
653
0
    {
654
0
        GDALRasterBandH hDstAlphaBand =
655
0
            GDALGetRasterBand(psOptions->hDstDS, psOptions->nDstAlphaBand);
656
0
        SetAlphaMax(psOptions, hDstAlphaBand, "DST_ALPHA_MAX");
657
0
    }
658
659
    /* -------------------------------------------------------------------- */
660
    /*      If the options don't validate, then wipe them.                  */
661
    /* -------------------------------------------------------------------- */
662
0
    if (!ValidateOptions())
663
0
        eErr = CE_Failure;
664
665
0
    if (eErr != CE_None)
666
0
    {
667
0
        WipeOptions();
668
0
    }
669
0
    else
670
0
    {
671
0
        psThreadData = GWKThreadsCreate(psOptions->papszWarpOptions,
672
0
                                        psOptions->pfnTransformer,
673
0
                                        psOptions->pTransformerArg);
674
0
        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
0
        for (double dfY : {-89.9999, 89.9999})
686
0
        {
687
0
            double dfX = 0;
688
0
            if ((GDALIsTransformer(psOptions->pTransformerArg,
689
0
                                   GDAL_APPROX_TRANSFORMER_CLASS_NAME) &&
690
0
                 GDALTransformLonLatToDestApproxTransformer(
691
0
                     psOptions->pTransformerArg, &dfX, &dfY)) ||
692
0
                (GDALIsTransformer(psOptions->pTransformerArg,
693
0
                                   GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME) &&
694
0
                 GDALTransformLonLatToDestGenImgProjTransformer(
695
0
                     psOptions->pTransformerArg, &dfX, &dfY)))
696
0
            {
697
0
                aDstXYSpecialPoints.emplace_back(
698
0
                    std::pair<double, double>(dfX, dfY));
699
0
            }
700
0
        }
701
702
0
        m_bIsTranslationOnPixelBoundaries =
703
0
            GDALTransformIsTranslationOnPixelBoundaries(
704
0
                psOptions->pfnTransformer, psOptions->pTransformerArg) &&
705
0
            CPLTestBool(
706
0
                CPLGetConfigOption("GDAL_WARP_USE_TRANSLATION_OPTIM", "YES"));
707
0
        if (m_bIsTranslationOnPixelBoundaries)
708
0
        {
709
0
            CPLDebug("WARP",
710
0
                     "Using translation-on-pixel-boundaries optimization");
711
0
        }
712
0
    }
713
714
0
    return eErr;
715
0
}
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
void *GDALWarpOperation::CreateDestinationBuffer(int nDstXSize, int nDstYSize,
733
                                                 int *pbInitialized)
734
0
{
735
736
    /* -------------------------------------------------------------------- */
737
    /*      Allocate block of memory large enough to hold all the bands     */
738
    /*      for this block.                                                 */
739
    /* -------------------------------------------------------------------- */
740
0
    const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
741
742
0
    void *pDstBuffer = VSI_MALLOC3_VERBOSE(
743
0
        cpl::fits_on<int>(nWordSize * psOptions->nBandCount), nDstXSize,
744
0
        nDstYSize);
745
0
    if (pDstBuffer)
746
0
    {
747
0
        auto eErr = InitializeDestinationBuffer(pDstBuffer, nDstXSize,
748
0
                                                nDstYSize, pbInitialized);
749
0
        if (eErr != CE_None)
750
0
        {
751
0
            CPLFree(pDstBuffer);
752
0
            return nullptr;
753
0
        }
754
0
    }
755
0
    return pDstBuffer;
756
0
}
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
CPLErr GDALWarpOperation::InitializeDestinationBuffer(void *pDstBuffer,
778
                                                      int nDstXSize,
779
                                                      int nDstYSize,
780
                                                      int *pbInitialized) const
781
0
{
782
0
    const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
783
784
0
    const GPtrDiff_t nBandSize =
785
0
        static_cast<GPtrDiff_t>(nWordSize) * nDstXSize * nDstYSize;
786
787
    /* -------------------------------------------------------------------- */
788
    /*      Initialize if requested in the options */
789
    /* -------------------------------------------------------------------- */
790
0
    const char *pszInitDest =
791
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST");
792
793
0
    if (pszInitDest == nullptr || EQUAL(pszInitDest, ""))
794
0
    {
795
0
        if (pbInitialized != nullptr)
796
0
        {
797
0
            *pbInitialized = FALSE;
798
0
        }
799
0
        return CE_None;
800
0
    }
801
802
0
    if (pbInitialized != nullptr)
803
0
    {
804
0
        *pbInitialized = TRUE;
805
0
    }
806
807
0
    CPLStringList aosInitValues(
808
0
        CSLTokenizeStringComplex(pszInitDest, ",", FALSE, FALSE));
809
0
    const int nInitCount = aosInitValues.Count();
810
811
0
    for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
812
0
    {
813
0
        double adfInitRealImag[2] = {0.0, 0.0};
814
0
        const char *pszBandInit =
815
0
            aosInitValues[std::min(iBand, nInitCount - 1)];
816
817
0
        if (EQUAL(pszBandInit, "NO_DATA"))
818
0
        {
819
0
            if (psOptions->padfDstNoDataReal == nullptr)
820
0
            {
821
                // TODO: Change to CE_Failure for GDAL 3.12
822
                // See https://github.com/OSGeo/gdal/pull/12189
823
0
                CPLError(CE_Warning, CPLE_AppDefined,
824
0
                         "INIT_DEST was set to NO_DATA, but a NoData value was "
825
0
                         "not defined. This warning will become a failure in a "
826
0
                         "future GDAL release.");
827
0
                return CE_Failure;
828
0
            }
829
830
0
            adfInitRealImag[0] = psOptions->padfDstNoDataReal[iBand];
831
0
            if (psOptions->padfDstNoDataImag != nullptr)
832
0
            {
833
0
                adfInitRealImag[1] = psOptions->padfDstNoDataImag[iBand];
834
0
            }
835
0
        }
836
0
        else
837
0
        {
838
0
            if (CPLStringToComplex(pszBandInit, &adfInitRealImag[0],
839
0
                                   &adfInitRealImag[1]) != CE_None)
840
0
            {
841
0
                CPLError(CE_Failure, CPLE_AppDefined,
842
0
                         "Error parsing INIT_DEST");
843
0
                return CE_Failure;
844
0
            }
845
0
        }
846
847
0
        GByte *pBandData = static_cast<GByte *>(pDstBuffer) + iBand * nBandSize;
848
849
0
        if (psOptions->eWorkingDataType == GDT_Byte)
850
0
        {
851
0
            memset(pBandData,
852
0
                   std::max(
853
0
                       0, std::min(255, static_cast<int>(adfInitRealImag[0]))),
854
0
                   nBandSize);
855
0
        }
856
0
        else if (!std::isnan(adfInitRealImag[0]) && adfInitRealImag[0] == 0.0 &&
857
0
                 !std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
858
0
        {
859
0
            memset(pBandData, 0, nBandSize);
860
0
        }
861
0
        else if (!std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
862
0
        {
863
0
            GDALCopyWords64(&adfInitRealImag, GDT_Float64, 0, pBandData,
864
0
                            psOptions->eWorkingDataType, nWordSize,
865
0
                            static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
866
0
        }
867
0
        else
868
0
        {
869
0
            GDALCopyWords64(&adfInitRealImag, GDT_CFloat64, 0, pBandData,
870
0
                            psOptions->eWorkingDataType, nWordSize,
871
0
                            static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
872
0
        }
873
0
    }
874
875
0
    return CE_None;
876
0
}
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
void GDALWarpOperation::DestroyDestinationBuffer(void *pDstBuffer)
888
0
{
889
0
    VSIFree(pDstBuffer);
890
0
}
891
892
/************************************************************************/
893
/*                         GDALCreateWarpOperation()                    */
894
/************************************************************************/
895
896
/**
897
 * @see GDALWarpOperation::Initialize()
898
 */
899
900
GDALWarpOperationH GDALCreateWarpOperation(const GDALWarpOptions *psNewOptions)
901
0
{
902
0
    GDALWarpOperation *poOperation = new GDALWarpOperation;
903
0
    if (poOperation->Initialize(psNewOptions) != CE_None)
904
0
    {
905
0
        delete poOperation;
906
0
        return nullptr;
907
0
    }
908
909
0
    return reinterpret_cast<GDALWarpOperationH>(poOperation);
910
0
}
911
912
/************************************************************************/
913
/*                         GDALDestroyWarpOperation()                   */
914
/************************************************************************/
915
916
/**
917
 * @see GDALWarpOperation::~GDALWarpOperation()
918
 */
919
920
void GDALDestroyWarpOperation(GDALWarpOperationH hOperation)
921
0
{
922
0
    if (hOperation)
923
0
        delete static_cast<GDALWarpOperation *>(hOperation);
924
0
}
925
926
/************************************************************************/
927
/*                          CollectChunkList()                          */
928
/************************************************************************/
929
930
void GDALWarpOperation::CollectChunkList(int nDstXOff, int nDstYOff,
931
                                         int nDstXSize, int nDstYSize)
932
933
0
{
934
    /* -------------------------------------------------------------------- */
935
    /*      Collect the list of chunks to operate on.                       */
936
    /* -------------------------------------------------------------------- */
937
0
    WipeChunkList();
938
0
    CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
939
940
    // Sort chunks from top to bottom, and for equal y, from left to right.
941
0
    if (nChunkListCount > 1)
942
0
    {
943
0
        std::sort(pasChunkList, pasChunkList + nChunkListCount,
944
0
                  [](const GDALWarpChunk &a, const GDALWarpChunk &b)
945
0
                  {
946
0
                      if (a.dy < b.dy)
947
0
                          return true;
948
0
                      if (a.dy > b.dy)
949
0
                          return false;
950
0
                      return a.dx < b.dx;
951
0
                  });
952
0
    }
953
954
    /* -------------------------------------------------------------------- */
955
    /*      Find the global source window.                                  */
956
    /* -------------------------------------------------------------------- */
957
958
0
    const int knIntMax = std::numeric_limits<int>::max();
959
0
    const int knIntMin = std::numeric_limits<int>::min();
960
0
    int nSrcXOff = knIntMax;
961
0
    int nSrcYOff = knIntMax;
962
0
    int nSrcX2Off = knIntMin;
963
0
    int nSrcY2Off = knIntMin;
964
0
    double dfApproxAccArea = 0;
965
0
    for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
966
0
         iChunk++)
967
0
    {
968
0
        GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
969
0
        nSrcXOff = std::min(nSrcXOff, pasThisChunk->sx);
970
0
        nSrcYOff = std::min(nSrcYOff, pasThisChunk->sy);
971
0
        nSrcX2Off = std::max(nSrcX2Off, pasThisChunk->sx + pasThisChunk->ssx);
972
0
        nSrcY2Off = std::max(nSrcY2Off, pasThisChunk->sy + pasThisChunk->ssy);
973
0
        dfApproxAccArea +=
974
0
            static_cast<double>(pasThisChunk->ssx) * pasThisChunk->ssy;
975
0
    }
976
0
    if (nSrcXOff < nSrcX2Off)
977
0
    {
978
0
        const double dfTotalArea =
979
0
            static_cast<double>(nSrcX2Off - nSrcXOff) * (nSrcY2Off - nSrcYOff);
980
        // This is really a gross heuristics, but should work in most cases
981
0
        if (dfApproxAccArea >= dfTotalArea * 0.80)
982
0
        {
983
0
            GDALDataset::FromHandle(psOptions->hSrcDS)
984
0
                ->AdviseRead(nSrcXOff, nSrcYOff, nSrcX2Off - nSrcXOff,
985
0
                             nSrcY2Off - nSrcYOff, nDstXSize, nDstYSize,
986
0
                             psOptions->eWorkingDataType, psOptions->nBandCount,
987
0
                             psOptions->panSrcBands, nullptr);
988
0
        }
989
0
    }
990
0
}
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
CPLErr GDALWarpOperation::ChunkAndWarpImage(int nDstXOff, int nDstYOff,
1020
                                            int nDstXSize, int nDstYSize)
1021
1022
0
{
1023
    /* -------------------------------------------------------------------- */
1024
    /*      Collect the list of chunks to operate on.                       */
1025
    /* -------------------------------------------------------------------- */
1026
0
    CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1027
1028
    /* -------------------------------------------------------------------- */
1029
    /*      Total up output pixels to process.                              */
1030
    /* -------------------------------------------------------------------- */
1031
0
    double dfTotalPixels = 0.0;
1032
1033
0
    for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
1034
0
         iChunk++)
1035
0
    {
1036
0
        GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1037
0
        const double dfChunkPixels =
1038
0
            pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1039
1040
0
        dfTotalPixels += dfChunkPixels;
1041
0
    }
1042
1043
    /* -------------------------------------------------------------------- */
1044
    /*      Process them one at a time, updating the progress               */
1045
    /*      information for each region.                                    */
1046
    /* -------------------------------------------------------------------- */
1047
0
    double dfPixelsProcessed = 0.0;
1048
1049
0
    for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
1050
0
         iChunk++)
1051
0
    {
1052
0
        GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1053
0
        const double dfChunkPixels =
1054
0
            pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1055
1056
0
        const double dfProgressBase = dfPixelsProcessed / dfTotalPixels;
1057
0
        const double dfProgressScale = dfChunkPixels / dfTotalPixels;
1058
1059
0
        CPLErr eErr = WarpRegion(
1060
0
            pasThisChunk->dx, pasThisChunk->dy, pasThisChunk->dsx,
1061
0
            pasThisChunk->dsy, pasThisChunk->sx, pasThisChunk->sy,
1062
0
            pasThisChunk->ssx, pasThisChunk->ssy, pasThisChunk->sExtraSx,
1063
0
            pasThisChunk->sExtraSy, dfProgressBase, dfProgressScale);
1064
1065
0
        if (eErr != CE_None)
1066
0
            return eErr;
1067
1068
0
        dfPixelsProcessed += dfChunkPixels;
1069
0
    }
1070
1071
0
    WipeChunkList();
1072
1073
0
    psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
1074
1075
0
    return CE_None;
1076
0
}
1077
1078
/************************************************************************/
1079
/*                         GDALChunkAndWarpImage()                      */
1080
/************************************************************************/
1081
1082
/**
1083
 * @see GDALWarpOperation::ChunkAndWarpImage()
1084
 */
1085
1086
CPLErr GDALChunkAndWarpImage(GDALWarpOperationH hOperation, int nDstXOff,
1087
                             int nDstYOff, int nDstXSize, int nDstYSize)
1088
0
{
1089
0
    VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpImage", CE_Failure);
1090
1091
0
    return reinterpret_cast<GDALWarpOperation *>(hOperation)
1092
0
        ->ChunkAndWarpImage(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1093
0
}
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
static void ChunkThreadMain(void *pThreadData)
1117
1118
0
{
1119
0
    volatile ChunkThreadData *psData =
1120
0
        static_cast<volatile ChunkThreadData *>(pThreadData);
1121
1122
0
    GDALWarpChunk *pasChunkInfo = psData->pasChunkInfo;
1123
1124
    /* -------------------------------------------------------------------- */
1125
    /*      Acquire IO mutex.                                               */
1126
    /* -------------------------------------------------------------------- */
1127
0
    if (!CPLAcquireMutex(psData->hIOMutex, 600.0))
1128
0
    {
1129
0
        CPLError(CE_Failure, CPLE_AppDefined,
1130
0
                 "Failed to acquire IOMutex in WarpRegion().");
1131
0
        psData->eErr = CE_Failure;
1132
0
    }
1133
0
    else
1134
0
    {
1135
0
        if (psData->hCond != nullptr)
1136
0
        {
1137
0
            CPLAcquireMutex(psData->hCondMutex, 1.0);
1138
0
            psData->bIOMutexTaken = TRUE;
1139
0
            CPLCondSignal(psData->hCond);
1140
0
            CPLReleaseMutex(psData->hCondMutex);
1141
0
        }
1142
1143
0
        auto oAccumulator =
1144
0
            psData->poErrorAccumulator->InstallForCurrentScope();
1145
0
        CPL_IGNORE_RET_VAL(oAccumulator);
1146
1147
0
        psData->eErr = psData->poOperation->WarpRegion(
1148
0
            pasChunkInfo->dx, pasChunkInfo->dy, pasChunkInfo->dsx,
1149
0
            pasChunkInfo->dsy, pasChunkInfo->sx, pasChunkInfo->sy,
1150
0
            pasChunkInfo->ssx, pasChunkInfo->ssy, pasChunkInfo->sExtraSx,
1151
0
            pasChunkInfo->sExtraSy, psData->dfProgressBase,
1152
0
            psData->dfProgressScale);
1153
1154
        /* --------------------------------------------------------------------
1155
         */
1156
        /*      Release the IO mutex. */
1157
        /* --------------------------------------------------------------------
1158
         */
1159
0
        CPLReleaseMutex(psData->hIOMutex);
1160
0
    }
1161
0
}
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
CPLErr GDALWarpOperation::ChunkAndWarpMulti(int nDstXOff, int nDstYOff,
1188
                                            int nDstXSize, int nDstYSize)
1189
1190
0
{
1191
0
    hIOMutex = CPLCreateMutex();
1192
0
    hWarpMutex = CPLCreateMutex();
1193
1194
0
    CPLReleaseMutex(hIOMutex);
1195
0
    CPLReleaseMutex(hWarpMutex);
1196
1197
0
    CPLCond *hCond = CPLCreateCond();
1198
0
    CPLMutex *hCondMutex = CPLCreateMutex();
1199
0
    CPLReleaseMutex(hCondMutex);
1200
1201
    /* -------------------------------------------------------------------- */
1202
    /*      Collect the list of chunks to operate on.                       */
1203
    /* -------------------------------------------------------------------- */
1204
0
    CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1205
1206
    /* -------------------------------------------------------------------- */
1207
    /*      Process them one at a time, updating the progress               */
1208
    /*      information for each region.                                    */
1209
    /* -------------------------------------------------------------------- */
1210
0
    ChunkThreadData volatile asThreadData[2] = {};
1211
0
    CPLErrorAccumulator oErrorAccumulator;
1212
0
    for (int i = 0; i < 2; ++i)
1213
0
    {
1214
0
        asThreadData[i].poOperation = this;
1215
0
        asThreadData[i].hIOMutex = hIOMutex;
1216
0
        asThreadData[i].poErrorAccumulator = &oErrorAccumulator;
1217
0
    }
1218
1219
0
    double dfPixelsProcessed = 0.0;
1220
0
    double dfTotalPixels = static_cast<double>(nDstXSize) * nDstYSize;
1221
1222
0
    CPLErr eErr = CE_None;
1223
0
    for (int iChunk = 0; iChunk < nChunkListCount + 1; iChunk++)
1224
0
    {
1225
0
        int iThread = iChunk % 2;
1226
1227
        /* --------------------------------------------------------------------
1228
         */
1229
        /*      Launch thread for this chunk. */
1230
        /* --------------------------------------------------------------------
1231
         */
1232
0
        if (pasChunkList != nullptr && iChunk < nChunkListCount)
1233
0
        {
1234
0
            GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1235
0
            const double dfChunkPixels =
1236
0
                pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1237
1238
0
            asThreadData[iThread].dfProgressBase =
1239
0
                dfPixelsProcessed / dfTotalPixels;
1240
0
            asThreadData[iThread].dfProgressScale =
1241
0
                dfChunkPixels / dfTotalPixels;
1242
1243
0
            dfPixelsProcessed += dfChunkPixels;
1244
1245
0
            asThreadData[iThread].pasChunkInfo = pasThisChunk;
1246
1247
0
            if (iChunk == 0)
1248
0
            {
1249
0
                asThreadData[iThread].hCond = hCond;
1250
0
                asThreadData[iThread].hCondMutex = hCondMutex;
1251
0
            }
1252
0
            else
1253
0
            {
1254
0
                asThreadData[iThread].hCond = nullptr;
1255
0
                asThreadData[iThread].hCondMutex = nullptr;
1256
0
            }
1257
0
            asThreadData[iThread].bIOMutexTaken = FALSE;
1258
1259
0
            CPLDebug("GDAL", "Start chunk %d / %d.", iChunk, nChunkListCount);
1260
0
            asThreadData[iThread].hThreadHandle = CPLCreateJoinableThread(
1261
0
                ChunkThreadMain,
1262
0
                const_cast<ChunkThreadData *>(&asThreadData[iThread]));
1263
0
            if (asThreadData[iThread].hThreadHandle == nullptr)
1264
0
            {
1265
0
                CPLError(
1266
0
                    CE_Failure, CPLE_AppDefined,
1267
0
                    "CPLCreateJoinableThread() failed in ChunkAndWarpMulti()");
1268
0
                eErr = CE_Failure;
1269
0
                break;
1270
0
            }
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
0
            if (iChunk == 0)
1276
0
            {
1277
0
                CPLAcquireMutex(hCondMutex, 1.0);
1278
0
                while (asThreadData[iThread].bIOMutexTaken == FALSE)
1279
0
                    CPLCondWait(hCond, hCondMutex);
1280
0
                CPLReleaseMutex(hCondMutex);
1281
0
            }
1282
0
        }
1283
1284
        /* --------------------------------------------------------------------
1285
         */
1286
        /*      Wait for previous chunks thread to complete. */
1287
        /* --------------------------------------------------------------------
1288
         */
1289
0
        if (iChunk > 0)
1290
0
        {
1291
0
            iThread = (iChunk - 1) % 2;
1292
1293
            // Wait for thread to finish.
1294
0
            CPLJoinThread(asThreadData[iThread].hThreadHandle);
1295
0
            asThreadData[iThread].hThreadHandle = nullptr;
1296
1297
0
            CPLDebug("GDAL", "Finished chunk %d / %d.", iChunk - 1,
1298
0
                     nChunkListCount);
1299
1300
0
            eErr = asThreadData[iThread].eErr;
1301
1302
0
            if (eErr != CE_None)
1303
0
                break;
1304
0
        }
1305
0
    }
1306
1307
    /* -------------------------------------------------------------------- */
1308
    /*      Wait for all threads to complete.                               */
1309
    /* -------------------------------------------------------------------- */
1310
0
    for (int iThread = 0; iThread < 2; iThread++)
1311
0
    {
1312
0
        if (asThreadData[iThread].hThreadHandle)
1313
0
            CPLJoinThread(asThreadData[iThread].hThreadHandle);
1314
0
    }
1315
1316
0
    CPLDestroyCond(hCond);
1317
0
    CPLDestroyMutex(hCondMutex);
1318
1319
0
    WipeChunkList();
1320
1321
0
    oErrorAccumulator.ReplayErrors();
1322
1323
0
    psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
1324
1325
0
    return eErr;
1326
0
}
1327
1328
/************************************************************************/
1329
/*                         GDALChunkAndWarpMulti()                      */
1330
/************************************************************************/
1331
1332
/**
1333
 * @see GDALWarpOperation::ChunkAndWarpMulti()
1334
 */
1335
1336
CPLErr GDALChunkAndWarpMulti(GDALWarpOperationH hOperation, int nDstXOff,
1337
                             int nDstYOff, int nDstXSize, int nDstYSize)
1338
0
{
1339
0
    VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpMulti", CE_Failure);
1340
1341
0
    return reinterpret_cast<GDALWarpOperation *>(hOperation)
1342
0
        ->ChunkAndWarpMulti(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1343
0
}
1344
1345
/************************************************************************/
1346
/*                           WipeChunkList()                            */
1347
/************************************************************************/
1348
1349
void GDALWarpOperation::WipeChunkList()
1350
1351
0
{
1352
0
    CPLFree(pasChunkList);
1353
0
    pasChunkList = nullptr;
1354
0
    nChunkListCount = 0;
1355
0
    nChunkListMax = 0;
1356
0
}
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
double GDALWarpOperation::GetWorkingMemoryForWindow(int nSrcXSize,
1367
                                                    int nSrcYSize,
1368
                                                    int nDstXSize,
1369
                                                    int nDstYSize) const
1370
0
{
1371
    /* -------------------------------------------------------------------- */
1372
    /*      Based on the types of masks in use, how many bits will each     */
1373
    /*      source pixel cost us?                                           */
1374
    /* -------------------------------------------------------------------- */
1375
0
    int nSrcPixelCostInBits =
1376
0
        GDALGetDataTypeSizeBits(psOptions->eWorkingDataType) *
1377
0
        psOptions->nBandCount;
1378
1379
0
    if (psOptions->pfnSrcDensityMaskFunc != nullptr)
1380
0
        nSrcPixelCostInBits += 32;  // Float mask?
1381
1382
0
    GDALRasterBandH hSrcBand = nullptr;
1383
0
    if (psOptions->nBandCount > 0)
1384
0
        hSrcBand =
1385
0
            GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
1386
1387
0
    if (psOptions->nSrcAlphaBand > 0 || psOptions->hCutline != nullptr)
1388
0
        nSrcPixelCostInBits += 32;  // UnifiedSrcDensity float mask.
1389
0
    else if (hSrcBand != nullptr &&
1390
0
             (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET))
1391
0
        nSrcPixelCostInBits += 1;  // UnifiedSrcValid bit mask.
1392
1393
0
    if (psOptions->papfnSrcPerBandValidityMaskFunc != nullptr ||
1394
0
        psOptions->padfSrcNoDataReal != nullptr)
1395
0
        nSrcPixelCostInBits += psOptions->nBandCount;  // Bit/band mask.
1396
1397
0
    if (psOptions->pfnSrcValidityMaskFunc != nullptr)
1398
0
        nSrcPixelCostInBits += 1;  // Bit mask.
1399
1400
    /* -------------------------------------------------------------------- */
1401
    /*      What about the cost for the destination.                        */
1402
    /* -------------------------------------------------------------------- */
1403
0
    int nDstPixelCostInBits =
1404
0
        GDALGetDataTypeSizeBits(psOptions->eWorkingDataType) *
1405
0
        psOptions->nBandCount;
1406
1407
0
    if (psOptions->pfnDstDensityMaskFunc != nullptr)
1408
0
        nDstPixelCostInBits += 32;
1409
1410
0
    if (psOptions->padfDstNoDataReal != nullptr ||
1411
0
        psOptions->pfnDstValidityMaskFunc != nullptr)
1412
0
        nDstPixelCostInBits += psOptions->nBandCount;
1413
1414
0
    if (psOptions->nDstAlphaBand > 0)
1415
0
        nDstPixelCostInBits += 32;  // DstDensity float mask.
1416
1417
0
    const double dfTotalMemoryUse =
1418
0
        (static_cast<double>(nSrcPixelCostInBits) * nSrcXSize * nSrcYSize +
1419
0
         static_cast<double>(nDstPixelCostInBits) * nDstXSize * nDstYSize) /
1420
0
        8.0;
1421
0
    return dfTotalMemoryUse;
1422
0
}
1423
1424
/************************************************************************/
1425
/*                       CollectChunkListInternal()                     */
1426
/************************************************************************/
1427
1428
CPLErr GDALWarpOperation::CollectChunkListInternal(int nDstXOff, int nDstYOff,
1429
                                                   int nDstXSize, int nDstYSize)
1430
1431
0
{
1432
    /* -------------------------------------------------------------------- */
1433
    /*      Compute the bounds of the input area corresponding to the       */
1434
    /*      output area.                                                    */
1435
    /* -------------------------------------------------------------------- */
1436
0
    int nSrcXOff = 0;
1437
0
    int nSrcYOff = 0;
1438
0
    int nSrcXSize = 0;
1439
0
    int nSrcYSize = 0;
1440
0
    double dfSrcXExtraSize = 0.0;
1441
0
    double dfSrcYExtraSize = 0.0;
1442
0
    double dfSrcFillRatio = 0.0;
1443
0
    CPLErr eErr;
1444
0
    {
1445
0
        CPLTurnFailureIntoWarningBackuper oBackuper;
1446
0
        eErr = ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1447
0
                                   &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
1448
0
                                   &dfSrcXExtraSize, &dfSrcYExtraSize,
1449
0
                                   &dfSrcFillRatio);
1450
0
    }
1451
1452
0
    if (eErr != CE_None)
1453
0
    {
1454
0
        const bool bErrorOutIfEmptySourceWindow =
1455
0
            CPLFetchBool(psOptions->papszWarpOptions,
1456
0
                         "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
1457
0
        if (bErrorOutIfEmptySourceWindow)
1458
0
        {
1459
0
            CPLError(CE_Warning, CPLE_AppDefined,
1460
0
                     "Unable to compute source region for "
1461
0
                     "output window %d,%d,%d,%d, skipping.",
1462
0
                     nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1463
0
        }
1464
0
        else
1465
0
        {
1466
0
            CPLDebug("WARP",
1467
0
                     "Unable to compute source region for "
1468
0
                     "output window %d,%d,%d,%d, skipping.",
1469
0
                     nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1470
0
        }
1471
0
    }
1472
1473
    /* -------------------------------------------------------------------- */
1474
    /*      If we are allowed to drop no-source regions, do so now if       */
1475
    /*      appropriate.                                                    */
1476
    /* -------------------------------------------------------------------- */
1477
0
    if ((nSrcXSize == 0 || nSrcYSize == 0) &&
1478
0
        CPLFetchBool(psOptions->papszWarpOptions, "SKIP_NOSOURCE", false))
1479
0
        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
0
    const double dfTotalMemoryUse =
1487
0
        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
0
    if ((dfTotalMemoryUse > psOptions->dfWarpMemoryLimit &&
1504
0
         (nDstXSize > 2 || nDstYSize > 2)) ||
1505
0
        (dfSrcFillRatio > 0 && dfSrcFillRatio < 0.5 &&
1506
0
         (nDstXSize > 100 || nDstYSize > 100) &&
1507
0
         CPLFetchBool(psOptions->papszWarpOptions, "SRC_FILL_RATIO_HEURISTICS",
1508
0
                      true)))
1509
0
    {
1510
0
        int nBlockXSize = 1;
1511
0
        int nBlockYSize = 1;
1512
0
        if (psOptions->hDstDS)
1513
0
        {
1514
0
            GDALGetBlockSize(GDALGetRasterBand(psOptions->hDstDS, 1),
1515
0
                             &nBlockXSize, &nBlockYSize);
1516
0
        }
1517
1518
0
        int bStreamableOutput = CPLFetchBool(psOptions->papszWarpOptions,
1519
0
                                             "STREAMABLE_OUTPUT", false);
1520
0
        const char *pszOptimizeSize =
1521
0
            CSLFetchNameValue(psOptions->papszWarpOptions, "OPTIMIZE_SIZE");
1522
0
        const bool bOptimizeSizeAuto =
1523
0
            !pszOptimizeSize || EQUAL(pszOptimizeSize, "AUTO");
1524
0
        const bool bOptimizeSize =
1525
0
            !bStreamableOutput &&
1526
0
            ((pszOptimizeSize && !bOptimizeSizeAuto &&
1527
0
              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
0
             (bOptimizeSizeAuto && nSrcXSize > 0 && nDstYSize > 0 &&
1533
0
              (nDstXSize > nDstYSize ? fabs(double(nDstXSize) / nDstYSize -
1534
0
                                            double(nSrcXSize) / nSrcYSize) <
1535
0
                                           5 * double(nDstXSize) / nDstYSize
1536
0
                                     : fabs(double(nDstYSize) / nDstXSize -
1537
0
                                            double(nSrcYSize) / nSrcXSize) <
1538
0
                                           5 * double(nDstYSize) / nDstXSize) &&
1539
0
              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
0
        bool bHasDivided = false;
1546
0
        CPLErr eErr2 = CE_None;
1547
0
        if (nDstXSize > nDstYSize &&
1548
0
            ((!bOptimizeSize && !bStreamableOutput) ||
1549
0
             (bOptimizeSize &&
1550
0
              (nDstXSize / 2 >= nBlockXSize || nDstYSize == 1)) ||
1551
0
             (bStreamableOutput && nDstXSize / 2 >= nBlockXSize &&
1552
0
              nDstYSize == nBlockYSize)))
1553
0
        {
1554
0
            bHasDivided = true;
1555
0
            int nChunk1 = nDstXSize / 2;
1556
1557
            // In the optimize size case, try to stick on target block
1558
            // boundaries.
1559
0
            if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockXSize)
1560
0
                nChunk1 = (nChunk1 / nBlockXSize) * nBlockXSize;
1561
1562
0
            int nChunk2 = nDstXSize - nChunk1;
1563
1564
0
            eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nChunk1,
1565
0
                                            nDstYSize);
1566
1567
0
            eErr2 = CollectChunkListInternal(nDstXOff + nChunk1, nDstYOff,
1568
0
                                             nChunk2, nDstYSize);
1569
0
        }
1570
0
        else if (!(bStreamableOutput && nDstYSize / 2 < nBlockYSize))
1571
0
        {
1572
0
            bHasDivided = true;
1573
0
            int nChunk1 = nDstYSize / 2;
1574
1575
            // In the optimize size case, try to stick on target block
1576
            // boundaries.
1577
0
            if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockYSize)
1578
0
                nChunk1 = (nChunk1 / nBlockYSize) * nBlockYSize;
1579
1580
0
            const int nChunk2 = nDstYSize - nChunk1;
1581
1582
0
            eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize,
1583
0
                                            nChunk1);
1584
1585
0
            eErr2 = CollectChunkListInternal(nDstXOff, nDstYOff + nChunk1,
1586
0
                                             nDstXSize, nChunk2);
1587
0
        }
1588
1589
0
        if (bHasDivided)
1590
0
        {
1591
0
            if (eErr == CE_None)
1592
0
                return eErr2;
1593
0
            else
1594
0
                return eErr;
1595
0
        }
1596
0
    }
1597
1598
    /* -------------------------------------------------------------------- */
1599
    /*      OK, everything fits, so add to the chunk list.                  */
1600
    /* -------------------------------------------------------------------- */
1601
0
    if (nChunkListCount == nChunkListMax)
1602
0
    {
1603
0
        nChunkListMax = nChunkListMax * 2 + 1;
1604
0
        pasChunkList = static_cast<GDALWarpChunk *>(
1605
0
            CPLRealloc(pasChunkList, sizeof(GDALWarpChunk) * nChunkListMax));
1606
0
    }
1607
1608
0
    pasChunkList[nChunkListCount].dx = nDstXOff;
1609
0
    pasChunkList[nChunkListCount].dy = nDstYOff;
1610
0
    pasChunkList[nChunkListCount].dsx = nDstXSize;
1611
0
    pasChunkList[nChunkListCount].dsy = nDstYSize;
1612
0
    pasChunkList[nChunkListCount].sx = nSrcXOff;
1613
0
    pasChunkList[nChunkListCount].sy = nSrcYOff;
1614
0
    pasChunkList[nChunkListCount].ssx = nSrcXSize;
1615
0
    pasChunkList[nChunkListCount].ssy = nSrcYSize;
1616
0
    pasChunkList[nChunkListCount].sExtraSx = dfSrcXExtraSize;
1617
0
    pasChunkList[nChunkListCount].sExtraSy = dfSrcYExtraSize;
1618
1619
0
    nChunkListCount++;
1620
1621
0
    return CE_None;
1622
0
}
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
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
0
{
1663
0
    return WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
1664
0
                      nSrcYOff, nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
1665
0
                      dfProgressScale);
1666
0
}
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
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
0
{
1707
0
    ReportTiming(nullptr);
1708
1709
    /* -------------------------------------------------------------------- */
1710
    /*      Allocate the output buffer.                                     */
1711
    /* -------------------------------------------------------------------- */
1712
0
    int bDstBufferInitialized = FALSE;
1713
0
    void *pDstBuffer =
1714
0
        CreateDestinationBuffer(nDstXSize, nDstYSize, &bDstBufferInitialized);
1715
0
    if (pDstBuffer == nullptr)
1716
0
    {
1717
0
        return CE_Failure;
1718
0
    }
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
0
    GDALDataset *poDstDS = GDALDataset::FromHandle(psOptions->hDstDS);
1725
0
    if (!bDstBufferInitialized)
1726
0
    {
1727
0
        CPLErr eErr = CE_None;
1728
0
        if (psOptions->nBandCount == 1)
1729
0
        {
1730
            // Particular case to simplify the stack a bit.
1731
            // TODO(rouault): Need an explanation of what and why r34502 helps.
1732
0
            eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
1733
0
                       ->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
1734
0
                                  nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
1735
0
                                  psOptions->eWorkingDataType, 0, 0, nullptr);
1736
0
        }
1737
0
        else
1738
0
        {
1739
0
            eErr = poDstDS->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
1740
0
                                     nDstYSize, pDstBuffer, nDstXSize,
1741
0
                                     nDstYSize, psOptions->eWorkingDataType,
1742
0
                                     psOptions->nBandCount,
1743
0
                                     psOptions->panDstBands, 0, 0, 0, nullptr);
1744
0
        }
1745
1746
0
        if (eErr != CE_None)
1747
0
        {
1748
0
            DestroyDestinationBuffer(pDstBuffer);
1749
0
            return eErr;
1750
0
        }
1751
1752
0
        ReportTiming("Output buffer read");
1753
0
    }
1754
1755
    /* -------------------------------------------------------------------- */
1756
    /*      Perform the warp.                                               */
1757
    /* -------------------------------------------------------------------- */
1758
0
    CPLErr eErr = nSrcXSize == 0
1759
0
                      ? CE_None
1760
0
                      : WarpRegionToBuffer(
1761
0
                            nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1762
0
                            pDstBuffer, psOptions->eWorkingDataType, nSrcXOff,
1763
0
                            nSrcYOff, nSrcXSize, nSrcYSize, dfSrcXExtraSize,
1764
0
                            dfSrcYExtraSize, dfProgressBase, dfProgressScale);
1765
1766
    /* -------------------------------------------------------------------- */
1767
    /*      Write the output data back to disk if all went well.            */
1768
    /* -------------------------------------------------------------------- */
1769
0
    if (eErr == CE_None)
1770
0
    {
1771
0
        if (psOptions->nBandCount == 1)
1772
0
        {
1773
            // Particular case to simplify the stack a bit.
1774
0
            eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
1775
0
                       ->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
1776
0
                                  nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
1777
0
                                  psOptions->eWorkingDataType, 0, 0, nullptr);
1778
0
        }
1779
0
        else
1780
0
        {
1781
0
            eErr = poDstDS->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
1782
0
                                     nDstYSize, pDstBuffer, nDstXSize,
1783
0
                                     nDstYSize, psOptions->eWorkingDataType,
1784
0
                                     psOptions->nBandCount,
1785
0
                                     psOptions->panDstBands, 0, 0, 0, nullptr);
1786
0
        }
1787
1788
0
        if (eErr == CE_None &&
1789
0
            CPLFetchBool(psOptions->papszWarpOptions, "WRITE_FLUSH", false))
1790
0
        {
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
0
        }
1799
0
        ReportTiming("Output buffer write");
1800
0
    }
1801
1802
    /* -------------------------------------------------------------------- */
1803
    /*      Cleanup and return.                                             */
1804
    /* -------------------------------------------------------------------- */
1805
0
    DestroyDestinationBuffer(pDstBuffer);
1806
1807
0
    return eErr;
1808
0
}
1809
1810
/************************************************************************/
1811
/*                             GDALWarpRegion()                         */
1812
/************************************************************************/
1813
1814
/**
1815
 * @see GDALWarpOperation::WarpRegion()
1816
 */
1817
1818
CPLErr GDALWarpRegion(GDALWarpOperationH hOperation, int nDstXOff, int nDstYOff,
1819
                      int nDstXSize, int nDstYSize, int nSrcXOff, int nSrcYOff,
1820
                      int nSrcXSize, int nSrcYSize)
1821
1822
0
{
1823
0
    VALIDATE_POINTER1(hOperation, "GDALWarpRegion", CE_Failure);
1824
1825
0
    return reinterpret_cast<GDALWarpOperation *>(hOperation)
1826
0
        ->WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
1827
0
                     nSrcYOff, nSrcXSize, nSrcYSize);
1828
0
}
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
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
0
{
1867
0
    return WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1868
0
                              pDataBuf, eBufDataType, nSrcXOff, nSrcYOff,
1869
0
                              nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
1870
0
                              dfProgressScale);
1871
0
}
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
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
0
{
1913
0
    const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
1914
1915
0
    CPLAssert(eBufDataType == psOptions->eWorkingDataType);
1916
1917
    /* -------------------------------------------------------------------- */
1918
    /*      If not given a corresponding source window compute one now.     */
1919
    /* -------------------------------------------------------------------- */
1920
0
    if (nSrcXSize == 0 && nSrcYSize == 0)
1921
0
    {
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
0
        if (hWarpMutex != nullptr && !CPLAcquireMutex(hWarpMutex, 600.0))
1926
0
        {
1927
0
            CPLError(CE_Failure, CPLE_AppDefined,
1928
0
                     "Failed to acquire WarpMutex in WarpRegion().");
1929
0
            return CE_Failure;
1930
0
        }
1931
0
        const CPLErr eErr =
1932
0
            ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1933
0
                                &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
1934
0
                                &dfSrcXExtraSize, &dfSrcYExtraSize, nullptr);
1935
0
        if (hWarpMutex != nullptr)
1936
0
            CPLReleaseMutex(hWarpMutex);
1937
0
        if (eErr != CE_None)
1938
0
        {
1939
0
            const bool bErrorOutIfEmptySourceWindow =
1940
0
                CPLFetchBool(psOptions->papszWarpOptions,
1941
0
                             "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
1942
0
            if (!bErrorOutIfEmptySourceWindow)
1943
0
                return CE_None;
1944
0
            return eErr;
1945
0
        }
1946
0
    }
1947
1948
    /* -------------------------------------------------------------------- */
1949
    /*      Prepare a WarpKernel object to match this operation.            */
1950
    /* -------------------------------------------------------------------- */
1951
0
    GDALWarpKernel oWK;
1952
1953
0
    oWK.eResample = m_bIsTranslationOnPixelBoundaries ? GRA_NearestNeighbour
1954
0
                                                      : psOptions->eResampleAlg;
1955
0
    oWK.eTieStrategy = psOptions->eTieStrategy;
1956
0
    oWK.nBands = psOptions->nBandCount;
1957
0
    oWK.eWorkingDataType = psOptions->eWorkingDataType;
1958
1959
0
    oWK.pfnTransformer = psOptions->pfnTransformer;
1960
0
    oWK.pTransformerArg = psOptions->pTransformerArg;
1961
1962
0
    oWK.pfnProgress = psOptions->pfnProgress;
1963
0
    oWK.pProgress = psOptions->pProgressArg;
1964
0
    oWK.dfProgressBase = dfProgressBase;
1965
0
    oWK.dfProgressScale = dfProgressScale;
1966
1967
0
    oWK.papszWarpOptions = psOptions->papszWarpOptions;
1968
0
    oWK.psThreadData = psThreadData;
1969
1970
0
    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
0
    oWK.nSrcXOff = nSrcXOff;
1979
0
    oWK.nSrcYOff = nSrcYOff;
1980
0
    oWK.nSrcXSize = nSrcXSize;
1981
0
    oWK.nSrcYSize = nSrcYSize;
1982
0
    oWK.dfSrcXExtraSize = dfSrcXExtraSize;
1983
0
    oWK.dfSrcYExtraSize = dfSrcYExtraSize;
1984
1985
0
    GInt64 nAlloc64 =
1986
0
        nWordSize *
1987
0
        (static_cast<GInt64>(nSrcXSize) * nSrcYSize + WARP_EXTRA_ELTS) *
1988
0
        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
0
    oWK.papabySrcImage = static_cast<GByte **>(
2000
0
        CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
2001
0
    oWK.papabySrcImage[0] =
2002
0
        static_cast<GByte *>(VSI_MALLOC_VERBOSE(static_cast<size_t>(nAlloc64)));
2003
2004
0
    CPLErr eErr =
2005
0
        nSrcXSize != 0 && nSrcYSize != 0 && oWK.papabySrcImage[0] == nullptr
2006
0
            ? CE_Failure
2007
0
            : CE_None;
2008
2009
0
    for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2010
0
        oWK.papabySrcImage[i] =
2011
0
            reinterpret_cast<GByte *>(oWK.papabySrcImage[0]) +
2012
0
            nWordSize *
2013
0
                (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
2014
0
                 WARP_EXTRA_ELTS) *
2015
0
                i;
2016
2017
0
    if (eErr == CE_None && nSrcXSize > 0 && nSrcYSize > 0)
2018
0
    {
2019
0
        GDALDataset *poSrcDS = GDALDataset::FromHandle(psOptions->hSrcDS);
2020
0
        if (psOptions->nBandCount == 1)
2021
0
        {
2022
            // Particular case to simplify the stack a bit.
2023
0
            eErr = poSrcDS->GetRasterBand(psOptions->panSrcBands[0])
2024
0
                       ->RasterIO(GF_Read, nSrcXOff, nSrcYOff, nSrcXSize,
2025
0
                                  nSrcYSize, oWK.papabySrcImage[0], nSrcXSize,
2026
0
                                  nSrcYSize, psOptions->eWorkingDataType, 0, 0,
2027
0
                                  nullptr);
2028
0
        }
2029
0
        else
2030
0
        {
2031
0
            eErr = poSrcDS->RasterIO(
2032
0
                GF_Read, nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
2033
0
                oWK.papabySrcImage[0], nSrcXSize, nSrcYSize,
2034
0
                psOptions->eWorkingDataType, psOptions->nBandCount,
2035
0
                psOptions->panSrcBands, 0, 0,
2036
0
                nWordSize * (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
2037
0
                             WARP_EXTRA_ELTS),
2038
0
                nullptr);
2039
0
        }
2040
0
    }
2041
2042
0
    ReportTiming("Input buffer read");
2043
2044
    /* -------------------------------------------------------------------- */
2045
    /*      Initialize destination buffer.                                  */
2046
    /* -------------------------------------------------------------------- */
2047
0
    oWK.nDstXOff = nDstXOff;
2048
0
    oWK.nDstYOff = nDstYOff;
2049
0
    oWK.nDstXSize = nDstXSize;
2050
0
    oWK.nDstYSize = nDstYSize;
2051
2052
0
    oWK.papabyDstImage = reinterpret_cast<GByte **>(
2053
0
        CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
2054
2055
0
    for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2056
0
    {
2057
0
        oWK.papabyDstImage[i] =
2058
0
            static_cast<GByte *>(pDataBuf) +
2059
0
            i * static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize * nWordSize;
2060
0
    }
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
0
    if (eErr == CE_None && psOptions->nSrcAlphaBand > 0 && nSrcXSize > 0 &&
2073
0
        nSrcYSize > 0)
2074
0
    {
2075
0
        CPLAssert(oWK.pafUnifiedSrcDensity == nullptr);
2076
2077
0
        eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
2078
2079
0
        if (eErr == CE_None)
2080
0
        {
2081
0
            int bOutAllOpaque = FALSE;
2082
0
            eErr = GDALWarpSrcAlphaMasker(
2083
0
                psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2084
0
                oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2085
0
                oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
2086
0
                &bOutAllOpaque);
2087
0
            if (bOutAllOpaque)
2088
0
            {
2089
#if DEBUG_VERBOSE
2090
                CPLDebug("WARP",
2091
                         "No need for a source density mask as all values "
2092
                         "are opaque");
2093
#endif
2094
0
                CPLFree(oWK.pafUnifiedSrcDensity);
2095
0
                oWK.pafUnifiedSrcDensity = nullptr;
2096
0
            }
2097
0
        }
2098
0
    }
2099
2100
    /* -------------------------------------------------------------------- */
2101
    /*      Generate a source density mask if we have a source cutline.     */
2102
    /* -------------------------------------------------------------------- */
2103
0
    if (eErr == CE_None && psOptions->hCutline != nullptr && nSrcXSize > 0 &&
2104
0
        nSrcYSize > 0)
2105
0
    {
2106
0
        const bool bUnifiedSrcDensityJustCreated =
2107
0
            (oWK.pafUnifiedSrcDensity == nullptr);
2108
0
        if (bUnifiedSrcDensityJustCreated)
2109
0
        {
2110
0
            eErr =
2111
0
                CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
2112
2113
0
            if (eErr == CE_None)
2114
0
            {
2115
0
                for (GPtrDiff_t j = 0;
2116
0
                     j < static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
2117
0
                     j++)
2118
0
                    oWK.pafUnifiedSrcDensity[j] = 1.0;
2119
0
            }
2120
0
        }
2121
2122
0
        int nValidityFlag = 0;
2123
0
        if (eErr == CE_None)
2124
0
            eErr = GDALWarpCutlineMaskerEx(
2125
0
                psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2126
0
                oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2127
0
                oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
2128
0
                &nValidityFlag);
2129
0
        if (nValidityFlag == GCMVF_CHUNK_FULLY_WITHIN_CUTLINE &&
2130
0
            bUnifiedSrcDensityJustCreated)
2131
0
        {
2132
0
            VSIFree(oWK.pafUnifiedSrcDensity);
2133
0
            oWK.pafUnifiedSrcDensity = nullptr;
2134
0
        }
2135
0
    }
2136
2137
    /* -------------------------------------------------------------------- */
2138
    /*      Generate a destination density mask if we have a destination    */
2139
    /*      alpha band.                                                     */
2140
    /* -------------------------------------------------------------------- */
2141
0
    if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
2142
0
    {
2143
0
        CPLAssert(oWK.pafDstDensity == nullptr);
2144
2145
0
        eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstDensity");
2146
2147
0
        if (eErr == CE_None)
2148
0
            eErr = GDALWarpDstAlphaMasker(
2149
0
                psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2150
0
                oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2151
0
                oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
2152
0
    }
2153
2154
    /* -------------------------------------------------------------------- */
2155
    /*      If we have source nodata values create the validity mask.       */
2156
    /* -------------------------------------------------------------------- */
2157
0
    if (eErr == CE_None && psOptions->padfSrcNoDataReal != nullptr &&
2158
0
        nSrcXSize > 0 && nSrcYSize > 0)
2159
0
    {
2160
0
        CPLAssert(oWK.papanBandSrcValid == nullptr);
2161
2162
0
        bool bAllBandsAllValid = true;
2163
0
        for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2164
0
        {
2165
0
            eErr = CreateKernelMask(&oWK, i, "BandSrcValid");
2166
0
            if (eErr == CE_None)
2167
0
            {
2168
0
                double adfNoData[2] = {psOptions->padfSrcNoDataReal[i],
2169
0
                                       psOptions->padfSrcNoDataImag != nullptr
2170
0
                                           ? psOptions->padfSrcNoDataImag[i]
2171
0
                                           : 0.0};
2172
2173
0
                int bAllValid = FALSE;
2174
0
                eErr = GDALWarpNoDataMasker(
2175
0
                    adfNoData, 1, psOptions->eWorkingDataType, oWK.nSrcXOff,
2176
0
                    oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2177
0
                    &(oWK.papabySrcImage[i]), FALSE, oWK.papanBandSrcValid[i],
2178
0
                    &bAllValid);
2179
0
                if (!bAllValid)
2180
0
                    bAllBandsAllValid = false;
2181
0
            }
2182
0
        }
2183
2184
        // Optimization: if all pixels in all bands are valid,
2185
        // we don't need a mask.
2186
0
        if (bAllBandsAllValid)
2187
0
        {
2188
#if DEBUG_VERBOSE
2189
            CPLDebug(
2190
                "WARP",
2191
                "No need for a source nodata mask as all values are valid");
2192
#endif
2193
0
            for (int k = 0; k < psOptions->nBandCount; k++)
2194
0
                CPLFree(oWK.papanBandSrcValid[k]);
2195
0
            CPLFree(oWK.papanBandSrcValid);
2196
0
            oWK.papanBandSrcValid = nullptr;
2197
0
        }
2198
2199
        /* --------------------------------------------------------------------
2200
         */
2201
        /*      If there's just a single band, then transfer */
2202
        /*      papanBandSrcValid[0] as panUnifiedSrcValid. */
2203
        /* --------------------------------------------------------------------
2204
         */
2205
0
        if (oWK.papanBandSrcValid != nullptr && psOptions->nBandCount == 1)
2206
0
        {
2207
0
            oWK.panUnifiedSrcValid = oWK.papanBandSrcValid[0];
2208
0
            CPLFree(oWK.papanBandSrcValid);
2209
0
            oWK.papanBandSrcValid = nullptr;
2210
0
        }
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
0
        else if (oWK.papanBandSrcValid != nullptr && eErr == CE_None)
2220
0
        {
2221
0
            bool bAtLeastOneBandAllValid = false;
2222
0
            for (int k = 0; k < psOptions->nBandCount; k++)
2223
0
            {
2224
0
                if (oWK.papanBandSrcValid[k] == nullptr)
2225
0
                {
2226
0
                    bAtLeastOneBandAllValid = true;
2227
0
                    break;
2228
0
                }
2229
0
            }
2230
2231
0
            const char *pszUnifiedSrcNoData = CSLFetchNameValue(
2232
0
                psOptions->papszWarpOptions, "UNIFIED_SRC_NODATA");
2233
0
            if (!bAtLeastOneBandAllValid && (pszUnifiedSrcNoData == nullptr ||
2234
0
                                             CPLTestBool(pszUnifiedSrcNoData)))
2235
0
            {
2236
0
                auto nMaskBits =
2237
0
                    static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
2238
2239
0
                eErr =
2240
0
                    CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
2241
2242
0
                if (eErr == CE_None)
2243
0
                {
2244
0
                    CPLMaskClearAll(oWK.panUnifiedSrcValid, nMaskBits);
2245
2246
0
                    for (int k = 0; k < psOptions->nBandCount; k++)
2247
0
                    {
2248
0
                        CPLMaskMerge(oWK.panUnifiedSrcValid,
2249
0
                                     oWK.papanBandSrcValid[k], nMaskBits);
2250
0
                    }
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
0
                    if (pszUnifiedSrcNoData != nullptr &&
2262
0
                        !EQUAL(pszUnifiedSrcNoData, "PARTIAL"))
2263
0
                    {
2264
0
                        for (int k = 0; k < psOptions->nBandCount; k++)
2265
0
                            CPLFree(oWK.papanBandSrcValid[k]);
2266
0
                        CPLFree(oWK.papanBandSrcValid);
2267
0
                        oWK.papanBandSrcValid = nullptr;
2268
0
                    }
2269
0
                }
2270
0
            }
2271
0
        }
2272
0
    }
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
0
    GDALRasterBandH hSrcBand =
2280
0
        psOptions->nBandCount < 1
2281
0
            ? nullptr
2282
0
            : GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
2283
2284
0
    if (eErr == CE_None && oWK.pafUnifiedSrcDensity == nullptr &&
2285
0
        oWK.panUnifiedSrcValid == nullptr && psOptions->nSrcAlphaBand <= 0 &&
2286
0
        (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET)
2287
        // Need to double check for -nosrcalpha case.
2288
0
        && !(GDALGetMaskFlags(hSrcBand) & GMF_ALPHA) && nSrcXSize > 0 &&
2289
0
        nSrcYSize > 0)
2290
2291
0
    {
2292
0
        eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
2293
2294
0
        if (eErr == CE_None)
2295
0
            eErr = GDALWarpSrcMaskMasker(
2296
0
                psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2297
0
                oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2298
0
                oWK.papabySrcImage, FALSE, oWK.panUnifiedSrcValid);
2299
0
    }
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
0
    if (eErr == CE_None && psOptions->padfDstNoDataReal != nullptr)
2311
0
    {
2312
0
        CPLAssert(oWK.panDstValid == nullptr);
2313
2314
0
        const GPtrDiff_t nMaskBits =
2315
0
            static_cast<GPtrDiff_t>(oWK.nDstXSize) * oWK.nDstYSize;
2316
2317
0
        eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstValid");
2318
0
        GUInt32 *panBandMask =
2319
0
            eErr == CE_None ? CPLMaskCreate(nMaskBits, true) : nullptr;
2320
2321
0
        if (eErr == CE_None && panBandMask != nullptr)
2322
0
        {
2323
0
            for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
2324
0
            {
2325
0
                CPLMaskSetAll(panBandMask, nMaskBits);
2326
2327
0
                double adfNoData[2] = {psOptions->padfDstNoDataReal[iBand],
2328
0
                                       psOptions->padfDstNoDataImag != nullptr
2329
0
                                           ? psOptions->padfDstNoDataImag[iBand]
2330
0
                                           : 0.0};
2331
2332
0
                int bAllValid = FALSE;
2333
0
                eErr = GDALWarpNoDataMasker(
2334
0
                    adfNoData, 1, psOptions->eWorkingDataType, oWK.nDstXOff,
2335
0
                    oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2336
0
                    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
0
                if (bAllValid && psOptions->nBandCount == 1)
2341
0
                {
2342
#if DEBUG_VERBOSE
2343
                    CPLDebug("WARP", "No need for a destination nodata mask as "
2344
                                     "all values are valid");
2345
#endif
2346
0
                    CPLFree(oWK.panDstValid);
2347
0
                    oWK.panDstValid = nullptr;
2348
0
                    break;
2349
0
                }
2350
2351
0
                CPLMaskMerge(oWK.panDstValid, panBandMask, nMaskBits);
2352
0
            }
2353
0
            CPLFree(panBandMask);
2354
0
        }
2355
0
    }
2356
2357
    /* -------------------------------------------------------------------- */
2358
    /*      Release IO Mutex, and acquire warper mutex.                     */
2359
    /* -------------------------------------------------------------------- */
2360
0
    if (hIOMutex != nullptr)
2361
0
    {
2362
0
        CPLReleaseMutex(hIOMutex);
2363
0
        if (!CPLAcquireMutex(hWarpMutex, 600.0))
2364
0
        {
2365
0
            CPLError(CE_Failure, CPLE_AppDefined,
2366
0
                     "Failed to acquire WarpMutex in WarpRegion().");
2367
0
            return CE_Failure;
2368
0
        }
2369
0
    }
2370
2371
    /* -------------------------------------------------------------------- */
2372
    /*      Optional application provided prewarp chunk processor.          */
2373
    /* -------------------------------------------------------------------- */
2374
0
    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
0
    if (eErr == CE_None)
2382
0
    {
2383
0
        eErr = oWK.PerformWarp();
2384
0
        ReportTiming("In memory warp operation");
2385
0
    }
2386
2387
    /* -------------------------------------------------------------------- */
2388
    /*      Optional application provided postwarp chunk processor.         */
2389
    /* -------------------------------------------------------------------- */
2390
0
    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
0
    if (hIOMutex != nullptr)
2398
0
    {
2399
0
        CPLReleaseMutex(hWarpMutex);
2400
0
        if (!CPLAcquireMutex(hIOMutex, 600.0))
2401
0
        {
2402
0
            CPLError(CE_Failure, CPLE_AppDefined,
2403
0
                     "Failed to acquire IOMutex in WarpRegion().");
2404
0
            return CE_Failure;
2405
0
        }
2406
0
    }
2407
2408
    /* -------------------------------------------------------------------- */
2409
    /*      Write destination alpha if available.                           */
2410
    /* -------------------------------------------------------------------- */
2411
0
    if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
2412
0
    {
2413
0
        eErr = GDALWarpDstAlphaMasker(
2414
0
            psOptions, -psOptions->nBandCount, psOptions->eWorkingDataType,
2415
0
            oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2416
0
            oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
2417
0
    }
2418
2419
    /* -------------------------------------------------------------------- */
2420
    /*      Cleanup.                                                        */
2421
    /* -------------------------------------------------------------------- */
2422
0
    CPLFree(oWK.papabySrcImage[0]);
2423
0
    CPLFree(oWK.papabySrcImage);
2424
0
    CPLFree(oWK.papabyDstImage);
2425
2426
0
    if (oWK.papanBandSrcValid != nullptr)
2427
0
    {
2428
0
        for (int i = 0; i < oWK.nBands; i++)
2429
0
            CPLFree(oWK.papanBandSrcValid[i]);
2430
0
        CPLFree(oWK.papanBandSrcValid);
2431
0
    }
2432
0
    CPLFree(oWK.panUnifiedSrcValid);
2433
0
    CPLFree(oWK.pafUnifiedSrcDensity);
2434
0
    CPLFree(oWK.panDstValid);
2435
0
    CPLFree(oWK.pafDstDensity);
2436
2437
0
    return eErr;
2438
0
}
2439
2440
/************************************************************************/
2441
/*                            GDALWarpRegionToBuffer()                  */
2442
/************************************************************************/
2443
2444
/**
2445
 * @see GDALWarpOperation::WarpRegionToBuffer()
2446
 */
2447
2448
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
0
{
2455
0
    VALIDATE_POINTER1(hOperation, "GDALWarpRegionToBuffer", CE_Failure);
2456
2457
0
    return reinterpret_cast<GDALWarpOperation *>(hOperation)
2458
0
        ->WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize, pDataBuf,
2459
0
                             eBufDataType, nSrcXOff, nSrcYOff, nSrcXSize,
2460
0
                             nSrcYSize);
2461
0
}
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
CPLErr GDALWarpOperation::CreateKernelMask(GDALWarpKernel *poKernel, int iBand,
2473
                                           const char *pszType)
2474
2475
0
{
2476
0
    void **ppMask = nullptr;
2477
0
    int nXSize = 0;
2478
0
    int nYSize = 0;
2479
0
    int nBitsPerPixel = 0;
2480
0
    int nDefault = 0;
2481
0
    int nExtraElts = 0;
2482
0
    bool bDoMemset = true;
2483
2484
    /* -------------------------------------------------------------------- */
2485
    /*      Get particulars of mask to be updated.                          */
2486
    /* -------------------------------------------------------------------- */
2487
0
    if (EQUAL(pszType, "BandSrcValid"))
2488
0
    {
2489
0
        if (poKernel->papanBandSrcValid == nullptr)
2490
0
            poKernel->papanBandSrcValid = static_cast<GUInt32 **>(
2491
0
                CPLCalloc(sizeof(void *), poKernel->nBands));
2492
2493
0
        ppMask =
2494
0
            reinterpret_cast<void **>(&(poKernel->papanBandSrcValid[iBand]));
2495
0
        nExtraElts = WARP_EXTRA_ELTS;
2496
0
        nXSize = poKernel->nSrcXSize;
2497
0
        nYSize = poKernel->nSrcYSize;
2498
0
        nBitsPerPixel = 1;
2499
0
        nDefault = 0xff;
2500
0
    }
2501
0
    else if (EQUAL(pszType, "UnifiedSrcValid"))
2502
0
    {
2503
0
        ppMask = reinterpret_cast<void **>(&(poKernel->panUnifiedSrcValid));
2504
0
        nExtraElts = WARP_EXTRA_ELTS;
2505
0
        nXSize = poKernel->nSrcXSize;
2506
0
        nYSize = poKernel->nSrcYSize;
2507
0
        nBitsPerPixel = 1;
2508
0
        nDefault = 0xff;
2509
0
    }
2510
0
    else if (EQUAL(pszType, "UnifiedSrcDensity"))
2511
0
    {
2512
0
        ppMask = reinterpret_cast<void **>(&(poKernel->pafUnifiedSrcDensity));
2513
0
        nExtraElts = WARP_EXTRA_ELTS;
2514
0
        nXSize = poKernel->nSrcXSize;
2515
0
        nYSize = poKernel->nSrcYSize;
2516
0
        nBitsPerPixel = 32;
2517
0
        nDefault = 0;
2518
0
        bDoMemset = false;
2519
0
    }
2520
0
    else if (EQUAL(pszType, "DstValid"))
2521
0
    {
2522
0
        ppMask = reinterpret_cast<void **>(&(poKernel->panDstValid));
2523
0
        nXSize = poKernel->nDstXSize;
2524
0
        nYSize = poKernel->nDstYSize;
2525
0
        nBitsPerPixel = 1;
2526
0
        nDefault = 0;
2527
0
    }
2528
0
    else if (EQUAL(pszType, "DstDensity"))
2529
0
    {
2530
0
        ppMask = reinterpret_cast<void **>(&(poKernel->pafDstDensity));
2531
0
        nXSize = poKernel->nDstXSize;
2532
0
        nYSize = poKernel->nDstYSize;
2533
0
        nBitsPerPixel = 32;
2534
0
        nDefault = 0;
2535
0
        bDoMemset = false;
2536
0
    }
2537
0
    else
2538
0
    {
2539
0
        CPLError(CE_Failure, CPLE_AppDefined,
2540
0
                 "Internal error in CreateKernelMask(%s).", pszType);
2541
0
        return CE_Failure;
2542
0
    }
2543
2544
    /* -------------------------------------------------------------------- */
2545
    /*      Allocate if needed.                                             */
2546
    /* -------------------------------------------------------------------- */
2547
0
    if (*ppMask == nullptr)
2548
0
    {
2549
0
        const GIntBig nBytes =
2550
0
            nBitsPerPixel == 32
2551
0
                ? (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts) * 4
2552
0
                : (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts + 31) / 8;
2553
2554
0
        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
0
        *ppMask = VSI_MALLOC_VERBOSE(nByteSize_t);
2565
2566
0
        if (*ppMask == nullptr)
2567
0
        {
2568
0
            return CE_Failure;
2569
0
        }
2570
2571
0
        if (bDoMemset)
2572
0
            memset(*ppMask, nDefault, nByteSize_t);
2573
0
    }
2574
2575
0
    return CE_None;
2576
0
}
2577
2578
/************************************************************************/
2579
/*               ComputeSourceWindowStartingFromSource()                */
2580
/************************************************************************/
2581
2582
constexpr int DEFAULT_STEP_COUNT = 21;
2583
2584
void GDALWarpOperation::ComputeSourceWindowStartingFromSource(
2585
    int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize,
2586
    double *padfSrcMinX, double *padfSrcMinY, double *padfSrcMaxX,
2587
    double *padfSrcMaxY)
2588
0
{
2589
0
    const int nSrcRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
2590
0
    const int nSrcRasterYSize = GDALGetRasterYSize(psOptions->hSrcDS);
2591
0
    if (nSrcRasterXSize == 0 || nSrcRasterYSize == 0)
2592
0
        return;
2593
2594
0
    GDALWarpPrivateData *privateData = GetWarpPrivateData(this);
2595
0
    if (privateData->nStepCount == 0)
2596
0
    {
2597
0
        int nStepCount = DEFAULT_STEP_COUNT;
2598
0
        std::vector<double> adfDstZ{};
2599
2600
0
        const char *pszSampleSteps =
2601
0
            CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
2602
0
        constexpr int knIntMax = std::numeric_limits<int>::max();
2603
0
        if (pszSampleSteps && !EQUAL(pszSampleSteps, "ALL"))
2604
0
        {
2605
0
            nStepCount = atoi(
2606
0
                CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS"));
2607
0
            nStepCount = std::max(2, nStepCount);
2608
0
        }
2609
2610
0
        const double dfStepSize = 1.0 / (nStepCount - 1);
2611
0
        if (nStepCount > knIntMax - 2 ||
2612
0
            (nStepCount + 2) > knIntMax / (nStepCount + 2))
2613
0
        {
2614
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
2615
0
                     nStepCount);
2616
0
            return;
2617
0
        }
2618
0
        const int nSampleMax = (nStepCount + 2) * (nStepCount + 2);
2619
2620
0
        try
2621
0
        {
2622
0
            privateData->abSuccess.resize(nSampleMax);
2623
0
            privateData->adfDstX.resize(nSampleMax);
2624
0
            privateData->adfDstY.resize(nSampleMax);
2625
0
            adfDstZ.resize(nSampleMax);
2626
0
        }
2627
0
        catch (const std::exception &)
2628
0
        {
2629
0
            return;
2630
0
        }
2631
2632
        /* --------------------------------------------------------------------
2633
         */
2634
        /*      Setup sample points on a grid pattern throughout the source */
2635
        /*      raster. */
2636
        /* --------------------------------------------------------------------
2637
         */
2638
0
        int iPoint = 0;
2639
0
        for (int iY = 0; iY < nStepCount + 2; iY++)
2640
0
        {
2641
0
            const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
2642
0
                                    : (iY <= nStepCount)
2643
0
                                        ? (iY - 1) * dfStepSize
2644
0
                                        : 1 - 0.5 / nSrcRasterYSize;
2645
0
            for (int iX = 0; iX < nStepCount + 2; iX++)
2646
0
            {
2647
0
                const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
2648
0
                                        : (iX <= nStepCount)
2649
0
                                            ? (iX - 1) * dfStepSize
2650
0
                                            : 1 - 0.5 / nSrcRasterXSize;
2651
0
                privateData->adfDstX[iPoint] = dfRatioX * nSrcRasterXSize;
2652
0
                privateData->adfDstY[iPoint] = dfRatioY * nSrcRasterYSize;
2653
0
                iPoint++;
2654
0
            }
2655
0
        }
2656
0
        CPLAssert(iPoint == nSampleMax);
2657
2658
        /* --------------------------------------------------------------------
2659
         */
2660
        /*      Transform them to the output pixel coordinate space */
2661
        /* --------------------------------------------------------------------
2662
         */
2663
0
        psOptions->pfnTransformer(psOptions->pTransformerArg, FALSE, nSampleMax,
2664
0
                                  privateData->adfDstX.data(),
2665
0
                                  privateData->adfDstY.data(), adfDstZ.data(),
2666
0
                                  privateData->abSuccess.data());
2667
0
        privateData->nStepCount = nStepCount;
2668
0
    }
2669
2670
    /* -------------------------------------------------------------------- */
2671
    /*      Collect the bounds, ignoring any failed points.                 */
2672
    /* -------------------------------------------------------------------- */
2673
0
    const int nStepCount = privateData->nStepCount;
2674
0
    const double dfStepSize = 1.0 / (nStepCount - 1);
2675
0
    int iPoint = 0;
2676
0
#ifdef DEBUG
2677
0
    const size_t nSampleMax =
2678
0
        static_cast<size_t>(nStepCount + 2) * (nStepCount + 2);
2679
0
    CPL_IGNORE_RET_VAL(nSampleMax);
2680
0
    CPLAssert(privateData->adfDstX.size() == nSampleMax);
2681
0
    CPLAssert(privateData->adfDstY.size() == nSampleMax);
2682
0
    CPLAssert(privateData->abSuccess.size() == nSampleMax);
2683
0
#endif
2684
0
    for (int iY = 0; iY < nStepCount + 2; iY++)
2685
0
    {
2686
0
        const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
2687
0
                                : (iY <= nStepCount)
2688
0
                                    ? (iY - 1) * dfStepSize
2689
0
                                    : 1 - 0.5 / nSrcRasterYSize;
2690
0
        for (int iX = 0; iX < nStepCount + 2; iX++)
2691
0
        {
2692
0
            if (privateData->abSuccess[iPoint] &&
2693
0
                privateData->adfDstX[iPoint] >= nDstXOff &&
2694
0
                privateData->adfDstX[iPoint] <= nDstXOff + nDstXSize &&
2695
0
                privateData->adfDstY[iPoint] >= nDstYOff &&
2696
0
                privateData->adfDstY[iPoint] <= nDstYOff + nDstYSize)
2697
0
            {
2698
0
                const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
2699
0
                                        : (iX <= nStepCount)
2700
0
                                            ? (iX - 1) * dfStepSize
2701
0
                                            : 1 - 0.5 / nSrcRasterXSize;
2702
0
                double dfSrcX = dfRatioX * nSrcRasterXSize;
2703
0
                double dfSrcY = dfRatioY * nSrcRasterYSize;
2704
0
                *padfSrcMinX = std::min(*padfSrcMinX, dfSrcX);
2705
0
                *padfSrcMinY = std::min(*padfSrcMinY, dfSrcY);
2706
0
                *padfSrcMaxX = std::max(*padfSrcMaxX, dfSrcX);
2707
0
                *padfSrcMaxY = std::max(*padfSrcMaxY, dfSrcY);
2708
0
            }
2709
0
            iPoint++;
2710
0
        }
2711
0
    }
2712
0
}
2713
2714
/************************************************************************/
2715
/*                    ComputeSourceWindowTransformPoints()              */
2716
/************************************************************************/
2717
2718
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
0
{
2724
0
    nSamplePoints = 0;
2725
0
    nFailedCount = 0;
2726
2727
0
    const double dfStepSize = bAll ? 0 : 1.0 / (nStepCount - 1);
2728
0
    constexpr int knIntMax = std::numeric_limits<int>::max();
2729
0
    int nSampleMax = 0;
2730
0
    if (bUseGrid)
2731
0
    {
2732
0
        if (bAll)
2733
0
        {
2734
0
            if (nDstYSize > knIntMax / (nDstXSize + 1) - 1)
2735
0
            {
2736
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
2737
0
                return false;
2738
0
            }
2739
0
            nSampleMax = (nDstXSize + 1) * (nDstYSize + 1);
2740
0
        }
2741
0
        else
2742
0
        {
2743
0
            if (nStepCount > knIntMax - 2 ||
2744
0
                (nStepCount + 2) > knIntMax / (nStepCount + 2))
2745
0
            {
2746
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
2747
0
                         nStepCount);
2748
0
                return false;
2749
0
            }
2750
0
            nSampleMax = (nStepCount + 2) * (nStepCount + 2);
2751
0
        }
2752
0
    }
2753
0
    else
2754
0
    {
2755
0
        if (bAll)
2756
0
        {
2757
0
            if (nDstXSize > (knIntMax - 2 * nDstYSize) / 2)
2758
0
            {
2759
                // Extremely unlikely !
2760
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
2761
0
                return false;
2762
0
            }
2763
0
            nSampleMax = 2 * (nDstXSize + nDstYSize);
2764
0
        }
2765
0
        else
2766
0
        {
2767
0
            if (nStepCount > knIntMax / 4)
2768
0
            {
2769
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d * 4",
2770
0
                         nStepCount);
2771
0
                return false;
2772
0
            }
2773
0
            nSampleMax = nStepCount * 4;
2774
0
        }
2775
0
    }
2776
2777
0
    int *pabSuccess =
2778
0
        static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nSampleMax));
2779
0
    double *padfX = static_cast<double *>(
2780
0
        VSI_MALLOC2_VERBOSE(sizeof(double) * 3, nSampleMax));
2781
0
    if (pabSuccess == nullptr || padfX == nullptr)
2782
0
    {
2783
0
        CPLFree(padfX);
2784
0
        CPLFree(pabSuccess);
2785
0
        return false;
2786
0
    }
2787
0
    double *padfY = padfX + nSampleMax;
2788
0
    double *padfZ = padfX + nSampleMax * 2;
2789
2790
    /* -------------------------------------------------------------------- */
2791
    /*      Setup sample points on a grid pattern throughout the area.      */
2792
    /* -------------------------------------------------------------------- */
2793
0
    if (bUseGrid)
2794
0
    {
2795
0
        if (bAll)
2796
0
        {
2797
0
            for (int iY = 0; iY <= nDstYSize; ++iY)
2798
0
            {
2799
0
                for (int iX = 0; iX <= nDstXSize; ++iX)
2800
0
                {
2801
0
                    padfX[nSamplePoints] = nDstXOff + iX;
2802
0
                    padfY[nSamplePoints] = nDstYOff + iY;
2803
0
                    padfZ[nSamplePoints++] = 0.0;
2804
0
                }
2805
0
            }
2806
0
        }
2807
0
        else
2808
0
        {
2809
0
            for (int iY = 0; iY < nStepCount + 2; iY++)
2810
0
            {
2811
0
                const double dfRatioY = (iY == 0) ? 0.5 / nDstXSize
2812
0
                                        : (iY <= nStepCount)
2813
0
                                            ? (iY - 1) * dfStepSize
2814
0
                                            : 1 - 0.5 / nDstXSize;
2815
0
                for (int iX = 0; iX < nStepCount + 2; iX++)
2816
0
                {
2817
0
                    const double dfRatioX = (iX == 0) ? 0.5 / nDstXSize
2818
0
                                            : (iX <= nStepCount)
2819
0
                                                ? (iX - 1) * dfStepSize
2820
0
                                                : 1 - 0.5 / nDstXSize;
2821
0
                    padfX[nSamplePoints] = dfRatioX * nDstXSize + nDstXOff;
2822
0
                    padfY[nSamplePoints] = dfRatioY * nDstYSize + nDstYOff;
2823
0
                    padfZ[nSamplePoints++] = 0.0;
2824
0
                }
2825
0
            }
2826
0
        }
2827
0
    }
2828
    /* -------------------------------------------------------------------- */
2829
    /*      Setup sample points all around the edge of the output raster.   */
2830
    /* -------------------------------------------------------------------- */
2831
0
    else
2832
0
    {
2833
0
        if (bAll)
2834
0
        {
2835
0
            for (int iX = 0; iX <= nDstXSize; ++iX)
2836
0
            {
2837
                // Along top
2838
0
                padfX[nSamplePoints] = nDstXOff + iX;
2839
0
                padfY[nSamplePoints] = nDstYOff;
2840
0
                padfZ[nSamplePoints++] = 0.0;
2841
2842
                // Along bottom
2843
0
                padfX[nSamplePoints] = nDstXOff + iX;
2844
0
                padfY[nSamplePoints] = nDstYOff + nDstYSize;
2845
0
                padfZ[nSamplePoints++] = 0.0;
2846
0
            }
2847
2848
0
            for (int iY = 1; iY < nDstYSize; ++iY)
2849
0
            {
2850
                // Along left
2851
0
                padfX[nSamplePoints] = nDstXOff;
2852
0
                padfY[nSamplePoints] = nDstYOff + iY;
2853
0
                padfZ[nSamplePoints++] = 0.0;
2854
2855
                // Along right
2856
0
                padfX[nSamplePoints] = nDstXOff + nDstXSize;
2857
0
                padfY[nSamplePoints] = nDstYOff + iY;
2858
0
                padfZ[nSamplePoints++] = 0.0;
2859
0
            }
2860
0
        }
2861
0
        else
2862
0
        {
2863
0
            for (double dfRatio = 0.0; dfRatio <= 1.0 + dfStepSize * 0.5;
2864
0
                 dfRatio += dfStepSize)
2865
0
            {
2866
                // Along top
2867
0
                padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
2868
0
                padfY[nSamplePoints] = nDstYOff;
2869
0
                padfZ[nSamplePoints++] = 0.0;
2870
2871
                // Along bottom
2872
0
                padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
2873
0
                padfY[nSamplePoints] = nDstYOff + nDstYSize;
2874
0
                padfZ[nSamplePoints++] = 0.0;
2875
2876
                // Along left
2877
0
                padfX[nSamplePoints] = nDstXOff;
2878
0
                padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
2879
0
                padfZ[nSamplePoints++] = 0.0;
2880
2881
                // Along right
2882
0
                padfX[nSamplePoints] = nDstXSize + nDstXOff;
2883
0
                padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
2884
0
                padfZ[nSamplePoints++] = 0.0;
2885
0
            }
2886
0
        }
2887
0
    }
2888
2889
0
    CPLAssert(nSamplePoints == nSampleMax);
2890
2891
    /* -------------------------------------------------------------------- */
2892
    /*      Transform them to the input pixel coordinate space              */
2893
    /* -------------------------------------------------------------------- */
2894
2895
0
    const auto RefreshTransformer = [this]()
2896
0
    {
2897
0
        if (GDALIsTransformer(psOptions->pTransformerArg,
2898
0
                              GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
2899
0
        {
2900
0
            GDALRefreshGenImgProjTransformer(psOptions->pTransformerArg);
2901
0
        }
2902
0
        else if (GDALIsTransformer(psOptions->pTransformerArg,
2903
0
                                   GDAL_APPROX_TRANSFORMER_CLASS_NAME))
2904
0
        {
2905
0
            GDALRefreshApproxTransformer(psOptions->pTransformerArg);
2906
0
        }
2907
0
    };
2908
2909
0
    if (bTryWithCheckWithInvertProj)
2910
0
    {
2911
0
        CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", "YES");
2912
0
        RefreshTransformer();
2913
0
    }
2914
0
    psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE, nSamplePoints,
2915
0
                              padfX, padfY, padfZ, pabSuccess);
2916
0
    if (bTryWithCheckWithInvertProj)
2917
0
    {
2918
0
        CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", nullptr);
2919
0
        RefreshTransformer();
2920
0
    }
2921
2922
    /* -------------------------------------------------------------------- */
2923
    /*      Collect the bounds, ignoring any failed points.                 */
2924
    /* -------------------------------------------------------------------- */
2925
0
    for (int i = 0; i < nSamplePoints; i++)
2926
0
    {
2927
0
        if (!pabSuccess[i])
2928
0
        {
2929
0
            nFailedCount++;
2930
0
            continue;
2931
0
        }
2932
2933
        // If this happens this is likely the symptom of a bug somewhere.
2934
0
        if (std::isnan(padfX[i]) || std::isnan(padfY[i]))
2935
0
        {
2936
0
            static bool bNanCoordFound = false;
2937
0
            if (!bNanCoordFound)
2938
0
            {
2939
0
                CPLDebug("WARP",
2940
0
                         "ComputeSourceWindow(): "
2941
0
                         "NaN coordinate found on point %d.",
2942
0
                         i);
2943
0
                bNanCoordFound = true;
2944
0
            }
2945
0
            nFailedCount++;
2946
0
            continue;
2947
0
        }
2948
2949
0
        dfMinXOut = std::min(dfMinXOut, padfX[i]);
2950
0
        dfMinYOut = std::min(dfMinYOut, padfY[i]);
2951
0
        dfMaxXOut = std::max(dfMaxXOut, padfX[i]);
2952
0
        dfMaxYOut = std::max(dfMaxYOut, padfY[i]);
2953
0
    }
2954
2955
0
    CPLFree(padfX);
2956
0
    CPLFree(pabSuccess);
2957
0
    return true;
2958
0
}
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
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
0
{
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
0
    int nStepCount = DEFAULT_STEP_COUNT;
2992
0
    bool bAll = false;
2993
2994
0
    bool bUseGrid =
2995
0
        CPLFetchBool(psOptions->papszWarpOptions, "SAMPLE_GRID", false);
2996
2997
0
    const char *pszSampleSteps =
2998
0
        CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
2999
0
    if (pszSampleSteps)
3000
0
    {
3001
0
        if (EQUAL(pszSampleSteps, "ALL"))
3002
0
        {
3003
0
            bAll = true;
3004
0
        }
3005
0
        else
3006
0
        {
3007
0
            nStepCount = atoi(pszSampleSteps);
3008
0
            nStepCount = std::max(2, nStepCount);
3009
0
        }
3010
0
    }
3011
0
    else if (!bUseGrid)
3012
0
    {
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
0
        double adfCornerX[4];
3018
0
        double adfCornerY[4];
3019
0
        double adfCornerZ[4] = {0, 0, 0, 0};
3020
0
        int anCornerSuccess[4] = {FALSE, FALSE, FALSE, FALSE};
3021
0
        adfCornerX[0] = nDstXOff;
3022
0
        adfCornerY[0] = nDstYOff;
3023
0
        adfCornerX[1] = nDstXOff + nDstXSize;
3024
0
        adfCornerY[1] = nDstYOff;
3025
0
        adfCornerX[2] = nDstXOff;
3026
0
        adfCornerY[2] = nDstYOff + nDstYSize;
3027
0
        adfCornerX[3] = nDstXOff + nDstXSize;
3028
0
        adfCornerY[3] = nDstYOff + nDstYSize;
3029
0
        if (!psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE, 4,
3030
0
                                       adfCornerX, adfCornerY, adfCornerZ,
3031
0
                                       anCornerSuccess) ||
3032
0
            !anCornerSuccess[0] || !anCornerSuccess[1] || !anCornerSuccess[2] ||
3033
0
            !anCornerSuccess[3])
3034
0
        {
3035
0
            bAll = true;
3036
0
        }
3037
0
    }
3038
3039
0
    bool bTryWithCheckWithInvertProj = false;
3040
0
    double dfMinXOut = std::numeric_limits<double>::infinity();
3041
0
    double dfMinYOut = std::numeric_limits<double>::infinity();
3042
0
    double dfMaxXOut = -std::numeric_limits<double>::infinity();
3043
0
    double dfMaxYOut = -std::numeric_limits<double>::infinity();
3044
3045
0
    int nSamplePoints = 0;
3046
0
    int nFailedCount = 0;
3047
0
    if (!ComputeSourceWindowTransformPoints(
3048
0
            nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3049
0
            nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3050
0
            dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3051
0
    {
3052
0
        return CE_Failure;
3053
0
    }
3054
3055
    // Use grid sampling as soon as a special point falls into the extent of
3056
    // the target raster.
3057
0
    if (!bUseGrid && psOptions->hDstDS)
3058
0
    {
3059
0
        for (const auto &xy : aDstXYSpecialPoints)
3060
0
        {
3061
0
            if (0 <= xy.first &&
3062
0
                GDALGetRasterXSize(psOptions->hDstDS) >= xy.first &&
3063
0
                0 <= xy.second &&
3064
0
                GDALGetRasterYSize(psOptions->hDstDS) >= xy.second)
3065
0
            {
3066
0
                bUseGrid = true;
3067
0
                bAll = false;
3068
0
                if (!ComputeSourceWindowTransformPoints(
3069
0
                        nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid,
3070
0
                        bAll, nStepCount, bTryWithCheckWithInvertProj,
3071
0
                        dfMinXOut, dfMinYOut, dfMaxXOut, dfMaxYOut,
3072
0
                        nSamplePoints, nFailedCount))
3073
0
                {
3074
0
                    return CE_Failure;
3075
0
                }
3076
0
                break;
3077
0
            }
3078
0
        }
3079
0
    }
3080
3081
0
    const int nRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
3082
0
    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
0
    if (nFailedCount == 0 && !bTryWithCheckWithInvertProj &&
3091
0
        (dfMinXOut < -1e6 || dfMinYOut < -1e6 ||
3092
0
         dfMaxXOut > nRasterXSize + 1e6 || dfMaxYOut > nRasterYSize + 1e6) &&
3093
0
        !CPLTestBool(CPLGetConfigOption("CHECK_WITH_INVERT_PROJ", "NO")))
3094
0
    {
3095
0
        CPLDebug("WARP",
3096
0
                 "ComputeSourceWindow(): bogus source dataset window "
3097
0
                 "returned. Trying again with CHECK_WITH_INVERT_PROJ=YES");
3098
0
        bTryWithCheckWithInvertProj = true;
3099
3100
        // We should probably perform the coordinate transformation in the
3101
        // warp kernel under CHECK_WITH_INVERT_PROJ too...
3102
0
        if (!ComputeSourceWindowTransformPoints(
3103
0
                nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3104
0
                nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3105
0
                dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3106
0
        {
3107
0
            return CE_Failure;
3108
0
        }
3109
0
    }
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
0
    if (!bUseGrid && nFailedCount > 0)
3117
0
    {
3118
0
        bUseGrid = true;
3119
0
        bAll = false;
3120
0
        if (!ComputeSourceWindowTransformPoints(
3121
0
                nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3122
0
                nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3123
0
                dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3124
0
        {
3125
0
            return CE_Failure;
3126
0
        }
3127
0
    }
3128
3129
    /* -------------------------------------------------------------------- */
3130
    /*      If we get hardly any points (or none) transforming, we give     */
3131
    /*      up.                                                             */
3132
    /* -------------------------------------------------------------------- */
3133
0
    if (nFailedCount > nSamplePoints - 5)
3134
0
    {
3135
0
        const bool bErrorOutIfEmptySourceWindow =
3136
0
            CPLFetchBool(psOptions->papszWarpOptions,
3137
0
                         "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
3138
0
        if (bErrorOutIfEmptySourceWindow)
3139
0
        {
3140
0
            CPLError(CE_Failure, CPLE_AppDefined,
3141
0
                     "Too many points (%d out of %d) failed to transform, "
3142
0
                     "unable to compute output bounds.",
3143
0
                     nFailedCount, nSamplePoints);
3144
0
        }
3145
0
        else
3146
0
        {
3147
0
            CPLDebug("WARP", "Cannot determine source window for %d,%d,%d,%d",
3148
0
                     nDstXOff, nDstYOff, nDstXSize, nDstYSize);
3149
0
        }
3150
0
        return CE_Failure;
3151
0
    }
3152
3153
0
    if (nFailedCount > 0)
3154
0
        CPLDebug("GDAL",
3155
0
                 "GDALWarpOperation::ComputeSourceWindow() %d out of %d "
3156
0
                 "points failed to transform.",
3157
0
                 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
0
    if (bUseGrid)
3167
0
    {
3168
0
        ComputeSourceWindowStartingFromSource(nDstXOff, nDstYOff, nDstXSize,
3169
0
                                              nDstYSize, &dfMinXOut, &dfMinYOut,
3170
0
                                              &dfMaxXOut, &dfMaxYOut);
3171
0
    }
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
0
    if (dfMinXOut > nRasterXSize || dfMaxXOut < 0 || dfMinYOut > nRasterYSize ||
3178
0
        dfMaxYOut < 0)
3179
0
    {
3180
0
        *pnSrcXOff = 0;
3181
0
        *pnSrcYOff = 0;
3182
0
        *pnSrcXSize = 0;
3183
0
        *pnSrcYSize = 0;
3184
0
        if (pdfSrcXExtraSize)
3185
0
            *pdfSrcXExtraSize = 0.0;
3186
0
        if (pdfSrcYExtraSize)
3187
0
            *pdfSrcYExtraSize = 0.0;
3188
0
        if (pdfSrcFillRatio)
3189
0
            *pdfSrcFillRatio = 0.0;
3190
0
        return CE_None;
3191
0
    }
3192
3193
    // For scenarios where warping is used as a "decoration", try to clamp
3194
    // source pixel coordinates to integer when very close.
3195
0
    const auto roundIfCloseEnough = [](double dfVal)
3196
0
    {
3197
0
        const double dfRounded = std::round(dfVal);
3198
0
        if (std::fabs(dfRounded - dfVal) < 1e-6)
3199
0
            return dfRounded;
3200
0
        return dfVal;
3201
0
    };
3202
3203
0
    dfMinXOut = roundIfCloseEnough(dfMinXOut);
3204
0
    dfMinYOut = roundIfCloseEnough(dfMinYOut);
3205
0
    dfMaxXOut = roundIfCloseEnough(dfMaxXOut);
3206
0
    dfMaxYOut = roundIfCloseEnough(dfMaxYOut);
3207
3208
0
    if (m_bIsTranslationOnPixelBoundaries)
3209
0
    {
3210
0
        CPLAssert(dfMinXOut == std::round(dfMinXOut));
3211
0
        CPLAssert(dfMinYOut == std::round(dfMinYOut));
3212
0
        CPLAssert(dfMaxXOut == std::round(dfMaxXOut));
3213
0
        CPLAssert(dfMaxYOut == std::round(dfMaxYOut));
3214
0
        CPLAssert(std::round(dfMaxXOut - dfMinXOut) == nDstXSize);
3215
0
        CPLAssert(std::round(dfMaxYOut - dfMinYOut) == nDstYSize);
3216
0
    }
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
0
    const int nResWinSize = m_bIsTranslationOnPixelBoundaries
3226
0
                                ? 0
3227
0
                                : GWKGetFilterRadius(psOptions->eResampleAlg);
3228
3229
    // Take scaling into account.
3230
    // Avoid ridiculous small scaling factors to avoid potential further integer
3231
    // overflows
3232
0
    const double dfXScale = std::max(1e-3, static_cast<double>(nDstXSize) /
3233
0
                                               (dfMaxXOut - dfMinXOut));
3234
0
    const double dfYScale = std::max(1e-3, static_cast<double>(nDstYSize) /
3235
0
                                               (dfMaxYOut - dfMinYOut));
3236
0
    int nXRadius = dfXScale < 0.95
3237
0
                       ? static_cast<int>(ceil(nResWinSize / dfXScale))
3238
0
                       : nResWinSize;
3239
0
    int nYRadius = dfYScale < 0.95
3240
0
                       ? static_cast<int>(ceil(nResWinSize / dfYScale))
3241
0
                       : 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
0
    if (CSLFetchNameValue(psOptions->papszWarpOptions, "SOURCE_EXTRA") !=
3250
0
        nullptr)
3251
0
    {
3252
0
        const int nSrcExtra = atoi(
3253
0
            CSLFetchNameValue(psOptions->papszWarpOptions, "SOURCE_EXTRA"));
3254
0
        nXRadius += nSrcExtra;
3255
0
        nYRadius += nSrcExtra;
3256
0
    }
3257
0
    else if (nFailedCount > 0)
3258
0
    {
3259
0
        nXRadius += 10;
3260
0
        nYRadius += 10;
3261
0
    }
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
0
    const int nMinXOutClamped = static_cast<int>(std::max(0.0, dfMinXOut));
3274
0
    const int nMinYOutClamped = static_cast<int>(std::max(0.0, dfMinYOut));
3275
0
    const int nMaxXOutClamped = static_cast<int>(
3276
0
        std::min(ceil(dfMaxXOut), static_cast<double>(nRasterXSize)));
3277
0
    const int nMaxYOutClamped = static_cast<int>(
3278
0
        std::min(ceil(dfMaxYOut), static_cast<double>(nRasterYSize)));
3279
3280
0
    const double dfSrcXSizeRaw = std::max(
3281
0
        0.0, std::min(static_cast<double>(nRasterXSize - nMinXOutClamped),
3282
0
                      dfMaxXOut - dfMinXOut));
3283
0
    const double dfSrcYSizeRaw = std::max(
3284
0
        0.0, std::min(static_cast<double>(nRasterYSize - nMinYOutClamped),
3285
0
                      dfMaxYOut - dfMinYOut));
3286
3287
    // If we cover more than 90% of the width, then use it fully (helps for
3288
    // anti-meridian discontinuities)
3289
0
    if (nMaxXOutClamped - nMinXOutClamped > 0.9 * nRasterXSize)
3290
0
    {
3291
0
        *pnSrcXOff = 0;
3292
0
        *pnSrcXSize = nRasterXSize;
3293
0
    }
3294
0
    else
3295
0
    {
3296
0
        *pnSrcXOff =
3297
0
            std::max(0, std::min(nMinXOutClamped - nXRadius, nRasterXSize));
3298
0
        *pnSrcXSize =
3299
0
            std::max(0, std::min(nRasterXSize - *pnSrcXOff,
3300
0
                                 nMaxXOutClamped - *pnSrcXOff + nXRadius));
3301
0
    }
3302
3303
0
    if (nMaxYOutClamped - nMinYOutClamped > 0.9 * nRasterYSize)
3304
0
    {
3305
0
        *pnSrcYOff = 0;
3306
0
        *pnSrcYSize = nRasterYSize;
3307
0
    }
3308
0
    else
3309
0
    {
3310
0
        *pnSrcYOff =
3311
0
            std::max(0, std::min(nMinYOutClamped - nYRadius, nRasterYSize));
3312
0
        *pnSrcYSize =
3313
0
            std::max(0, std::min(nRasterYSize - *pnSrcYOff,
3314
0
                                 nMaxYOutClamped - *pnSrcYOff + nYRadius));
3315
0
    }
3316
3317
0
    if (pdfSrcXExtraSize)
3318
0
        *pdfSrcXExtraSize = *pnSrcXSize - dfSrcXSizeRaw;
3319
0
    if (pdfSrcYExtraSize)
3320
0
        *pdfSrcYExtraSize = *pnSrcYSize - dfSrcYSizeRaw;
3321
3322
    // Computed the ratio of the clamped source raster window size over
3323
    // the unclamped source raster window size.
3324
0
    if (pdfSrcFillRatio)
3325
0
        *pdfSrcFillRatio =
3326
0
            static_cast<double>(*pnSrcXSize) * (*pnSrcYSize) /
3327
0
            std::max(1.0, (dfMaxXOut - dfMinXOut + 2 * nXRadius) *
3328
0
                              (dfMaxYOut - dfMinYOut + 2 * nYRadius));
3329
3330
0
    return CE_None;
3331
0
}
3332
3333
/************************************************************************/
3334
/*                            ReportTiming()                            */
3335
/************************************************************************/
3336
3337
void GDALWarpOperation::ReportTiming(const char *pszMessage)
3338
3339
0
{
3340
0
    if (!bReportTimings)
3341
0
        return;
3342
3343
0
    const unsigned long nNewTime = VSITime(nullptr);
3344
3345
0
    if (pszMessage != nullptr)
3346
0
    {
3347
0
        CPLDebug("WARP_TIMING", "%s: %lds", pszMessage,
3348
0
                 static_cast<long>(nNewTime - nLastTimeReported));
3349
0
    }
3350
3351
0
    nLastTimeReported = nNewTime;
3352
0
}