Coverage Report

Created: 2026-07-14 06:21

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