Coverage Report

Created: 2025-06-13 06:29

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