Coverage Report

Created: 2025-12-31 06:48

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