Coverage Report

Created: 2026-02-14 09:00

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