Coverage Report

Created: 2026-07-25 06:50

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