Coverage Report

Created: 2026-04-01 06:20

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