Coverage Report

Created: 2025-11-16 06:25

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