Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdalwarpkernel.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  High Performance Image Reprojector
4
 * Purpose:  Implementation of the GDALWarpKernel class.  Implements the actual
5
 *           image warping for a "chunk" of input and output imagery already
6
 *           loaded into memory.
7
 * Author:   Frank Warmerdam, warmerdam@pobox.com
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
11
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
#include "cpl_port.h"
17
#include "gdalwarper.h"
18
19
#include <cfloat>
20
#include <cmath>
21
#include <cstddef>
22
#include <cstdlib>
23
#include <cstring>
24
25
#include <algorithm>
26
#include <limits>
27
#include <mutex>
28
#include <new>
29
#include <utility>
30
#include <vector>
31
32
#include "cpl_atomic_ops.h"
33
#include "cpl_conv.h"
34
#include "cpl_error.h"
35
#include "cpl_float.h"
36
#include "cpl_mask.h"
37
#include "cpl_multiproc.h"
38
#include "cpl_progress.h"
39
#include "cpl_string.h"
40
#include "cpl_vsi.h"
41
#include "cpl_worker_thread_pool.h"
42
#include "cpl_quad_tree.h"
43
#include "gdal.h"
44
#include "gdal_alg.h"
45
#include "gdal_alg_priv.h"
46
#include "gdal_thread_pool.h"
47
#include "gdalresamplingkernels.h"
48
49
// #define CHECK_SUM_WITH_GEOS
50
#ifdef CHECK_SUM_WITH_GEOS
51
#include "ogr_geometry.h"
52
#include "ogr_geos.h"
53
#endif
54
55
#ifdef USE_NEON_OPTIMIZATIONS
56
#include "include_sse2neon.h"
57
#define USE_SSE2
58
59
#include "gdalsse_priv.h"
60
61
// We restrict to 64bit processors because they are guaranteed to have SSE2.
62
// Could possibly be used too on 32bit, but we would need to check at runtime.
63
#elif defined(__x86_64) || defined(_M_X64)
64
#define USE_SSE2
65
66
#include "gdalsse_priv.h"
67
68
#if __SSE4_1__
69
#include <smmintrin.h>
70
#endif
71
72
#if __SSE3__
73
#include <pmmintrin.h>
74
#endif
75
76
#endif
77
78
constexpr double BAND_DENSITY_THRESHOLD = 0.0000000001;
79
constexpr float SRC_DENSITY_THRESHOLD_FLOAT = 0.000000001f;
80
constexpr double SRC_DENSITY_THRESHOLD_DOUBLE = 0.000000001;
81
82
// #define INSTANTIATE_FLOAT64_SSE2_IMPL
83
84
static const int anGWKFilterRadius[] = {
85
    0,  // Nearest neighbour
86
    1,  // Bilinear
87
    2,  // Cubic Convolution (Catmull-Rom)
88
    2,  // Cubic B-Spline
89
    3,  // Lanczos windowed sinc
90
    0,  // Average
91
    0,  // Mode
92
    0,  // Reserved GRA_Gauss=7
93
    0,  // Max
94
    0,  // Min
95
    0,  // Med
96
    0,  // Q1
97
    0,  // Q3
98
    0,  // Sum
99
    0,  // RMS
100
};
101
102
static double GWKBilinear(double dfX);
103
static double GWKCubic(double dfX);
104
static double GWKBSpline(double dfX);
105
static double GWKLanczosSinc(double dfX);
106
107
static const FilterFuncType apfGWKFilter[] = {
108
    nullptr,         // Nearest neighbour
109
    GWKBilinear,     // Bilinear
110
    GWKCubic,        // Cubic Convolution (Catmull-Rom)
111
    GWKBSpline,      // Cubic B-Spline
112
    GWKLanczosSinc,  // Lanczos windowed sinc
113
    nullptr,         // Average
114
    nullptr,         // Mode
115
    nullptr,         // Reserved GRA_Gauss=7
116
    nullptr,         // Max
117
    nullptr,         // Min
118
    nullptr,         // Med
119
    nullptr,         // Q1
120
    nullptr,         // Q3
121
    nullptr,         // Sum
122
    nullptr,         // RMS
123
};
124
125
// TODO(schwehr): Can we make these functions have a const * const arg?
126
static double GWKBilinear4Values(double *padfVals);
127
static double GWKCubic4Values(double *padfVals);
128
static double GWKBSpline4Values(double *padfVals);
129
static double GWKLanczosSinc4Values(double *padfVals);
130
131
static const FilterFunc4ValuesType apfGWKFilter4Values[] = {
132
    nullptr,                // Nearest neighbour
133
    GWKBilinear4Values,     // Bilinear
134
    GWKCubic4Values,        // Cubic Convolution (Catmull-Rom)
135
    GWKBSpline4Values,      // Cubic B-Spline
136
    GWKLanczosSinc4Values,  // Lanczos windowed sinc
137
    nullptr,                // Average
138
    nullptr,                // Mode
139
    nullptr,                // Reserved GRA_Gauss=7
140
    nullptr,                // Max
141
    nullptr,                // Min
142
    nullptr,                // Med
143
    nullptr,                // Q1
144
    nullptr,                // Q3
145
    nullptr,                // Sum
146
    nullptr,                // RMS
147
};
148
149
int GWKGetFilterRadius(GDALResampleAlg eResampleAlg)
150
0
{
151
0
    static_assert(CPL_ARRAYSIZE(anGWKFilterRadius) == GRA_LAST_VALUE + 1,
152
0
                  "Bad size of anGWKFilterRadius");
153
0
    return anGWKFilterRadius[eResampleAlg];
154
0
}
155
156
FilterFuncType GWKGetFilterFunc(GDALResampleAlg eResampleAlg)
157
0
{
158
0
    static_assert(CPL_ARRAYSIZE(apfGWKFilter) == GRA_LAST_VALUE + 1,
159
0
                  "Bad size of apfGWKFilter");
160
0
    return apfGWKFilter[eResampleAlg];
161
0
}
162
163
FilterFunc4ValuesType GWKGetFilterFunc4Values(GDALResampleAlg eResampleAlg)
164
0
{
165
0
    static_assert(CPL_ARRAYSIZE(apfGWKFilter4Values) == GRA_LAST_VALUE + 1,
166
0
                  "Bad size of apfGWKFilter4Values");
167
0
    return apfGWKFilter4Values[eResampleAlg];
168
0
}
169
170
static CPLErr GWKGeneralCase(GDALWarpKernel *);
171
static CPLErr GWKRealCase(GDALWarpKernel *poWK);
172
static CPLErr GWKNearestNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK);
173
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK);
174
static CPLErr GWKCubicNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK);
175
static CPLErr GWKCubicNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK);
176
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
177
static CPLErr GWKCubicNoMasksOrDstDensityOnlyDouble(GDALWarpKernel *poWK);
178
#endif
179
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK);
180
static CPLErr GWKNearestByte(GDALWarpKernel *poWK);
181
static CPLErr GWKNearestNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK);
182
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK);
183
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK);
184
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
185
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyDouble(GDALWarpKernel *poWK);
186
#endif
187
static CPLErr GWKCubicNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK);
188
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK);
189
static CPLErr GWKNearestShort(GDALWarpKernel *poWK);
190
static CPLErr GWKNearestUnsignedShort(GDALWarpKernel *poWK);
191
static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK);
192
static CPLErr GWKNearestFloat(GDALWarpKernel *poWK);
193
static CPLErr GWKAverageOrMode(GDALWarpKernel *);
194
static CPLErr GWKSumPreserving(GDALWarpKernel *);
195
static CPLErr GWKCubicNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *);
196
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *);
197
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *);
198
199
/************************************************************************/
200
/*                             GWKJobStruct                             */
201
/************************************************************************/
202
203
struct GWKJobStruct
204
{
205
    std::mutex &mutex;
206
    std::condition_variable &cv;
207
    int counterSingleThreaded = 0;
208
    int &counter;
209
    bool &stopFlag;
210
    GDALWarpKernel *poWK = nullptr;
211
    int iYMin = 0;
212
    int iYMax = 0;
213
    int (*pfnProgress)(GWKJobStruct *psJob) = nullptr;
214
    void *pTransformerArg = nullptr;
215
    // used by GWKRun() to assign the proper pTransformerArg
216
    void (*pfnFunc)(void *) = nullptr;
217
218
    GWKJobStruct(std::mutex &mutex_, std::condition_variable &cv_,
219
                 int &counter_, bool &stopFlag_)
220
0
        : mutex(mutex_), cv(cv_), counter(counter_), stopFlag(stopFlag_)
221
0
    {
222
0
    }
223
};
224
225
struct GWKThreadData
226
{
227
    std::unique_ptr<CPLJobQueue> poJobQueue{};
228
    std::unique_ptr<std::vector<GWKJobStruct>> threadJobs{};
229
    int nMaxThreads{0};
230
    int counter{0};
231
    bool stopFlag{false};
232
    std::mutex mutex{};
233
    std::condition_variable cv{};
234
    bool bTransformerArgInputAssignedToThread{false};
235
    void *pTransformerArgInput{
236
        nullptr};  // owned by calling layer. Not to be destroyed
237
    std::map<GIntBig, void *> mapThreadToTransformerArg{};
238
    int nTotalThreadCountForThisRun = 0;
239
    int nCurThreadCountForThisRun = 0;
240
};
241
242
/************************************************************************/
243
/*                         GWKProgressThread()                          */
244
/************************************************************************/
245
246
// Return TRUE if the computation must be interrupted.
247
static int GWKProgressThread(GWKJobStruct *psJob)
248
0
{
249
0
    bool stop = false;
250
0
    {
251
0
        std::lock_guard<std::mutex> lock(psJob->mutex);
252
0
        psJob->counter++;
253
0
        stop = psJob->stopFlag;
254
0
    }
255
0
    psJob->cv.notify_one();
256
257
0
    return stop;
258
0
}
259
260
/************************************************************************/
261
/*                       GWKProgressMonoThread()                        */
262
/************************************************************************/
263
264
// Return TRUE if the computation must be interrupted.
265
static int GWKProgressMonoThread(GWKJobStruct *psJob)
266
0
{
267
0
    GDALWarpKernel *poWK = psJob->poWK;
268
0
    if (!poWK->pfnProgress(poWK->dfProgressBase +
269
0
                               poWK->dfProgressScale *
270
0
                                   (++psJob->counterSingleThreaded /
271
0
                                    static_cast<double>(psJob->iYMax)),
272
0
                           "", poWK->pProgress))
273
0
    {
274
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
275
0
        psJob->stopFlag = true;
276
0
        return TRUE;
277
0
    }
278
0
    return FALSE;
279
0
}
280
281
/************************************************************************/
282
/*                        GWKGenericMonoThread()                        */
283
/************************************************************************/
284
285
static CPLErr GWKGenericMonoThread(GDALWarpKernel *poWK,
286
                                   void (*pfnFunc)(void *pUserData))
287
0
{
288
0
    GWKThreadData td;
289
290
    // NOTE: the mutex is not used.
291
0
    GWKJobStruct job(td.mutex, td.cv, td.counter, td.stopFlag);
292
0
    job.poWK = poWK;
293
0
    job.iYMin = 0;
294
0
    job.iYMax = poWK->nDstYSize;
295
0
    job.pfnProgress = GWKProgressMonoThread;
296
0
    job.pTransformerArg = poWK->pTransformerArg;
297
0
    job.counterSingleThreaded = td.counter;
298
0
    pfnFunc(&job);
299
0
    td.counter = job.counterSingleThreaded;
300
301
0
    return td.stopFlag ? CE_Failure : CE_None;
302
0
}
303
304
/************************************************************************/
305
/*                          GWKThreadsCreate()                          */
306
/************************************************************************/
307
308
void *GWKThreadsCreate(char **papszWarpOptions,
309
                       GDALTransformerFunc /* pfnTransformer */,
310
                       void *pTransformerArg)
311
0
{
312
0
    const int nThreads = GDALGetNumThreads(papszWarpOptions, "NUM_THREADS",
313
0
                                           GDAL_DEFAULT_MAX_THREAD_COUNT,
314
0
                                           /* bDefaultAllCPUs = */ false);
315
0
    GWKThreadData *psThreadData = new GWKThreadData();
316
0
    auto poThreadPool =
317
0
        nThreads > 1 ? GDALGetGlobalThreadPool(nThreads) : nullptr;
318
0
    if (poThreadPool)
319
0
    {
320
0
        psThreadData->nMaxThreads = nThreads;
321
0
        psThreadData->threadJobs.reset(new std::vector<GWKJobStruct>(
322
0
            nThreads,
323
0
            GWKJobStruct(psThreadData->mutex, psThreadData->cv,
324
0
                         psThreadData->counter, psThreadData->stopFlag)));
325
326
0
        psThreadData->poJobQueue = poThreadPool->CreateJobQueue();
327
0
        psThreadData->pTransformerArgInput = pTransformerArg;
328
0
    }
329
330
0
    return psThreadData;
331
0
}
332
333
/************************************************************************/
334
/*                           GWKThreadsEnd()                            */
335
/************************************************************************/
336
337
void GWKThreadsEnd(void *psThreadDataIn)
338
0
{
339
0
    if (psThreadDataIn == nullptr)
340
0
        return;
341
342
0
    GWKThreadData *psThreadData = static_cast<GWKThreadData *>(psThreadDataIn);
343
0
    if (psThreadData->poJobQueue)
344
0
    {
345
        // cppcheck-suppress constVariableReference
346
0
        for (auto &pair : psThreadData->mapThreadToTransformerArg)
347
0
        {
348
0
            CPLAssert(pair.second != psThreadData->pTransformerArgInput);
349
0
            GDALDestroyTransformer(pair.second);
350
0
        }
351
0
        psThreadData->poJobQueue.reset();
352
0
    }
353
0
    delete psThreadData;
354
0
}
355
356
/************************************************************************/
357
/*                         ThreadFuncAdapter()                          */
358
/************************************************************************/
359
360
static void ThreadFuncAdapter(void *pData)
361
0
{
362
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
363
0
    GWKThreadData *psThreadData =
364
0
        static_cast<GWKThreadData *>(psJob->poWK->psThreadData);
365
366
    // Look if we have already a per-thread transformer
367
0
    void *pTransformerArg = nullptr;
368
0
    const GIntBig nThreadId = CPLGetPID();
369
370
0
    {
371
0
        std::lock_guard<std::mutex> lock(psThreadData->mutex);
372
0
        ++psThreadData->nCurThreadCountForThisRun;
373
374
0
        auto oIter = psThreadData->mapThreadToTransformerArg.find(nThreadId);
375
0
        if (oIter != psThreadData->mapThreadToTransformerArg.end())
376
0
        {
377
0
            pTransformerArg = oIter->second;
378
0
        }
379
0
        else if (!psThreadData->bTransformerArgInputAssignedToThread &&
380
0
                 psThreadData->nCurThreadCountForThisRun ==
381
0
                     psThreadData->nTotalThreadCountForThisRun)
382
0
        {
383
            // If we are the last thread to be started, temporarily borrow the
384
            // original transformer
385
0
            psThreadData->bTransformerArgInputAssignedToThread = true;
386
0
            pTransformerArg = psThreadData->pTransformerArgInput;
387
0
            psThreadData->mapThreadToTransformerArg[nThreadId] =
388
0
                pTransformerArg;
389
0
        }
390
391
0
        if (pTransformerArg == nullptr)
392
0
        {
393
0
            CPLAssert(psThreadData->pTransformerArgInput != nullptr);
394
0
            CPLAssert(!psThreadData->bTransformerArgInputAssignedToThread);
395
0
        }
396
0
    }
397
398
    // If no transformer assigned to current thread, instantiate one
399
0
    if (pTransformerArg == nullptr)
400
0
    {
401
        // This somehow assumes that GDALCloneTransformer() is thread-safe
402
        // which should normally be the case.
403
0
        pTransformerArg =
404
0
            GDALCloneTransformer(psThreadData->pTransformerArgInput);
405
406
        // Lock for the stop flag and the transformer map.
407
0
        std::lock_guard<std::mutex> lock(psThreadData->mutex);
408
0
        if (!pTransformerArg)
409
0
        {
410
0
            psJob->stopFlag = true;
411
0
            return;
412
0
        }
413
0
        psThreadData->mapThreadToTransformerArg[nThreadId] = pTransformerArg;
414
0
    }
415
416
0
    psJob->pTransformerArg = pTransformerArg;
417
0
    psJob->pfnFunc(pData);
418
419
    // Give back original transformer, if borrowed.
420
0
    {
421
0
        std::lock_guard<std::mutex> lock(psThreadData->mutex);
422
0
        if (psThreadData->bTransformerArgInputAssignedToThread &&
423
0
            pTransformerArg == psThreadData->pTransformerArgInput)
424
0
        {
425
0
            psThreadData->mapThreadToTransformerArg.erase(
426
0
                psThreadData->mapThreadToTransformerArg.find(nThreadId));
427
0
            psThreadData->bTransformerArgInputAssignedToThread = false;
428
0
        }
429
0
    }
430
0
}
431
432
/************************************************************************/
433
/*                               GWKRun()                               */
434
/************************************************************************/
435
436
static CPLErr GWKRun(GDALWarpKernel *poWK, const char *pszFuncName,
437
                     void (*pfnFunc)(void *pUserData))
438
439
0
{
440
0
    const int nDstYSize = poWK->nDstYSize;
441
442
0
    CPLDebug("GDAL",
443
0
             "GDALWarpKernel()::%s() "
444
0
             "Src=%d,%d,%dx%d Dst=%d,%d,%dx%d",
445
0
             pszFuncName, poWK->nSrcXOff, poWK->nSrcYOff, poWK->nSrcXSize,
446
0
             poWK->nSrcYSize, poWK->nDstXOff, poWK->nDstYOff, poWK->nDstXSize,
447
0
             poWK->nDstYSize);
448
449
0
    if (!poWK->pfnProgress(poWK->dfProgressBase, "", poWK->pProgress))
450
0
    {
451
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
452
0
        return CE_Failure;
453
0
    }
454
455
0
    GWKThreadData *psThreadData =
456
0
        static_cast<GWKThreadData *>(poWK->psThreadData);
457
0
    if (psThreadData == nullptr || psThreadData->poJobQueue == nullptr)
458
0
    {
459
0
        return GWKGenericMonoThread(poWK, pfnFunc);
460
0
    }
461
462
0
    int nThreads = std::min(psThreadData->nMaxThreads, nDstYSize / 2);
463
    // Config option mostly useful for tests to be able to test multithreading
464
    // with small rasters
465
0
    const int nWarpChunkSize =
466
0
        atoi(CPLGetConfigOption("WARP_THREAD_CHUNK_SIZE", "65536"));
467
0
    if (nWarpChunkSize > 0)
468
0
    {
469
0
        GIntBig nChunks =
470
0
            static_cast<GIntBig>(nDstYSize) * poWK->nDstXSize / nWarpChunkSize;
471
0
        if (nThreads > nChunks)
472
0
            nThreads = static_cast<int>(nChunks);
473
0
    }
474
0
    if (nThreads <= 0)
475
0
        nThreads = 1;
476
477
0
    CPLDebug("WARP", "Using %d threads", nThreads);
478
479
0
    auto &jobs = *psThreadData->threadJobs;
480
0
    CPLAssert(static_cast<int>(jobs.size()) >= nThreads);
481
    // Fill-in job structures.
482
0
    for (int i = 0; i < nThreads; ++i)
483
0
    {
484
0
        auto &job = jobs[i];
485
0
        job.poWK = poWK;
486
0
        job.iYMin =
487
0
            static_cast<int>(static_cast<int64_t>(i) * nDstYSize / nThreads);
488
0
        job.iYMax = static_cast<int>(static_cast<int64_t>(i + 1) * nDstYSize /
489
0
                                     nThreads);
490
0
        if (poWK->pfnProgress != GDALDummyProgress)
491
0
            job.pfnProgress = GWKProgressThread;
492
0
        job.pfnFunc = pfnFunc;
493
0
    }
494
495
0
    bool bStopFlag;
496
0
    {
497
0
        std::unique_lock<std::mutex> lock(psThreadData->mutex);
498
499
0
        psThreadData->nTotalThreadCountForThisRun = nThreads;
500
        // coverity[missing_lock]
501
0
        psThreadData->nCurThreadCountForThisRun = 0;
502
503
        // Start jobs.
504
0
        for (int i = 0; i < nThreads; ++i)
505
0
        {
506
0
            auto &job = jobs[i];
507
0
            psThreadData->poJobQueue->SubmitJob(ThreadFuncAdapter,
508
0
                                                static_cast<void *>(&job));
509
0
        }
510
511
        /* --------------------------------------------------------------------
512
         */
513
        /*      Report progress. */
514
        /* --------------------------------------------------------------------
515
         */
516
0
        if (poWK->pfnProgress != GDALDummyProgress)
517
0
        {
518
0
            while (psThreadData->counter < nDstYSize)
519
0
            {
520
0
                psThreadData->cv.wait(lock);
521
0
                if (!poWK->pfnProgress(poWK->dfProgressBase +
522
0
                                           poWK->dfProgressScale *
523
0
                                               (psThreadData->counter /
524
0
                                                static_cast<double>(nDstYSize)),
525
0
                                       "", poWK->pProgress))
526
0
                {
527
0
                    CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
528
0
                    psThreadData->stopFlag = true;
529
0
                    break;
530
0
                }
531
0
            }
532
0
        }
533
534
0
        bStopFlag = psThreadData->stopFlag;
535
0
    }
536
537
    /* -------------------------------------------------------------------- */
538
    /*      Wait for all jobs to complete.                                  */
539
    /* -------------------------------------------------------------------- */
540
0
    psThreadData->poJobQueue->WaitCompletion();
541
542
0
    return bStopFlag ? CE_Failure : CE_None;
543
0
}
544
545
/************************************************************************/
546
/* ==================================================================== */
547
/*                            GDALWarpKernel                            */
548
/* ==================================================================== */
549
/************************************************************************/
550
551
/**
552
 * \class GDALWarpKernel "gdalwarper.h"
553
 *
554
 * Low level image warping class.
555
 *
556
 * This class is responsible for low level image warping for one
557
 * "chunk" of imagery.  The class is essentially a structure with all
558
 * data members public - primarily so that new special-case functions
559
 * can be added without changing the class declaration.
560
 *
561
 * Applications are normally intended to interactive with warping facilities
562
 * through the GDALWarpOperation class, though the GDALWarpKernel can in
563
 * theory be used directly if great care is taken in setting up the
564
 * control data.
565
 *
566
 * <h3>Design Issues</h3>
567
 *
568
 * The intention is that PerformWarp() would analyze the setup in terms
569
 * of the datatype, resampling type, and validity/density mask usage and
570
 * pick one of many specific implementations of the warping algorithm over
571
 * a continuum of optimization vs. generality.  At one end there will be a
572
 * reference general purpose implementation of the algorithm that supports
573
 * any data type (working internally in double precision complex), all three
574
 * resampling types, and any or all of the validity/density masks.  At the
575
 * other end would be highly optimized algorithms for common cases like
576
 * nearest neighbour resampling on GDT_UInt8 data with no masks.
577
 *
578
 * The full set of optimized versions have not been decided but we should
579
 * expect to have at least:
580
 *  - One for each resampling algorithm for 8bit data with no masks.
581
 *  - One for each resampling algorithm for float data with no masks.
582
 *  - One for each resampling algorithm for float data with any/all masks
583
 *    (essentially the generic case for just float data).
584
 *  - One for each resampling algorithm for 8bit data with support for
585
 *    input validity masks (per band or per pixel).  This handles the common
586
 *    case of nodata masking.
587
 *  - One for each resampling algorithm for float data with support for
588
 *    input validity masks (per band or per pixel).  This handles the common
589
 *    case of nodata masking.
590
 *
591
 * Some of the specializations would operate on all bands in one pass
592
 * (especially the ones without masking would do this), while others might
593
 * process each band individually to reduce code complexity.
594
 *
595
 * <h3>Masking Semantics</h3>
596
 *
597
 * A detailed explanation of the semantics of the validity and density masks,
598
 * and their effects on resampling kernels is needed here.
599
 */
600
601
/************************************************************************/
602
/*                     GDALWarpKernel Data Members                      */
603
/************************************************************************/
604
605
/**
606
 * \var GDALResampleAlg GDALWarpKernel::eResample;
607
 *
608
 * Resampling algorithm.
609
 *
610
 * The resampling algorithm to use.  One of GRA_NearestNeighbour, GRA_Bilinear,
611
 * GRA_Cubic, GRA_CubicSpline, GRA_Lanczos, GRA_Average, GRA_RMS,
612
 * GRA_Mode or GRA_Sum.
613
 *
614
 * This field is required. GDT_NearestNeighbour may be used as a default
615
 * value.
616
 */
617
618
/**
619
 * \var GDALDataType GDALWarpKernel::eWorkingDataType;
620
 *
621
 * Working pixel data type.
622
 *
623
 * The datatype of pixels in the source image (papabySrcimage) and
624
 * destination image (papabyDstImage) buffers.  Note that operations on
625
 * some data types (such as GDT_UInt8) may be much better optimized than other
626
 * less common cases.
627
 *
628
 * This field is required.  It may not be GDT_Unknown.
629
 */
630
631
/**
632
 * \var int GDALWarpKernel::nBands;
633
 *
634
 * Number of bands.
635
 *
636
 * The number of bands (layers) of imagery being warped.  Determines the
637
 * number of entries in the papabySrcImage, papanBandSrcValid,
638
 * and papabyDstImage arrays.
639
 *
640
 * This field is required.
641
 */
642
643
/**
644
 * \var int GDALWarpKernel::nSrcXSize;
645
 *
646
 * Source image width in pixels.
647
 *
648
 * This field is required.
649
 */
650
651
/**
652
 * \var int GDALWarpKernel::nSrcYSize;
653
 *
654
 * Source image height in pixels.
655
 *
656
 * This field is required.
657
 */
658
659
/**
660
 * \var double GDALWarpKernel::dfSrcXExtraSize;
661
 *
662
 * Number of pixels included in nSrcXSize that are present on the edges of
663
 * the area of interest to take into account the width of the kernel.
664
 *
665
 * This field is required.
666
 */
667
668
/**
669
 * \var double GDALWarpKernel::dfSrcYExtraSize;
670
 *
671
 * Number of pixels included in nSrcYExtraSize that are present on the edges of
672
 * the area of interest to take into account the height of the kernel.
673
 *
674
 * This field is required.
675
 */
676
677
/**
678
 * \var int GDALWarpKernel::papabySrcImage;
679
 *
680
 * Array of source image band data.
681
 *
682
 * This is an array of pointers (of size GDALWarpKernel::nBands) pointers
683
 * to image data.  Each individual band of image data is organized as a single
684
 * block of image data in left to right, then bottom to top order.  The actual
685
 * type of the image data is determined by GDALWarpKernel::eWorkingDataType.
686
 *
687
 * To access the pixel value for the (x=3, y=4) pixel (zero based) of
688
 * the second band with eWorkingDataType set to GDT_Float32 use code like
689
 * this:
690
 *
691
 * \code
692
 *   float dfPixelValue;
693
 *   int   nBand = 2-1;  // Band indexes are zero based.
694
 *   int   nPixel = 3; // Zero based.
695
 *   int   nLine = 4;  // Zero based.
696
 *
697
 *   assert( nPixel >= 0 && nPixel < poKern->nSrcXSize );
698
 *   assert( nLine >= 0 && nLine < poKern->nSrcYSize );
699
 *   assert( nBand >= 0 && nBand < poKern->nBands );
700
 *   dfPixelValue = ((float *) poKern->papabySrcImage[nBand])
701
 *                                  [nPixel + nLine * poKern->nSrcXSize];
702
 * \endcode
703
 *
704
 * This field is required.
705
 */
706
707
/**
708
 * \var GUInt32 **GDALWarpKernel::papanBandSrcValid;
709
 *
710
 * Per band validity mask for source pixels.
711
 *
712
 * Array of pixel validity mask layers for each source band.   Each of
713
 * the mask layers is the same size (in pixels) as the source image with
714
 * one bit per pixel.  Note that it is legal (and common) for this to be
715
 * NULL indicating that none of the pixels are invalidated, or for some
716
 * band validity masks to be NULL in which case all pixels of the band are
717
 * valid.  The following code can be used to test the validity of a particular
718
 * pixel.
719
 *
720
 * \code
721
 *   int   bIsValid = TRUE;
722
 *   int   nBand = 2-1;  // Band indexes are zero based.
723
 *   int   nPixel = 3; // Zero based.
724
 *   int   nLine = 4;  // Zero based.
725
 *
726
 *   assert( nPixel >= 0 && nPixel < poKern->nSrcXSize );
727
 *   assert( nLine >= 0 && nLine < poKern->nSrcYSize );
728
 *   assert( nBand >= 0 && nBand < poKern->nBands );
729
 *
730
 *   if( poKern->papanBandSrcValid != NULL
731
 *       && poKern->papanBandSrcValid[nBand] != NULL )
732
 *   {
733
 *       GUInt32 *panBandMask = poKern->papanBandSrcValid[nBand];
734
 *       int    iPixelOffset = nPixel + nLine * poKern->nSrcXSize;
735
 *
736
 *       bIsValid = CPLMaskGet(panBandMask, iPixelOffset)
737
 *   }
738
 * \endcode
739
 */
740
741
/**
742
 * \var GUInt32 *GDALWarpKernel::panUnifiedSrcValid;
743
 *
744
 * Per pixel validity mask for source pixels.
745
 *
746
 * A single validity mask layer that applies to the pixels of all source
747
 * bands.  It is accessed similarly to papanBandSrcValid, but without the
748
 * extra level of band indirection.
749
 *
750
 * This pointer may be NULL indicating that all pixels are valid.
751
 *
752
 * Note that if both panUnifiedSrcValid, and papanBandSrcValid are available,
753
 * the pixel isn't considered to be valid unless both arrays indicate it is
754
 * valid.
755
 */
756
757
/**
758
 * \var float *GDALWarpKernel::pafUnifiedSrcDensity;
759
 *
760
 * Per pixel density mask for source pixels.
761
 *
762
 * A single density mask layer that applies to the pixels of all source
763
 * bands.  It contains values between 0.0 and 1.0 indicating the degree to
764
 * which this pixel should be allowed to contribute to the output result.
765
 *
766
 * This pointer may be NULL indicating that all pixels have a density of 1.0.
767
 *
768
 * The density for a pixel may be accessed like this:
769
 *
770
 * \code
771
 *   float fDensity = 1.0;
772
 *   int nPixel = 3;  // Zero based.
773
 *   int nLine = 4;   // Zero based.
774
 *
775
 *   assert( nPixel >= 0 && nPixel < poKern->nSrcXSize );
776
 *   assert( nLine >= 0 && nLine < poKern->nSrcYSize );
777
 *   if( poKern->pafUnifiedSrcDensity != NULL )
778
 *     fDensity = poKern->pafUnifiedSrcDensity
779
 *                                  [nPixel + nLine * poKern->nSrcXSize];
780
 * \endcode
781
 */
782
783
/**
784
 * \var int GDALWarpKernel::nDstXSize;
785
 *
786
 * Width of destination image in pixels.
787
 *
788
 * This field is required.
789
 */
790
791
/**
792
 * \var int GDALWarpKernel::nDstYSize;
793
 *
794
 * Height of destination image in pixels.
795
 *
796
 * This field is required.
797
 */
798
799
/**
800
 * \var GByte **GDALWarpKernel::papabyDstImage;
801
 *
802
 * Array of destination image band data.
803
 *
804
 * This is an array of pointers (of size GDALWarpKernel::nBands) pointers
805
 * to image data.  Each individual band of image data is organized as a single
806
 * block of image data in left to right, then bottom to top order.  The actual
807
 * type of the image data is determined by GDALWarpKernel::eWorkingDataType.
808
 *
809
 * To access the pixel value for the (x=3, y=4) pixel (zero based) of
810
 * the second band with eWorkingDataType set to GDT_Float32 use code like
811
 * this:
812
 *
813
 * \code
814
 *   float dfPixelValue;
815
 *   int   nBand = 2-1;  // Band indexes are zero based.
816
 *   int   nPixel = 3; // Zero based.
817
 *   int   nLine = 4;  // Zero based.
818
 *
819
 *   assert( nPixel >= 0 && nPixel < poKern->nDstXSize );
820
 *   assert( nLine >= 0 && nLine < poKern->nDstYSize );
821
 *   assert( nBand >= 0 && nBand < poKern->nBands );
822
 *   dfPixelValue = ((float *) poKern->papabyDstImage[nBand])
823
 *                                  [nPixel + nLine * poKern->nSrcYSize];
824
 * \endcode
825
 *
826
 * This field is required.
827
 */
828
829
/**
830
 * \var GUInt32 *GDALWarpKernel::panDstValid;
831
 *
832
 * Per pixel validity mask for destination pixels.
833
 *
834
 * A single validity mask layer that applies to the pixels of all destination
835
 * bands.  It is accessed similarly to papanUnitifiedSrcValid, but based
836
 * on the size of the destination image.
837
 *
838
 * This pointer may be NULL indicating that all pixels are valid.
839
 */
840
841
/**
842
 * \var float *GDALWarpKernel::pafDstDensity;
843
 *
844
 * Per pixel density mask for destination pixels.
845
 *
846
 * A single density mask layer that applies to the pixels of all destination
847
 * bands.  It contains values between 0.0 and 1.0.
848
 *
849
 * This pointer may be NULL indicating that all pixels have a density of 1.0.
850
 *
851
 * The density for a pixel may be accessed like this:
852
 *
853
 * \code
854
 *   float fDensity = 1.0;
855
 *   int   nPixel = 3; // Zero based.
856
 *   int   nLine = 4;  // Zero based.
857
 *
858
 *   assert( nPixel >= 0 && nPixel < poKern->nDstXSize );
859
 *   assert( nLine >= 0 && nLine < poKern->nDstYSize );
860
 *   if( poKern->pafDstDensity != NULL )
861
 *     fDensity = poKern->pafDstDensity[nPixel + nLine * poKern->nDstXSize];
862
 * \endcode
863
 */
864
865
/**
866
 * \var int GDALWarpKernel::nSrcXOff;
867
 *
868
 * X offset to source pixel coordinates for transformation.
869
 *
870
 * See pfnTransformer.
871
 *
872
 * This field is required.
873
 */
874
875
/**
876
 * \var int GDALWarpKernel::nSrcYOff;
877
 *
878
 * Y offset to source pixel coordinates for transformation.
879
 *
880
 * See pfnTransformer.
881
 *
882
 * This field is required.
883
 */
884
885
/**
886
 * \var int GDALWarpKernel::nDstXOff;
887
 *
888
 * X offset to destination pixel coordinates for transformation.
889
 *
890
 * See pfnTransformer.
891
 *
892
 * This field is required.
893
 */
894
895
/**
896
 * \var int GDALWarpKernel::nDstYOff;
897
 *
898
 * Y offset to destination pixel coordinates for transformation.
899
 *
900
 * See pfnTransformer.
901
 *
902
 * This field is required.
903
 */
904
905
/**
906
 * \var GDALTransformerFunc GDALWarpKernel::pfnTransformer;
907
 *
908
 * Source/destination location transformer.
909
 *
910
 * The function to call to transform coordinates between source image
911
 * pixel/line coordinates and destination image pixel/line coordinates.
912
 * See GDALTransformerFunc() for details of the semantics of this function.
913
 *
914
 * The GDALWarpKern algorithm will only ever use this transformer in
915
 * "destination to source" mode (bDstToSrc=TRUE), and will always pass
916
 * partial or complete scanlines of points in the destination image as
917
 * input.  This means, among other things, that it is safe to the
918
 * approximating transform GDALApproxTransform() as the transformation
919
 * function.
920
 *
921
 * Source and destination images may be subsets of a larger overall image.
922
 * The transformation algorithms will expect and return pixel/line coordinates
923
 * in terms of this larger image, so coordinates need to be offset by
924
 * the offsets specified in nSrcXOff, nSrcYOff, nDstXOff, and nDstYOff before
925
 * passing to pfnTransformer, and after return from it.
926
 *
927
 * The GDALWarpKernel::pfnTransformerArg value will be passed as the callback
928
 * data to this function when it is called.
929
 *
930
 * This field is required.
931
 */
932
933
/**
934
 * \var void *GDALWarpKernel::pTransformerArg;
935
 *
936
 * Callback data for pfnTransformer.
937
 *
938
 * This field may be NULL if not required for the pfnTransformer being used.
939
 */
940
941
/**
942
 * \var GDALProgressFunc GDALWarpKernel::pfnProgress;
943
 *
944
 * The function to call to report progress of the algorithm, and to check
945
 * for a requested termination of the operation.  It operates according to
946
 * GDALProgressFunc() semantics.
947
 *
948
 * Generally speaking the progress function will be invoked for each
949
 * scanline of the destination buffer that has been processed.
950
 *
951
 * This field may be NULL (internally set to GDALDummyProgress()).
952
 */
953
954
/**
955
 * \var void *GDALWarpKernel::pProgress;
956
 *
957
 * Callback data for pfnProgress.
958
 *
959
 * This field may be NULL if not required for the pfnProgress being used.
960
 */
961
962
/************************************************************************/
963
/*                           GDALWarpKernel()                           */
964
/************************************************************************/
965
966
GDALWarpKernel::GDALWarpKernel()
967
0
    : papszWarpOptions(nullptr), eResample(GRA_NearestNeighbour),
968
0
      eWorkingDataType(GDT_Unknown), nBands(0), nSrcXSize(0), nSrcYSize(0),
969
0
      dfSrcXExtraSize(0.0), dfSrcYExtraSize(0.0), papabySrcImage(nullptr),
970
0
      papanBandSrcValid(nullptr), panUnifiedSrcValid(nullptr),
971
0
      pafUnifiedSrcDensity(nullptr), nDstXSize(0), nDstYSize(0),
972
0
      papabyDstImage(nullptr), panDstValid(nullptr), pafDstDensity(nullptr),
973
0
      dfXScale(1.0), dfYScale(1.0), dfXFilter(0.0), dfYFilter(0.0), nXRadius(0),
974
0
      nYRadius(0), nFiltInitX(0), nFiltInitY(0), nSrcXOff(0), nSrcYOff(0),
975
0
      nDstXOff(0), nDstYOff(0), pfnTransformer(nullptr),
976
0
      pTransformerArg(nullptr), pfnProgress(GDALDummyProgress),
977
0
      pProgress(nullptr), dfProgressBase(0.0), dfProgressScale(1.0),
978
0
      padfDstNoDataReal(nullptr), psThreadData(nullptr),
979
0
      eTieStrategy(GWKTS_First)
980
0
{
981
0
}
982
983
/************************************************************************/
984
/*                          ~GDALWarpKernel()                           */
985
/************************************************************************/
986
987
GDALWarpKernel::~GDALWarpKernel()
988
0
{
989
0
}
990
991
/************************************************************************/
992
/*                              getArea()                               */
993
/************************************************************************/
994
995
typedef std::pair<double, double> XYPair;
996
997
typedef std::vector<XYPair> XYPoly;
998
999
// poly may or may not be closed.
1000
static double getArea(const XYPoly &poly)
1001
0
{
1002
    // CPLAssert(poly.size() >= 2);
1003
0
    const size_t nPointCount = poly.size();
1004
0
    double dfAreaSum =
1005
0
        poly[0].first * (poly[1].second - poly[nPointCount - 1].second);
1006
1007
0
    for (size_t i = 1; i < nPointCount - 1; i++)
1008
0
    {
1009
0
        dfAreaSum += poly[i].first * (poly[i + 1].second - poly[i - 1].second);
1010
0
    }
1011
1012
0
    dfAreaSum += poly[nPointCount - 1].first *
1013
0
                 (poly[0].second - poly[nPointCount - 2].second);
1014
1015
0
    return 0.5 * std::fabs(dfAreaSum);
1016
0
}
1017
1018
/************************************************************************/
1019
/*                       CanUse4SamplesFormula()                        */
1020
/************************************************************************/
1021
1022
static bool CanUse4SamplesFormula(const GDALWarpKernel *poWK)
1023
0
{
1024
0
    if (poWK->eResample == GRA_Bilinear || poWK->eResample == GRA_Cubic)
1025
0
    {
1026
        // Use 4-sample formula if we are not downsampling by more than a
1027
        // factor of 1:2
1028
0
        if (poWK->dfXScale > 0.5 && poWK->dfYScale > 0.5)
1029
0
            return true;
1030
0
        CPLDebugOnce("WARP",
1031
0
                     "Not using 4-sample bilinear/bicubic formula because "
1032
0
                     "XSCALE(=%f) and/or YSCALE(=%f) <= 0.5",
1033
0
                     poWK->dfXScale, poWK->dfYScale);
1034
0
    }
1035
0
    return false;
1036
0
}
1037
1038
/************************************************************************/
1039
/*                            PerformWarp()                             */
1040
/************************************************************************/
1041
1042
/**
1043
 * \fn CPLErr GDALWarpKernel::PerformWarp();
1044
 *
1045
 * This method performs the warp described in the GDALWarpKernel.
1046
 *
1047
 * @return CE_None on success or CE_Failure if an error occurs.
1048
 */
1049
1050
CPLErr GDALWarpKernel::PerformWarp()
1051
1052
0
{
1053
0
    const CPLErr eErr = Validate();
1054
1055
0
    if (eErr != CE_None)
1056
0
        return eErr;
1057
1058
    // See #2445 and #3079.
1059
0
    if (nSrcXSize <= 0 || nSrcYSize <= 0)
1060
0
    {
1061
0
        if (!pfnProgress(dfProgressBase + dfProgressScale, "", pProgress))
1062
0
        {
1063
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1064
0
            return CE_Failure;
1065
0
        }
1066
0
        return CE_None;
1067
0
    }
1068
1069
    /* -------------------------------------------------------------------- */
1070
    /*      Pre-calculate resampling scales and window sizes for filtering. */
1071
    /* -------------------------------------------------------------------- */
1072
1073
0
    dfXScale = 0.0;
1074
0
    dfYScale = 0.0;
1075
1076
    // XSCALE and YSCALE per warping chunk is not necessarily ideal, in case of
1077
    // heterogeneous change in shapes.
1078
    // Best would probably be a per-pixel scale computation.
1079
0
    const char *pszXScale = CSLFetchNameValue(papszWarpOptions, "XSCALE");
1080
0
    const char *pszYScale = CSLFetchNameValue(papszWarpOptions, "YSCALE");
1081
0
    if (!pszXScale || !pszYScale)
1082
0
    {
1083
        // Sample points along a grid in the destination space
1084
0
        constexpr int MAX_POINTS_PER_DIM = 10;
1085
0
        const int nPointsX = std::min(MAX_POINTS_PER_DIM, nDstXSize);
1086
0
        const int nPointsY = std::min(MAX_POINTS_PER_DIM, nDstYSize);
1087
0
        constexpr int CORNER_COUNT_PER_SQUARE = 4;
1088
0
        const int nPoints = CORNER_COUNT_PER_SQUARE * nPointsX * nPointsY;
1089
0
        std::vector<double> adfX;
1090
0
        std::vector<double> adfY;
1091
0
        adfX.reserve(CORNER_COUNT_PER_SQUARE * nPoints);
1092
0
        adfY.reserve(CORNER_COUNT_PER_SQUARE * nPoints);
1093
0
        std::vector<double> adfZ(CORNER_COUNT_PER_SQUARE * nPoints);
1094
0
        std::vector<int> abSuccess(CORNER_COUNT_PER_SQUARE * nPoints);
1095
0
        for (int iY = 0; iY < nPointsY; iY++)
1096
0
        {
1097
0
            const double dfYShift = (iY > 0 && iY == nPointsY - 1) ? -1.0 : 0.0;
1098
0
            const double dfY =
1099
0
                dfYShift + (nPointsY == 1 ? 0.0
1100
0
                                          : static_cast<double>(iY) *
1101
0
                                                nDstYSize / (nPointsY - 1));
1102
1103
0
            for (int iX = 0; iX < nPointsX; iX++)
1104
0
            {
1105
0
                const double dfXShift =
1106
0
                    (iX > 0 && iX == nPointsX - 1) ? -1.0 : 0.0;
1107
1108
0
                const double dfX =
1109
0
                    dfXShift + (nPointsX == 1 ? 0.0
1110
0
                                              : static_cast<double>(iX) *
1111
0
                                                    nDstXSize / (nPointsX - 1));
1112
1113
                // Reproject a unit square at each sample point
1114
0
                adfX.push_back(dfX);
1115
0
                adfY.push_back(dfY);
1116
1117
0
                adfX.push_back(dfX + 1);
1118
0
                adfY.push_back(dfY);
1119
1120
0
                adfX.push_back(dfX);
1121
0
                adfY.push_back(dfY + 1);
1122
1123
0
                adfX.push_back(dfX + 1);
1124
0
                adfY.push_back(dfY + 1);
1125
0
            }
1126
0
        }
1127
0
        pfnTransformer(pTransformerArg, TRUE, static_cast<int>(adfX.size()),
1128
0
                       adfX.data(), adfY.data(), adfZ.data(), abSuccess.data());
1129
1130
0
        std::vector<XYPair> adfXYScales;
1131
0
        adfXYScales.reserve(nPoints);
1132
0
        for (int i = 0; i < nPoints; i += CORNER_COUNT_PER_SQUARE)
1133
0
        {
1134
0
            if (abSuccess[i + 0] && abSuccess[i + 1] && abSuccess[i + 2] &&
1135
0
                abSuccess[i + 3])
1136
0
            {
1137
0
                const auto square = [](double x) { return x * x; };
1138
1139
0
                const double vx01 = adfX[i + 1] - adfX[i + 0];
1140
0
                const double vy01 = adfY[i + 1] - adfY[i + 0];
1141
0
                const double len01_sq = square(vx01) + square(vy01);
1142
1143
0
                const double vx23 = adfX[i + 3] - adfX[i + 2];
1144
0
                const double vy23 = adfY[i + 3] - adfY[i + 2];
1145
0
                const double len23_sq = square(vx23) + square(vy23);
1146
1147
0
                const double vx02 = adfX[i + 2] - adfX[i + 0];
1148
0
                const double vy02 = adfY[i + 2] - adfY[i + 0];
1149
0
                const double len02_sq = square(vx02) + square(vy02);
1150
1151
0
                const double vx13 = adfX[i + 3] - adfX[i + 1];
1152
0
                const double vy13 = adfY[i + 3] - adfY[i + 1];
1153
0
                const double len13_sq = square(vx13) + square(vy13);
1154
1155
                // ~ 20 degree, heuristic
1156
0
                constexpr double TAN_MODEST_ANGLE = 0.35;
1157
1158
                // 10%, heuristic
1159
0
                constexpr double LENGTH_RELATIVE_TOLERANCE = 0.1;
1160
1161
                // Security margin to avoid division by zero (would only
1162
                // happen in case of degenerated coordinate transformation,
1163
                // or insane upsampling)
1164
0
                constexpr double EPSILON = 1e-10;
1165
1166
                // Does the transformed square looks like an almost non-rotated
1167
                // quasi-rectangle ?
1168
0
                if (std::fabs(vy01) < TAN_MODEST_ANGLE * vx01 &&
1169
0
                    std::fabs(vy23) < TAN_MODEST_ANGLE * vx23 &&
1170
0
                    std::fabs(len01_sq - len23_sq) <
1171
0
                        LENGTH_RELATIVE_TOLERANCE * std::fabs(len01_sq) &&
1172
0
                    std::fabs(len02_sq - len13_sq) <
1173
0
                        LENGTH_RELATIVE_TOLERANCE * std::fabs(len02_sq))
1174
0
                {
1175
                    // Using a geometric average here of lenAB_sq and lenCD_sq,
1176
                    // hence a sqrt(), and as this is still a squared value,
1177
                    // we need another sqrt() to get a distance.
1178
0
                    const double dfXLength =
1179
0
                        std::sqrt(std::sqrt(len01_sq * len23_sq));
1180
0
                    const double dfYLength =
1181
0
                        std::sqrt(std::sqrt(len02_sq * len13_sq));
1182
0
                    if (dfXLength > EPSILON && dfYLength > EPSILON)
1183
0
                    {
1184
0
                        const double dfThisXScale = 1.0 / dfXLength;
1185
0
                        const double dfThisYScale = 1.0 / dfYLength;
1186
0
                        adfXYScales.push_back({dfThisXScale, dfThisYScale});
1187
0
                    }
1188
0
                }
1189
0
                else
1190
0
                {
1191
                    // If not, then consider the area of the transformed unit
1192
                    // square to determine the X/Y scales.
1193
0
                    const XYPoly poly{{adfX[i + 0], adfY[i + 0]},
1194
0
                                      {adfX[i + 1], adfY[i + 1]},
1195
0
                                      {adfX[i + 3], adfY[i + 3]},
1196
0
                                      {adfX[i + 2], adfY[i + 2]}};
1197
0
                    const double dfSrcArea = getArea(poly);
1198
0
                    const double dfFactor = std::sqrt(dfSrcArea);
1199
0
                    if (dfFactor > EPSILON)
1200
0
                    {
1201
0
                        const double dfThisXScale = 1.0 / dfFactor;
1202
0
                        const double dfThisYScale = dfThisXScale;
1203
0
                        adfXYScales.push_back({dfThisXScale, dfThisYScale});
1204
0
                    }
1205
0
                }
1206
0
            }
1207
0
        }
1208
1209
0
        if (!adfXYScales.empty())
1210
0
        {
1211
            // Sort by increasing xscale * yscale
1212
0
            std::sort(adfXYScales.begin(), adfXYScales.end(),
1213
0
                      [](const XYPair &a, const XYPair &b)
1214
0
                      { return a.first * a.second < b.first * b.second; });
1215
1216
            // Compute the per-axis maximum of scale
1217
0
            double dfXMax = 0;
1218
0
            double dfYMax = 0;
1219
0
            for (const auto &[dfX, dfY] : adfXYScales)
1220
0
            {
1221
0
                dfXMax = std::max(dfXMax, dfX);
1222
0
                dfYMax = std::max(dfYMax, dfY);
1223
0
            }
1224
1225
            // Now eliminate outliers, defined as ones whose value is < 10% of
1226
            // the maximum value, typically found at a polar discontinuity, and
1227
            // compute the average of non-outlier values.
1228
0
            dfXScale = 0;
1229
0
            dfYScale = 0;
1230
0
            int i = 0;
1231
0
            constexpr double THRESHOLD = 0.1;  // 10%, rather arbitrary
1232
0
            for (const auto &[dfX, dfY] : adfXYScales)
1233
0
            {
1234
0
                if (dfX > THRESHOLD * dfXMax && dfY > THRESHOLD * dfYMax)
1235
0
                {
1236
0
                    ++i;
1237
0
                    const double dfXDelta = dfX - dfXScale;
1238
0
                    const double dfYDelta = dfY - dfYScale;
1239
0
                    const double dfInvI = 1.0 / i;
1240
0
                    dfXScale += dfXDelta * dfInvI;
1241
0
                    dfYScale += dfYDelta * dfInvI;
1242
0
                }
1243
0
            }
1244
0
        }
1245
0
    }
1246
1247
    // Round to closest integer reciprocal scale if we are very close to it
1248
0
    const auto RoundToClosestIntegerReciprocalScaleIfCloseEnough =
1249
0
        [](double dfScale)
1250
0
    {
1251
0
        if (dfScale < 1.0)
1252
0
        {
1253
0
            double dfReciprocalScale = 1.0 / dfScale;
1254
0
            const int nReciprocalScale =
1255
0
                static_cast<int>(dfReciprocalScale + 0.5);
1256
0
            if (fabs(dfReciprocalScale - nReciprocalScale) < 0.05)
1257
0
                dfScale = 1.0 / nReciprocalScale;
1258
0
        }
1259
0
        return dfScale;
1260
0
    };
1261
1262
0
    if (dfXScale <= 0)
1263
0
        dfXScale = 1.0;
1264
0
    if (dfYScale <= 0)
1265
0
        dfYScale = 1.0;
1266
1267
0
    dfXScale = RoundToClosestIntegerReciprocalScaleIfCloseEnough(dfXScale);
1268
0
    dfYScale = RoundToClosestIntegerReciprocalScaleIfCloseEnough(dfYScale);
1269
1270
0
    if (pszXScale != nullptr)
1271
0
        dfXScale = CPLAtof(pszXScale);
1272
0
    if (pszYScale != nullptr)
1273
0
        dfYScale = CPLAtof(pszYScale);
1274
1275
0
    if (!pszXScale || !pszYScale)
1276
0
        CPLDebug("WARP", "dfXScale = %f, dfYScale = %f", dfXScale, dfYScale);
1277
1278
0
    const int bUse4SamplesFormula = CanUse4SamplesFormula(this);
1279
1280
    // Safety check for callers that would use GDALWarpKernel without using
1281
    // GDALWarpOperation.
1282
0
    if ((eResample == GRA_CubicSpline || eResample == GRA_Lanczos ||
1283
0
         ((eResample == GRA_Cubic || eResample == GRA_Bilinear) &&
1284
0
          !bUse4SamplesFormula)) &&
1285
0
        atoi(CSLFetchNameValueDef(papszWarpOptions, "EXTRA_ELTS", "0")) !=
1286
0
            WARP_EXTRA_ELTS)
1287
0
    {
1288
0
        CPLError(CE_Failure, CPLE_AppDefined,
1289
0
                 "Source arrays must have WARP_EXTRA_ELTS extra elements at "
1290
0
                 "their end. "
1291
0
                 "See GDALWarpKernel class definition. If this condition is "
1292
0
                 "fulfilled, define a EXTRA_ELTS=%d warp options",
1293
0
                 WARP_EXTRA_ELTS);
1294
0
        return CE_Failure;
1295
0
    }
1296
1297
0
    dfXFilter = anGWKFilterRadius[eResample];
1298
0
    dfYFilter = anGWKFilterRadius[eResample];
1299
1300
0
    nXRadius = dfXScale < 1.0 ? static_cast<int>(ceil(dfXFilter / dfXScale))
1301
0
                              : static_cast<int>(dfXFilter);
1302
0
    nYRadius = dfYScale < 1.0 ? static_cast<int>(ceil(dfYFilter / dfYScale))
1303
0
                              : static_cast<int>(dfYFilter);
1304
1305
    // Filter window offset depends on the parity of the kernel radius.
1306
0
    nFiltInitX = ((anGWKFilterRadius[eResample] + 1) % 2) - nXRadius;
1307
0
    nFiltInitY = ((anGWKFilterRadius[eResample] + 1) % 2) - nYRadius;
1308
1309
0
    bApplyVerticalShift =
1310
0
        CPLFetchBool(papszWarpOptions, "APPLY_VERTICAL_SHIFT", false);
1311
0
    dfMultFactorVerticalShift = CPLAtof(CSLFetchNameValueDef(
1312
0
        papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT", "1.0"));
1313
1314
    /* -------------------------------------------------------------------- */
1315
    /*      Set up resampling functions.                                    */
1316
    /* -------------------------------------------------------------------- */
1317
0
    if (CPLFetchBool(papszWarpOptions, "USE_GENERAL_CASE", false))
1318
0
        return GWKGeneralCase(this);
1319
1320
0
    const bool bNoMasksOrDstDensityOnly =
1321
0
        papanBandSrcValid == nullptr && panUnifiedSrcValid == nullptr &&
1322
0
        pafUnifiedSrcDensity == nullptr && panDstValid == nullptr;
1323
1324
0
    if (eWorkingDataType == GDT_UInt8 && eResample == GRA_NearestNeighbour &&
1325
0
        bNoMasksOrDstDensityOnly)
1326
0
        return GWKNearestNoMasksOrDstDensityOnlyByte(this);
1327
1328
0
    if (eWorkingDataType == GDT_UInt8 && eResample == GRA_Bilinear &&
1329
0
        bNoMasksOrDstDensityOnly)
1330
0
        return GWKBilinearNoMasksOrDstDensityOnlyByte(this);
1331
1332
0
    if (eWorkingDataType == GDT_UInt8 && eResample == GRA_Cubic &&
1333
0
        bNoMasksOrDstDensityOnly)
1334
0
        return GWKCubicNoMasksOrDstDensityOnlyByte(this);
1335
1336
0
    if (eWorkingDataType == GDT_UInt8 && eResample == GRA_CubicSpline &&
1337
0
        bNoMasksOrDstDensityOnly)
1338
0
        return GWKCubicSplineNoMasksOrDstDensityOnlyByte(this);
1339
1340
0
    if (eWorkingDataType == GDT_UInt8 && eResample == GRA_NearestNeighbour)
1341
0
        return GWKNearestByte(this);
1342
1343
0
    if ((eWorkingDataType == GDT_Int16 || eWorkingDataType == GDT_UInt16) &&
1344
0
        eResample == GRA_NearestNeighbour && bNoMasksOrDstDensityOnly)
1345
0
        return GWKNearestNoMasksOrDstDensityOnlyShort(this);
1346
1347
0
    if ((eWorkingDataType == GDT_Int16) && eResample == GRA_Cubic &&
1348
0
        bNoMasksOrDstDensityOnly)
1349
0
        return GWKCubicNoMasksOrDstDensityOnlyShort(this);
1350
1351
0
    if ((eWorkingDataType == GDT_Int16) && eResample == GRA_CubicSpline &&
1352
0
        bNoMasksOrDstDensityOnly)
1353
0
        return GWKCubicSplineNoMasksOrDstDensityOnlyShort(this);
1354
1355
0
    if ((eWorkingDataType == GDT_Int16) && eResample == GRA_Bilinear &&
1356
0
        bNoMasksOrDstDensityOnly)
1357
0
        return GWKBilinearNoMasksOrDstDensityOnlyShort(this);
1358
1359
0
    if ((eWorkingDataType == GDT_UInt16) && eResample == GRA_Cubic &&
1360
0
        bNoMasksOrDstDensityOnly)
1361
0
        return GWKCubicNoMasksOrDstDensityOnlyUShort(this);
1362
1363
0
    if ((eWorkingDataType == GDT_UInt16) && eResample == GRA_CubicSpline &&
1364
0
        bNoMasksOrDstDensityOnly)
1365
0
        return GWKCubicSplineNoMasksOrDstDensityOnlyUShort(this);
1366
1367
0
    if ((eWorkingDataType == GDT_UInt16) && eResample == GRA_Bilinear &&
1368
0
        bNoMasksOrDstDensityOnly)
1369
0
        return GWKBilinearNoMasksOrDstDensityOnlyUShort(this);
1370
1371
0
    if (eWorkingDataType == GDT_Int16 && eResample == GRA_NearestNeighbour)
1372
0
        return GWKNearestShort(this);
1373
1374
0
    if (eWorkingDataType == GDT_UInt16 && eResample == GRA_NearestNeighbour)
1375
0
        return GWKNearestUnsignedShort(this);
1376
1377
0
    if (eWorkingDataType == GDT_Float32 && eResample == GRA_NearestNeighbour &&
1378
0
        bNoMasksOrDstDensityOnly)
1379
0
        return GWKNearestNoMasksOrDstDensityOnlyFloat(this);
1380
1381
0
    if (eWorkingDataType == GDT_Float32 && eResample == GRA_NearestNeighbour)
1382
0
        return GWKNearestFloat(this);
1383
1384
0
    if (eWorkingDataType == GDT_Float32 && eResample == GRA_Bilinear &&
1385
0
        bNoMasksOrDstDensityOnly)
1386
0
        return GWKBilinearNoMasksOrDstDensityOnlyFloat(this);
1387
1388
0
    if (eWorkingDataType == GDT_Float32 && eResample == GRA_Cubic &&
1389
0
        bNoMasksOrDstDensityOnly)
1390
0
        return GWKCubicNoMasksOrDstDensityOnlyFloat(this);
1391
1392
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
1393
    if (eWorkingDataType == GDT_Float64 && eResample == GRA_Bilinear &&
1394
        bNoMasksOrDstDensityOnly)
1395
        return GWKBilinearNoMasksOrDstDensityOnlyDouble(this);
1396
1397
    if (eWorkingDataType == GDT_Float64 && eResample == GRA_Cubic &&
1398
        bNoMasksOrDstDensityOnly)
1399
        return GWKCubicNoMasksOrDstDensityOnlyDouble(this);
1400
#endif
1401
1402
0
    if (eResample == GRA_Average)
1403
0
        return GWKAverageOrMode(this);
1404
1405
0
    if (eResample == GRA_RMS)
1406
0
        return GWKAverageOrMode(this);
1407
1408
0
    if (eResample == GRA_Mode)
1409
0
        return GWKAverageOrMode(this);
1410
1411
0
    if (eResample == GRA_Max)
1412
0
        return GWKAverageOrMode(this);
1413
1414
0
    if (eResample == GRA_Min)
1415
0
        return GWKAverageOrMode(this);
1416
1417
0
    if (eResample == GRA_Med)
1418
0
        return GWKAverageOrMode(this);
1419
1420
0
    if (eResample == GRA_Q1)
1421
0
        return GWKAverageOrMode(this);
1422
1423
0
    if (eResample == GRA_Q3)
1424
0
        return GWKAverageOrMode(this);
1425
1426
0
    if (eResample == GRA_Sum)
1427
0
        return GWKSumPreserving(this);
1428
1429
0
    if (!GDALDataTypeIsComplex(eWorkingDataType))
1430
0
    {
1431
0
        return GWKRealCase(this);
1432
0
    }
1433
1434
0
    return GWKGeneralCase(this);
1435
0
}
1436
1437
/************************************************************************/
1438
/*                              Validate()                              */
1439
/************************************************************************/
1440
1441
/**
1442
 * \fn CPLErr GDALWarpKernel::Validate()
1443
 *
1444
 * Check the settings in the GDALWarpKernel, and issue a CPLError()
1445
 * (and return CE_Failure) if the configuration is considered to be
1446
 * invalid for some reason.
1447
 *
1448
 * This method will also do some standard defaulting such as setting
1449
 * pfnProgress to GDALDummyProgress() if it is NULL.
1450
 *
1451
 * @return CE_None on success or CE_Failure if an error is detected.
1452
 */
1453
1454
CPLErr GDALWarpKernel::Validate()
1455
1456
0
{
1457
0
    if (static_cast<size_t>(eResample) >=
1458
0
        (sizeof(anGWKFilterRadius) / sizeof(anGWKFilterRadius[0])))
1459
0
    {
1460
0
        CPLError(CE_Failure, CPLE_AppDefined,
1461
0
                 "Unsupported resampling method %d.",
1462
0
                 static_cast<int>(eResample));
1463
0
        return CE_Failure;
1464
0
    }
1465
1466
    // Tuples of values (e.g. "<R>,<G>,<B>" or "(<R1>,<G1>,<B1>),(<R2>,<G2>,<B2>)") that must
1467
    // be ignored as contributing source pixels during resampling. Only taken into account by
1468
    // Average currently
1469
0
    const char *pszExcludedValues =
1470
0
        CSLFetchNameValue(papszWarpOptions, "EXCLUDED_VALUES");
1471
0
    if (pszExcludedValues)
1472
0
    {
1473
0
        const CPLStringList aosTokens(
1474
0
            CSLTokenizeString2(pszExcludedValues, "(,)", 0));
1475
0
        if ((aosTokens.size() % nBands) != 0)
1476
0
        {
1477
0
            CPLError(CE_Failure, CPLE_AppDefined,
1478
0
                     "EXCLUDED_VALUES should contain one or several tuples of "
1479
0
                     "%d values formatted like <R>,<G>,<B> or "
1480
0
                     "(<R1>,<G1>,<B1>),(<R2>,<G2>,<B2>) if there are multiple "
1481
0
                     "tuples",
1482
0
                     nBands);
1483
0
            return CE_Failure;
1484
0
        }
1485
0
        std::vector<double> adfTuple;
1486
0
        for (int i = 0; i < aosTokens.size(); ++i)
1487
0
        {
1488
0
            adfTuple.push_back(CPLAtof(aosTokens[i]));
1489
0
            if (((i + 1) % nBands) == 0)
1490
0
            {
1491
0
                m_aadfExcludedValues.push_back(adfTuple);
1492
0
                adfTuple.clear();
1493
0
            }
1494
0
        }
1495
0
    }
1496
1497
0
    return CE_None;
1498
0
}
1499
1500
/************************************************************************/
1501
/*                         GWKOverlayDensity()                          */
1502
/*                                                                      */
1503
/*      Compute the final density for the destination pixel.  This      */
1504
/*      is a function of the overlay density (passed in) and the        */
1505
/*      original density.                                               */
1506
/************************************************************************/
1507
1508
static void GWKOverlayDensity(const GDALWarpKernel *poWK, GPtrDiff_t iDstOffset,
1509
                              double dfDensity)
1510
0
{
1511
0
    if (dfDensity < 0.0001 || poWK->pafDstDensity == nullptr)
1512
0
        return;
1513
1514
0
    poWK->pafDstDensity[iDstOffset] =
1515
0
        1.0f -
1516
0
        (1.0f - float(dfDensity)) * (1.0f - poWK->pafDstDensity[iDstOffset]);
1517
0
}
1518
1519
/************************************************************************/
1520
/*                           GWKRoundValueT()                           */
1521
/************************************************************************/
1522
1523
template <class T, class U, bool is_signed> struct sGWKRoundValueT
1524
{
1525
    static T eval(U);
1526
};
1527
1528
template <class T, class U> struct sGWKRoundValueT<T, U, true> /* signed */
1529
{
1530
    static T eval(U value)
1531
0
    {
1532
0
        return static_cast<T>(floor(value + U(0.5)));
1533
0
    }
Unexecuted instantiation: sGWKRoundValueT<short, double, true>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<int, double, true>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<long, double, true>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<cpl::Float16, double, true>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<double, double, true>::eval(double)
1534
};
1535
1536
template <class T, class U> struct sGWKRoundValueT<T, U, false> /* unsigned */
1537
{
1538
    static T eval(U value)
1539
0
    {
1540
0
        return static_cast<T>(value + U(0.5));
1541
0
    }
Unexecuted instantiation: sGWKRoundValueT<unsigned char, double, false>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<unsigned char, float, false>::eval(float)
Unexecuted instantiation: sGWKRoundValueT<unsigned short, double, false>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<unsigned short, float, false>::eval(float)
Unexecuted instantiation: sGWKRoundValueT<unsigned int, double, false>::eval(double)
Unexecuted instantiation: sGWKRoundValueT<unsigned long, double, false>::eval(double)
1542
};
1543
1544
template <class T, class U> static T GWKRoundValueT(U value)
1545
0
{
1546
0
    return sGWKRoundValueT<T, U, cpl::NumericLimits<T>::is_signed>::eval(value);
1547
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned char GWKRoundValueT<unsigned char, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned char GWKRoundValueT<unsigned char, float>(float)
Unexecuted instantiation: gdalwarpkernel.cpp:short GWKRoundValueT<short, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned short GWKRoundValueT<unsigned short, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned short GWKRoundValueT<unsigned short, float>(float)
Unexecuted instantiation: gdalwarpkernel.cpp:int GWKRoundValueT<int, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned int GWKRoundValueT<unsigned int, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:long GWKRoundValueT<long, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned long GWKRoundValueT<unsigned long, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:cpl::Float16 GWKRoundValueT<cpl::Float16, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:double GWKRoundValueT<double, double>(double)
1548
1549
template <> float GWKRoundValueT<float, double>(double value)
1550
0
{
1551
0
    return static_cast<float>(value);
1552
0
}
1553
1554
#ifdef notused
1555
template <> double GWKRoundValueT<double, double>(double value)
1556
{
1557
    return value;
1558
}
1559
#endif
1560
1561
/************************************************************************/
1562
/*                           GWKClampValueT()                           */
1563
/************************************************************************/
1564
1565
template <class T, class U> static CPL_INLINE T GWKClampValueT(U value)
1566
0
{
1567
0
    if (value < static_cast<U>(cpl::NumericLimits<T>::min()))
1568
0
        return cpl::NumericLimits<T>::min();
1569
0
    else if (value > static_cast<U>(cpl::NumericLimits<T>::max()))
1570
0
        return cpl::NumericLimits<T>::max();
1571
0
    else
1572
0
        return GWKRoundValueT<T, U>(value);
1573
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned char GWKClampValueT<unsigned char, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned char GWKClampValueT<unsigned char, float>(float)
Unexecuted instantiation: gdalwarpkernel.cpp:short GWKClampValueT<short, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned short GWKClampValueT<unsigned short, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned short GWKClampValueT<unsigned short, float>(float)
Unexecuted instantiation: gdalwarpkernel.cpp:int GWKClampValueT<int, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned int GWKClampValueT<unsigned int, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:long GWKClampValueT<long, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:unsigned long GWKClampValueT<unsigned long, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:cpl::Float16 GWKClampValueT<cpl::Float16, double>(double)
Unexecuted instantiation: gdalwarpkernel.cpp:double GWKClampValueT<double, double>(double)
1574
1575
template <> float GWKClampValueT<float, double>(double dfValue)
1576
0
{
1577
0
    return static_cast<float>(dfValue);
1578
0
}
1579
1580
#ifdef notused
1581
template <> double GWKClampValueT<double, double>(double dfValue)
1582
{
1583
    return dfValue;
1584
}
1585
#endif
1586
1587
/************************************************************************/
1588
/*                            AvoidNoData()                             */
1589
/************************************************************************/
1590
1591
template <class T> inline void AvoidNoData(T *pDst, GPtrDiff_t iDstOffset)
1592
0
{
1593
    if constexpr (cpl::NumericLimits<T>::is_integer)
1594
0
    {
1595
0
        if (pDst[iDstOffset] == static_cast<T>(cpl::NumericLimits<T>::lowest()))
1596
0
        {
1597
0
            pDst[iDstOffset] =
1598
0
                static_cast<T>(cpl::NumericLimits<T>::lowest() + 1);
1599
0
        }
1600
0
        else
1601
0
            pDst[iDstOffset]--;
1602
    }
1603
    else
1604
0
    {
1605
0
        if (pDst[iDstOffset] == cpl::NumericLimits<T>::max())
1606
0
        {
1607
0
            using std::nextafter;
1608
0
            pDst[iDstOffset] = nextafter(pDst[iDstOffset], static_cast<T>(0));
1609
0
        }
1610
0
        else
1611
0
        {
1612
0
            using std::nextafter;
1613
0
            pDst[iDstOffset] =
1614
0
                nextafter(pDst[iDstOffset], cpl::NumericLimits<T>::max());
1615
0
        }
1616
0
    }
1617
0
}
Unexecuted instantiation: void AvoidNoData<unsigned char>(unsigned char*, long long)
Unexecuted instantiation: void AvoidNoData<signed char>(signed char*, long long)
Unexecuted instantiation: void AvoidNoData<short>(short*, long long)
Unexecuted instantiation: void AvoidNoData<unsigned short>(unsigned short*, long long)
Unexecuted instantiation: void AvoidNoData<unsigned int>(unsigned int*, long long)
Unexecuted instantiation: void AvoidNoData<int>(int*, long long)
Unexecuted instantiation: void AvoidNoData<unsigned long>(unsigned long*, long long)
Unexecuted instantiation: void AvoidNoData<long>(long*, long long)
Unexecuted instantiation: void AvoidNoData<cpl::Float16>(cpl::Float16*, long long)
Unexecuted instantiation: void AvoidNoData<float>(float*, long long)
Unexecuted instantiation: void AvoidNoData<double>(double*, long long)
1618
1619
/************************************************************************/
1620
/*                            AvoidNoData()                             */
1621
/************************************************************************/
1622
1623
template <class T>
1624
inline void AvoidNoData(const GDALWarpKernel *poWK, int iBand,
1625
                        GPtrDiff_t iDstOffset)
1626
0
{
1627
0
    GByte *pabyDst = poWK->papabyDstImage[iBand];
1628
0
    T *pDst = reinterpret_cast<T *>(pabyDst);
1629
1630
0
    if (poWK->padfDstNoDataReal != nullptr &&
1631
0
        poWK->padfDstNoDataReal[iBand] == static_cast<double>(pDst[iDstOffset]))
1632
0
    {
1633
0
        AvoidNoData(pDst, iDstOffset);
1634
1635
0
        if (!poWK->bWarnedAboutDstNoDataReplacement)
1636
0
        {
1637
0
            const_cast<GDALWarpKernel *>(poWK)
1638
0
                ->bWarnedAboutDstNoDataReplacement = true;
1639
0
            CPLError(CE_Warning, CPLE_AppDefined,
1640
0
                     "Value %g in the source dataset has been changed to %g "
1641
0
                     "in the destination dataset to avoid being treated as "
1642
0
                     "NoData. To avoid this, select a different NoData value "
1643
0
                     "for the destination dataset.",
1644
0
                     poWK->padfDstNoDataReal[iBand],
1645
0
                     static_cast<double>(pDst[iDstOffset]));
1646
0
        }
1647
0
    }
1648
0
}
Unexecuted instantiation: void AvoidNoData<unsigned char>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<signed char>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<short>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<unsigned short>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<unsigned int>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<int>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<unsigned long>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<long>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<cpl::Float16>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<float>(GDALWarpKernel const*, int, long long)
Unexecuted instantiation: void AvoidNoData<double>(GDALWarpKernel const*, int, long long)
1649
1650
/************************************************************************/
1651
/*                      GWKAvoidNoDataMultiBand()                       */
1652
/************************************************************************/
1653
1654
template <class T>
1655
static void GWKAvoidNoDataMultiBand(const GDALWarpKernel *poWK,
1656
                                    GPtrDiff_t iDstOffset)
1657
0
{
1658
0
    T **ppDst = reinterpret_cast<T **>(poWK->papabyDstImage);
1659
0
    if (poWK->padfDstNoDataReal != nullptr)
1660
0
    {
1661
0
        for (int iBand = 0; iBand < poWK->nBands; ++iBand)
1662
0
        {
1663
0
            if (poWK->padfDstNoDataReal[iBand] !=
1664
0
                static_cast<double>(ppDst[iBand][iDstOffset]))
1665
0
                return;
1666
0
        }
1667
0
        for (int iBand = 0; iBand < poWK->nBands; ++iBand)
1668
0
        {
1669
0
            AvoidNoData(ppDst[iBand], iDstOffset);
1670
0
        }
1671
1672
0
        if (!poWK->bWarnedAboutDstNoDataReplacement)
1673
0
        {
1674
0
            const_cast<GDALWarpKernel *>(poWK)
1675
0
                ->bWarnedAboutDstNoDataReplacement = true;
1676
0
            std::string valueSrc, valueDst;
1677
0
            for (int iBand = 0; iBand < poWK->nBands; ++iBand)
1678
0
            {
1679
0
                if (!valueSrc.empty())
1680
0
                {
1681
0
                    valueSrc += ',';
1682
0
                    valueDst += ',';
1683
0
                }
1684
0
                valueSrc += CPLSPrintf("%g", poWK->padfDstNoDataReal[iBand]);
1685
0
                valueDst += CPLSPrintf(
1686
0
                    "%g", static_cast<double>(ppDst[iBand][iDstOffset]));
1687
0
            }
1688
0
            CPLError(CE_Warning, CPLE_AppDefined,
1689
0
                     "Value %s in the source dataset has been changed to %s "
1690
0
                     "in the destination dataset to avoid being treated as "
1691
0
                     "NoData. To avoid this, select a different NoData value "
1692
0
                     "for the destination dataset.",
1693
0
                     valueSrc.c_str(), valueDst.c_str());
1694
0
        }
1695
0
    }
1696
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<unsigned char>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<signed char>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<short>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<unsigned short>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<int>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<unsigned int>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<long>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<unsigned long>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<cpl::Float16>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<float>(GDALWarpKernel const*, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKAvoidNoDataMultiBand<double>(GDALWarpKernel const*, long long)
1697
1698
/************************************************************************/
1699
/*                      GWKAvoidNoDataMultiBand()                       */
1700
/************************************************************************/
1701
1702
static void GWKAvoidNoDataMultiBand(const GDALWarpKernel *poWK,
1703
                                    GPtrDiff_t iDstOffset)
1704
0
{
1705
0
    switch (poWK->eWorkingDataType)
1706
0
    {
1707
0
        case GDT_UInt8:
1708
0
            GWKAvoidNoDataMultiBand<std::uint8_t>(poWK, iDstOffset);
1709
0
            break;
1710
1711
0
        case GDT_Int8:
1712
0
            GWKAvoidNoDataMultiBand<std::int8_t>(poWK, iDstOffset);
1713
0
            break;
1714
1715
0
        case GDT_Int16:
1716
0
            GWKAvoidNoDataMultiBand<std::int16_t>(poWK, iDstOffset);
1717
0
            break;
1718
1719
0
        case GDT_UInt16:
1720
0
            GWKAvoidNoDataMultiBand<std::uint16_t>(poWK, iDstOffset);
1721
0
            break;
1722
1723
0
        case GDT_Int32:
1724
0
            GWKAvoidNoDataMultiBand<std::int32_t>(poWK, iDstOffset);
1725
0
            break;
1726
1727
0
        case GDT_UInt32:
1728
0
            GWKAvoidNoDataMultiBand<std::uint32_t>(poWK, iDstOffset);
1729
0
            break;
1730
1731
0
        case GDT_Int64:
1732
0
            GWKAvoidNoDataMultiBand<std::int64_t>(poWK, iDstOffset);
1733
0
            break;
1734
1735
0
        case GDT_UInt64:
1736
0
            GWKAvoidNoDataMultiBand<std::uint64_t>(poWK, iDstOffset);
1737
0
            break;
1738
1739
0
        case GDT_Float16:
1740
0
            GWKAvoidNoDataMultiBand<GFloat16>(poWK, iDstOffset);
1741
0
            break;
1742
1743
0
        case GDT_Float32:
1744
0
            GWKAvoidNoDataMultiBand<float>(poWK, iDstOffset);
1745
0
            break;
1746
1747
0
        case GDT_Float64:
1748
0
            GWKAvoidNoDataMultiBand<double>(poWK, iDstOffset);
1749
0
            break;
1750
1751
0
        case GDT_CInt16:
1752
0
        case GDT_CInt32:
1753
0
        case GDT_CFloat16:
1754
0
        case GDT_CFloat32:
1755
0
        case GDT_CFloat64:
1756
0
        case GDT_Unknown:
1757
0
        case GDT_TypeCount:
1758
0
            break;
1759
0
    }
1760
0
}
1761
1762
/************************************************************************/
1763
/*                       GWKSetPixelValueRealT()                        */
1764
/************************************************************************/
1765
1766
template <class T>
1767
static bool GWKSetPixelValueRealT(const GDALWarpKernel *poWK, int iBand,
1768
                                  GPtrDiff_t iDstOffset, double dfDensity,
1769
                                  T value, bool bAvoidNoDataSingleBand)
1770
0
{
1771
0
    T *pDst = reinterpret_cast<T *>(poWK->papabyDstImage[iBand]);
1772
1773
    /* -------------------------------------------------------------------- */
1774
    /*      If the source density is less than 100% we need to fetch the    */
1775
    /*      existing destination value, and mix it with the source to       */
1776
    /*      get the new "to apply" value.  Also compute composite           */
1777
    /*      density.                                                        */
1778
    /*                                                                      */
1779
    /*      We avoid mixing if density is very near one or risk mixing      */
1780
    /*      in very extreme nodata values and causing odd results (#1610)   */
1781
    /* -------------------------------------------------------------------- */
1782
0
    if (dfDensity < 0.9999)
1783
0
    {
1784
0
        if (dfDensity < 0.0001)
1785
0
            return true;
1786
1787
0
        double dfDstDensity = 1.0;
1788
1789
0
        if (poWK->pafDstDensity != nullptr)
1790
0
            dfDstDensity = double(poWK->pafDstDensity[iDstOffset]);
1791
0
        else if (poWK->panDstValid != nullptr &&
1792
0
                 !CPLMaskGet(poWK->panDstValid, iDstOffset))
1793
0
            dfDstDensity = 0.0;
1794
1795
        // It seems like we also ought to be testing panDstValid[] here!
1796
1797
0
        const double dfDstReal = static_cast<double>(pDst[iDstOffset]);
1798
1799
        // The destination density is really only relative to the portion
1800
        // not occluded by the overlay.
1801
0
        const double dfDstInfluence = (1.0 - dfDensity) * dfDstDensity;
1802
1803
0
        const double dfReal =
1804
0
            (double(value) * dfDensity + dfDstReal * dfDstInfluence) /
1805
0
            (dfDensity + dfDstInfluence);
1806
1807
        /* --------------------------------------------------------------------
1808
         */
1809
        /*      Actually apply the destination value. */
1810
        /*                                                                      */
1811
        /*      Avoid using the destination nodata value for integer datatypes
1812
         */
1813
        /*      if by chance it is equal to the computed pixel value. */
1814
        /* --------------------------------------------------------------------
1815
         */
1816
0
        pDst[iDstOffset] = GWKClampValueT<T>(dfReal);
1817
0
    }
1818
0
    else
1819
0
    {
1820
0
        pDst[iDstOffset] = value;
1821
0
    }
1822
1823
0
    if (bAvoidNoDataSingleBand)
1824
0
        AvoidNoData<T>(poWK, iBand, iDstOffset);
1825
1826
0
    return true;
1827
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<unsigned char>(GDALWarpKernel const*, int, long long, double, unsigned char, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<short>(GDALWarpKernel const*, int, long long, double, short, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<unsigned short>(GDALWarpKernel const*, int, long long, double, unsigned short, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<float>(GDALWarpKernel const*, int, long long, double, float, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<int>(GDALWarpKernel const*, int, long long, double, int, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<unsigned int>(GDALWarpKernel const*, int, long long, double, unsigned int, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<long>(GDALWarpKernel const*, int, long long, double, long, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<unsigned long>(GDALWarpKernel const*, int, long long, double, unsigned long, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<cpl::Float16>(GDALWarpKernel const*, int, long long, double, cpl::Float16, bool)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKSetPixelValueRealT<double>(GDALWarpKernel const*, int, long long, double, double, bool)
1828
1829
/************************************************************************/
1830
/*                      ClampRoundAndAvoidNoData()                      */
1831
/************************************************************************/
1832
1833
template <class T>
1834
inline void ClampRoundAndAvoidNoData(const GDALWarpKernel *poWK, int iBand,
1835
                                     GPtrDiff_t iDstOffset, double dfReal,
1836
                                     bool bAvoidNoDataSingleBand)
1837
0
{
1838
0
    GByte *pabyDst = poWK->papabyDstImage[iBand];
1839
0
    T *pDst = reinterpret_cast<T *>(pabyDst);
1840
1841
    if constexpr (cpl::NumericLimits<T>::is_integer)
1842
0
    {
1843
0
        using std::floor;
1844
0
        if (dfReal < static_cast<double>(cpl::NumericLimits<T>::lowest()))
1845
0
            pDst[iDstOffset] = static_cast<T>(cpl::NumericLimits<T>::lowest());
1846
0
        else if (dfReal > static_cast<double>(cpl::NumericLimits<T>::max()))
1847
0
            pDst[iDstOffset] = static_cast<T>(cpl::NumericLimits<T>::max());
1848
        else if constexpr (cpl::NumericLimits<T>::is_signed)
1849
0
            pDst[iDstOffset] = static_cast<T>(floor(dfReal + 0.5));
1850
        else
1851
0
            pDst[iDstOffset] = static_cast<T>(dfReal + 0.5);
1852
    }
1853
    else
1854
0
    {
1855
0
        pDst[iDstOffset] = static_cast<T>(dfReal);
1856
0
    }
1857
1858
0
    if (bAvoidNoDataSingleBand)
1859
0
        AvoidNoData<T>(poWK, iBand, iDstOffset);
1860
0
}
Unexecuted instantiation: void ClampRoundAndAvoidNoData<unsigned char>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<signed char>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<short>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<unsigned short>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<unsigned int>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<int>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<unsigned long>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<long>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<cpl::Float16>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<float>(GDALWarpKernel const*, int, long long, double, bool)
Unexecuted instantiation: void ClampRoundAndAvoidNoData<double>(GDALWarpKernel const*, int, long long, double, bool)
1861
1862
/************************************************************************/
1863
/*                          GWKSetPixelValue()                          */
1864
/************************************************************************/
1865
1866
static bool GWKSetPixelValue(const GDALWarpKernel *poWK, int iBand,
1867
                             GPtrDiff_t iDstOffset, double dfDensity,
1868
                             double dfReal, double dfImag,
1869
                             bool bAvoidNoDataSingleBand)
1870
1871
0
{
1872
0
    GByte *pabyDst = poWK->papabyDstImage[iBand];
1873
1874
    /* -------------------------------------------------------------------- */
1875
    /*      If the source density is less than 100% we need to fetch the    */
1876
    /*      existing destination value, and mix it with the source to       */
1877
    /*      get the new "to apply" value.  Also compute composite           */
1878
    /*      density.                                                        */
1879
    /*                                                                      */
1880
    /*      We avoid mixing if density is very near one or risk mixing      */
1881
    /*      in very extreme nodata values and causing odd results (#1610)   */
1882
    /* -------------------------------------------------------------------- */
1883
0
    if (dfDensity < 0.9999)
1884
0
    {
1885
0
        if (dfDensity < 0.0001)
1886
0
            return true;
1887
1888
0
        double dfDstDensity = 1.0;
1889
0
        if (poWK->pafDstDensity != nullptr)
1890
0
            dfDstDensity = double(poWK->pafDstDensity[iDstOffset]);
1891
0
        else if (poWK->panDstValid != nullptr &&
1892
0
                 !CPLMaskGet(poWK->panDstValid, iDstOffset))
1893
0
            dfDstDensity = 0.0;
1894
1895
0
        double dfDstReal = 0.0;
1896
0
        double dfDstImag = 0.0;
1897
        // It seems like we also ought to be testing panDstValid[] here!
1898
1899
        // TODO(schwehr): Factor out this repreated type of set.
1900
0
        switch (poWK->eWorkingDataType)
1901
0
        {
1902
0
            case GDT_UInt8:
1903
0
                dfDstReal = pabyDst[iDstOffset];
1904
0
                dfDstImag = 0.0;
1905
0
                break;
1906
1907
0
            case GDT_Int8:
1908
0
                dfDstReal = reinterpret_cast<GInt8 *>(pabyDst)[iDstOffset];
1909
0
                dfDstImag = 0.0;
1910
0
                break;
1911
1912
0
            case GDT_Int16:
1913
0
                dfDstReal = reinterpret_cast<GInt16 *>(pabyDst)[iDstOffset];
1914
0
                dfDstImag = 0.0;
1915
0
                break;
1916
1917
0
            case GDT_UInt16:
1918
0
                dfDstReal = reinterpret_cast<GUInt16 *>(pabyDst)[iDstOffset];
1919
0
                dfDstImag = 0.0;
1920
0
                break;
1921
1922
0
            case GDT_Int32:
1923
0
                dfDstReal = reinterpret_cast<GInt32 *>(pabyDst)[iDstOffset];
1924
0
                dfDstImag = 0.0;
1925
0
                break;
1926
1927
0
            case GDT_UInt32:
1928
0
                dfDstReal = reinterpret_cast<GUInt32 *>(pabyDst)[iDstOffset];
1929
0
                dfDstImag = 0.0;
1930
0
                break;
1931
1932
0
            case GDT_Int64:
1933
0
                dfDstReal = static_cast<double>(
1934
0
                    reinterpret_cast<std::int64_t *>(pabyDst)[iDstOffset]);
1935
0
                dfDstImag = 0.0;
1936
0
                break;
1937
1938
0
            case GDT_UInt64:
1939
0
                dfDstReal = static_cast<double>(
1940
0
                    reinterpret_cast<std::uint64_t *>(pabyDst)[iDstOffset]);
1941
0
                dfDstImag = 0.0;
1942
0
                break;
1943
1944
0
            case GDT_Float16:
1945
0
                dfDstReal = reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset];
1946
0
                dfDstImag = 0.0;
1947
0
                break;
1948
1949
0
            case GDT_Float32:
1950
0
                dfDstReal =
1951
0
                    double(reinterpret_cast<float *>(pabyDst)[iDstOffset]);
1952
0
                dfDstImag = 0.0;
1953
0
                break;
1954
1955
0
            case GDT_Float64:
1956
0
                dfDstReal = reinterpret_cast<double *>(pabyDst)[iDstOffset];
1957
0
                dfDstImag = 0.0;
1958
0
                break;
1959
1960
0
            case GDT_CInt16:
1961
0
                dfDstReal = reinterpret_cast<GInt16 *>(pabyDst)[iDstOffset * 2];
1962
0
                dfDstImag =
1963
0
                    reinterpret_cast<GInt16 *>(pabyDst)[iDstOffset * 2 + 1];
1964
0
                break;
1965
1966
0
            case GDT_CInt32:
1967
0
                dfDstReal = reinterpret_cast<GInt32 *>(pabyDst)[iDstOffset * 2];
1968
0
                dfDstImag =
1969
0
                    reinterpret_cast<GInt32 *>(pabyDst)[iDstOffset * 2 + 1];
1970
0
                break;
1971
1972
0
            case GDT_CFloat16:
1973
0
                dfDstReal =
1974
0
                    reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset * 2];
1975
0
                dfDstImag =
1976
0
                    reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset * 2 + 1];
1977
0
                break;
1978
1979
0
            case GDT_CFloat32:
1980
0
                dfDstReal =
1981
0
                    double(reinterpret_cast<float *>(pabyDst)[iDstOffset * 2]);
1982
0
                dfDstImag = double(
1983
0
                    reinterpret_cast<float *>(pabyDst)[iDstOffset * 2 + 1]);
1984
0
                break;
1985
1986
0
            case GDT_CFloat64:
1987
0
                dfDstReal = reinterpret_cast<double *>(pabyDst)[iDstOffset * 2];
1988
0
                dfDstImag =
1989
0
                    reinterpret_cast<double *>(pabyDst)[iDstOffset * 2 + 1];
1990
0
                break;
1991
1992
0
            case GDT_Unknown:
1993
0
            case GDT_TypeCount:
1994
0
                CPLAssert(false);
1995
0
                return false;
1996
0
        }
1997
1998
        // The destination density is really only relative to the portion
1999
        // not occluded by the overlay.
2000
0
        const double dfDstInfluence = (1.0 - dfDensity) * dfDstDensity;
2001
2002
0
        dfReal = (dfReal * dfDensity + dfDstReal * dfDstInfluence) /
2003
0
                 (dfDensity + dfDstInfluence);
2004
2005
0
        dfImag = (dfImag * dfDensity + dfDstImag * dfDstInfluence) /
2006
0
                 (dfDensity + dfDstInfluence);
2007
0
    }
2008
2009
    /* -------------------------------------------------------------------- */
2010
    /*      Actually apply the destination value.                           */
2011
    /*                                                                      */
2012
    /*      Avoid using the destination nodata value for integer datatypes  */
2013
    /*      if by chance it is equal to the computed pixel value.           */
2014
    /* -------------------------------------------------------------------- */
2015
2016
0
    switch (poWK->eWorkingDataType)
2017
0
    {
2018
0
        case GDT_UInt8:
2019
0
            ClampRoundAndAvoidNoData<GByte>(poWK, iBand, iDstOffset, dfReal,
2020
0
                                            bAvoidNoDataSingleBand);
2021
0
            break;
2022
2023
0
        case GDT_Int8:
2024
0
            ClampRoundAndAvoidNoData<GInt8>(poWK, iBand, iDstOffset, dfReal,
2025
0
                                            bAvoidNoDataSingleBand);
2026
0
            break;
2027
2028
0
        case GDT_Int16:
2029
0
            ClampRoundAndAvoidNoData<GInt16>(poWK, iBand, iDstOffset, dfReal,
2030
0
                                             bAvoidNoDataSingleBand);
2031
0
            break;
2032
2033
0
        case GDT_UInt16:
2034
0
            ClampRoundAndAvoidNoData<GUInt16>(poWK, iBand, iDstOffset, dfReal,
2035
0
                                              bAvoidNoDataSingleBand);
2036
0
            break;
2037
2038
0
        case GDT_UInt32:
2039
0
            ClampRoundAndAvoidNoData<GUInt32>(poWK, iBand, iDstOffset, dfReal,
2040
0
                                              bAvoidNoDataSingleBand);
2041
0
            break;
2042
2043
0
        case GDT_Int32:
2044
0
            ClampRoundAndAvoidNoData<GInt32>(poWK, iBand, iDstOffset, dfReal,
2045
0
                                             bAvoidNoDataSingleBand);
2046
0
            break;
2047
2048
0
        case GDT_UInt64:
2049
0
            ClampRoundAndAvoidNoData<std::uint64_t>(
2050
0
                poWK, iBand, iDstOffset, dfReal, bAvoidNoDataSingleBand);
2051
0
            break;
2052
2053
0
        case GDT_Int64:
2054
0
            ClampRoundAndAvoidNoData<std::int64_t>(
2055
0
                poWK, iBand, iDstOffset, dfReal, bAvoidNoDataSingleBand);
2056
0
            break;
2057
2058
0
        case GDT_Float16:
2059
0
            ClampRoundAndAvoidNoData<GFloat16>(poWK, iBand, iDstOffset, dfReal,
2060
0
                                               bAvoidNoDataSingleBand);
2061
0
            break;
2062
2063
0
        case GDT_Float32:
2064
0
            ClampRoundAndAvoidNoData<float>(poWK, iBand, iDstOffset, dfReal,
2065
0
                                            bAvoidNoDataSingleBand);
2066
0
            break;
2067
2068
0
        case GDT_Float64:
2069
0
            ClampRoundAndAvoidNoData<double>(poWK, iBand, iDstOffset, dfReal,
2070
0
                                             bAvoidNoDataSingleBand);
2071
0
            break;
2072
2073
0
        case GDT_CInt16:
2074
0
        {
2075
0
            typedef GInt16 T;
2076
0
            if (dfReal < static_cast<double>(cpl::NumericLimits<T>::min()))
2077
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2078
0
                    cpl::NumericLimits<T>::min();
2079
0
            else if (dfReal > static_cast<double>(cpl::NumericLimits<T>::max()))
2080
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2081
0
                    cpl::NumericLimits<T>::max();
2082
0
            else
2083
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2084
0
                    static_cast<T>(floor(dfReal + 0.5));
2085
0
            if (dfImag < static_cast<double>(cpl::NumericLimits<T>::min()))
2086
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2087
0
                    cpl::NumericLimits<T>::min();
2088
0
            else if (dfImag > static_cast<double>(cpl::NumericLimits<T>::max()))
2089
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2090
0
                    cpl::NumericLimits<T>::max();
2091
0
            else
2092
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2093
0
                    static_cast<T>(floor(dfImag + 0.5));
2094
0
            break;
2095
0
        }
2096
2097
0
        case GDT_CInt32:
2098
0
        {
2099
0
            typedef GInt32 T;
2100
0
            if (dfReal < static_cast<double>(cpl::NumericLimits<T>::min()))
2101
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2102
0
                    cpl::NumericLimits<T>::min();
2103
0
            else if (dfReal > static_cast<double>(cpl::NumericLimits<T>::max()))
2104
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2105
0
                    cpl::NumericLimits<T>::max();
2106
0
            else
2107
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2] =
2108
0
                    static_cast<T>(floor(dfReal + 0.5));
2109
0
            if (dfImag < static_cast<double>(cpl::NumericLimits<T>::min()))
2110
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2111
0
                    cpl::NumericLimits<T>::min();
2112
0
            else if (dfImag > static_cast<double>(cpl::NumericLimits<T>::max()))
2113
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2114
0
                    cpl::NumericLimits<T>::max();
2115
0
            else
2116
0
                reinterpret_cast<T *>(pabyDst)[iDstOffset * 2 + 1] =
2117
0
                    static_cast<T>(floor(dfImag + 0.5));
2118
0
            break;
2119
0
        }
2120
2121
0
        case GDT_CFloat16:
2122
0
            reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset * 2] =
2123
0
                static_cast<GFloat16>(dfReal);
2124
0
            reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset * 2 + 1] =
2125
0
                static_cast<GFloat16>(dfImag);
2126
0
            break;
2127
2128
0
        case GDT_CFloat32:
2129
0
            reinterpret_cast<float *>(pabyDst)[iDstOffset * 2] =
2130
0
                static_cast<float>(dfReal);
2131
0
            reinterpret_cast<float *>(pabyDst)[iDstOffset * 2 + 1] =
2132
0
                static_cast<float>(dfImag);
2133
0
            break;
2134
2135
0
        case GDT_CFloat64:
2136
0
            reinterpret_cast<double *>(pabyDst)[iDstOffset * 2] = dfReal;
2137
0
            reinterpret_cast<double *>(pabyDst)[iDstOffset * 2 + 1] = dfImag;
2138
0
            break;
2139
2140
0
        case GDT_Unknown:
2141
0
        case GDT_TypeCount:
2142
0
            return false;
2143
0
    }
2144
2145
0
    return true;
2146
0
}
2147
2148
/************************************************************************/
2149
/*                        GWKSetPixelValueReal()                        */
2150
/************************************************************************/
2151
2152
static bool GWKSetPixelValueReal(const GDALWarpKernel *poWK, int iBand,
2153
                                 GPtrDiff_t iDstOffset, double dfDensity,
2154
                                 double dfReal, bool bAvoidNoDataSingleBand)
2155
2156
0
{
2157
0
    GByte *pabyDst = poWK->papabyDstImage[iBand];
2158
2159
    /* -------------------------------------------------------------------- */
2160
    /*      If the source density is less than 100% we need to fetch the    */
2161
    /*      existing destination value, and mix it with the source to       */
2162
    /*      get the new "to apply" value.  Also compute composite           */
2163
    /*      density.                                                        */
2164
    /*                                                                      */
2165
    /*      We avoid mixing if density is very near one or risk mixing      */
2166
    /*      in very extreme nodata values and causing odd results (#1610)   */
2167
    /* -------------------------------------------------------------------- */
2168
0
    if (dfDensity < 0.9999)
2169
0
    {
2170
0
        if (dfDensity < 0.0001)
2171
0
            return true;
2172
2173
0
        double dfDstReal = 0.0;
2174
0
        double dfDstDensity = 1.0;
2175
2176
0
        if (poWK->pafDstDensity != nullptr)
2177
0
            dfDstDensity = double(poWK->pafDstDensity[iDstOffset]);
2178
0
        else if (poWK->panDstValid != nullptr &&
2179
0
                 !CPLMaskGet(poWK->panDstValid, iDstOffset))
2180
0
            dfDstDensity = 0.0;
2181
2182
        // It seems like we also ought to be testing panDstValid[] here!
2183
2184
0
        switch (poWK->eWorkingDataType)
2185
0
        {
2186
0
            case GDT_UInt8:
2187
0
                dfDstReal = pabyDst[iDstOffset];
2188
0
                break;
2189
2190
0
            case GDT_Int8:
2191
0
                dfDstReal = reinterpret_cast<GInt8 *>(pabyDst)[iDstOffset];
2192
0
                break;
2193
2194
0
            case GDT_Int16:
2195
0
                dfDstReal = reinterpret_cast<GInt16 *>(pabyDst)[iDstOffset];
2196
0
                break;
2197
2198
0
            case GDT_UInt16:
2199
0
                dfDstReal = reinterpret_cast<GUInt16 *>(pabyDst)[iDstOffset];
2200
0
                break;
2201
2202
0
            case GDT_Int32:
2203
0
                dfDstReal = reinterpret_cast<GInt32 *>(pabyDst)[iDstOffset];
2204
0
                break;
2205
2206
0
            case GDT_UInt32:
2207
0
                dfDstReal = reinterpret_cast<GUInt32 *>(pabyDst)[iDstOffset];
2208
0
                break;
2209
2210
0
            case GDT_Int64:
2211
0
                dfDstReal = static_cast<double>(
2212
0
                    reinterpret_cast<std::int64_t *>(pabyDst)[iDstOffset]);
2213
0
                break;
2214
2215
0
            case GDT_UInt64:
2216
0
                dfDstReal = static_cast<double>(
2217
0
                    reinterpret_cast<std::uint64_t *>(pabyDst)[iDstOffset]);
2218
0
                break;
2219
2220
0
            case GDT_Float16:
2221
0
                dfDstReal = reinterpret_cast<GFloat16 *>(pabyDst)[iDstOffset];
2222
0
                break;
2223
2224
0
            case GDT_Float32:
2225
0
                dfDstReal =
2226
0
                    double(reinterpret_cast<float *>(pabyDst)[iDstOffset]);
2227
0
                break;
2228
2229
0
            case GDT_Float64:
2230
0
                dfDstReal = reinterpret_cast<double *>(pabyDst)[iDstOffset];
2231
0
                break;
2232
2233
0
            case GDT_CInt16:
2234
0
            case GDT_CInt32:
2235
0
            case GDT_CFloat16:
2236
0
            case GDT_CFloat32:
2237
0
            case GDT_CFloat64:
2238
0
            case GDT_Unknown:
2239
0
            case GDT_TypeCount:
2240
0
                CPLAssert(false);
2241
0
                return false;
2242
0
        }
2243
2244
        // The destination density is really only relative to the portion
2245
        // not occluded by the overlay.
2246
0
        const double dfDstInfluence = (1.0 - dfDensity) * dfDstDensity;
2247
2248
0
        dfReal = (dfReal * dfDensity + dfDstReal * dfDstInfluence) /
2249
0
                 (dfDensity + dfDstInfluence);
2250
0
    }
2251
2252
    /* -------------------------------------------------------------------- */
2253
    /*      Actually apply the destination value.                           */
2254
    /*                                                                      */
2255
    /*      Avoid using the destination nodata value for integer datatypes  */
2256
    /*      if by chance it is equal to the computed pixel value.           */
2257
    /* -------------------------------------------------------------------- */
2258
2259
0
    switch (poWK->eWorkingDataType)
2260
0
    {
2261
0
        case GDT_UInt8:
2262
0
            ClampRoundAndAvoidNoData<GByte>(poWK, iBand, iDstOffset, dfReal,
2263
0
                                            bAvoidNoDataSingleBand);
2264
0
            break;
2265
2266
0
        case GDT_Int8:
2267
0
            ClampRoundAndAvoidNoData<GInt8>(poWK, iBand, iDstOffset, dfReal,
2268
0
                                            bAvoidNoDataSingleBand);
2269
0
            break;
2270
2271
0
        case GDT_Int16:
2272
0
            ClampRoundAndAvoidNoData<GInt16>(poWK, iBand, iDstOffset, dfReal,
2273
0
                                             bAvoidNoDataSingleBand);
2274
0
            break;
2275
2276
0
        case GDT_UInt16:
2277
0
            ClampRoundAndAvoidNoData<GUInt16>(poWK, iBand, iDstOffset, dfReal,
2278
0
                                              bAvoidNoDataSingleBand);
2279
0
            break;
2280
2281
0
        case GDT_UInt32:
2282
0
            ClampRoundAndAvoidNoData<GUInt32>(poWK, iBand, iDstOffset, dfReal,
2283
0
                                              bAvoidNoDataSingleBand);
2284
0
            break;
2285
2286
0
        case GDT_Int32:
2287
0
            ClampRoundAndAvoidNoData<GInt32>(poWK, iBand, iDstOffset, dfReal,
2288
0
                                             bAvoidNoDataSingleBand);
2289
0
            break;
2290
2291
0
        case GDT_UInt64:
2292
0
            ClampRoundAndAvoidNoData<std::uint64_t>(
2293
0
                poWK, iBand, iDstOffset, dfReal, bAvoidNoDataSingleBand);
2294
0
            break;
2295
2296
0
        case GDT_Int64:
2297
0
            ClampRoundAndAvoidNoData<std::int64_t>(
2298
0
                poWK, iBand, iDstOffset, dfReal, bAvoidNoDataSingleBand);
2299
0
            break;
2300
2301
0
        case GDT_Float16:
2302
0
            ClampRoundAndAvoidNoData<GFloat16>(poWK, iBand, iDstOffset, dfReal,
2303
0
                                               bAvoidNoDataSingleBand);
2304
0
            break;
2305
2306
0
        case GDT_Float32:
2307
0
            ClampRoundAndAvoidNoData<float>(poWK, iBand, iDstOffset, dfReal,
2308
0
                                            bAvoidNoDataSingleBand);
2309
0
            break;
2310
2311
0
        case GDT_Float64:
2312
0
            ClampRoundAndAvoidNoData<double>(poWK, iBand, iDstOffset, dfReal,
2313
0
                                             bAvoidNoDataSingleBand);
2314
0
            break;
2315
2316
0
        case GDT_CInt16:
2317
0
        case GDT_CInt32:
2318
0
        case GDT_CFloat16:
2319
0
        case GDT_CFloat32:
2320
0
        case GDT_CFloat64:
2321
0
            return false;
2322
2323
0
        case GDT_Unknown:
2324
0
        case GDT_TypeCount:
2325
0
            CPLAssert(false);
2326
0
            return false;
2327
0
    }
2328
2329
0
    return true;
2330
0
}
2331
2332
/************************************************************************/
2333
/*                          GWKGetPixelValue()                          */
2334
/************************************************************************/
2335
2336
/* It is assumed that panUnifiedSrcValid has been checked before */
2337
2338
static bool GWKGetPixelValue(const GDALWarpKernel *poWK, int iBand,
2339
                             GPtrDiff_t iSrcOffset, double *pdfDensity,
2340
                             double *pdfReal, double *pdfImag)
2341
2342
0
{
2343
0
    GByte *pabySrc = poWK->papabySrcImage[iBand];
2344
2345
0
    if (poWK->papanBandSrcValid != nullptr &&
2346
0
        poWK->papanBandSrcValid[iBand] != nullptr &&
2347
0
        !CPLMaskGet(poWK->papanBandSrcValid[iBand], iSrcOffset))
2348
0
    {
2349
0
        *pdfDensity = 0.0;
2350
0
        return false;
2351
0
    }
2352
2353
0
    *pdfReal = 0.0;
2354
0
    *pdfImag = 0.0;
2355
2356
    // TODO(schwehr): Fix casting.
2357
0
    switch (poWK->eWorkingDataType)
2358
0
    {
2359
0
        case GDT_UInt8:
2360
0
            *pdfReal = pabySrc[iSrcOffset];
2361
0
            *pdfImag = 0.0;
2362
0
            break;
2363
2364
0
        case GDT_Int8:
2365
0
            *pdfReal = reinterpret_cast<GInt8 *>(pabySrc)[iSrcOffset];
2366
0
            *pdfImag = 0.0;
2367
0
            break;
2368
2369
0
        case GDT_Int16:
2370
0
            *pdfReal = reinterpret_cast<GInt16 *>(pabySrc)[iSrcOffset];
2371
0
            *pdfImag = 0.0;
2372
0
            break;
2373
2374
0
        case GDT_UInt16:
2375
0
            *pdfReal = reinterpret_cast<GUInt16 *>(pabySrc)[iSrcOffset];
2376
0
            *pdfImag = 0.0;
2377
0
            break;
2378
2379
0
        case GDT_Int32:
2380
0
            *pdfReal = reinterpret_cast<GInt32 *>(pabySrc)[iSrcOffset];
2381
0
            *pdfImag = 0.0;
2382
0
            break;
2383
2384
0
        case GDT_UInt32:
2385
0
            *pdfReal = reinterpret_cast<GUInt32 *>(pabySrc)[iSrcOffset];
2386
0
            *pdfImag = 0.0;
2387
0
            break;
2388
2389
0
        case GDT_Int64:
2390
0
            *pdfReal = static_cast<double>(
2391
0
                reinterpret_cast<std::int64_t *>(pabySrc)[iSrcOffset]);
2392
0
            *pdfImag = 0.0;
2393
0
            break;
2394
2395
0
        case GDT_UInt64:
2396
0
            *pdfReal = static_cast<double>(
2397
0
                reinterpret_cast<std::uint64_t *>(pabySrc)[iSrcOffset]);
2398
0
            *pdfImag = 0.0;
2399
0
            break;
2400
2401
0
        case GDT_Float16:
2402
0
            *pdfReal = reinterpret_cast<GFloat16 *>(pabySrc)[iSrcOffset];
2403
0
            *pdfImag = 0.0;
2404
0
            break;
2405
2406
0
        case GDT_Float32:
2407
0
            *pdfReal = double(reinterpret_cast<float *>(pabySrc)[iSrcOffset]);
2408
0
            *pdfImag = 0.0;
2409
0
            break;
2410
2411
0
        case GDT_Float64:
2412
0
            *pdfReal = reinterpret_cast<double *>(pabySrc)[iSrcOffset];
2413
0
            *pdfImag = 0.0;
2414
0
            break;
2415
2416
0
        case GDT_CInt16:
2417
0
            *pdfReal = reinterpret_cast<GInt16 *>(pabySrc)[iSrcOffset * 2];
2418
0
            *pdfImag = reinterpret_cast<GInt16 *>(pabySrc)[iSrcOffset * 2 + 1];
2419
0
            break;
2420
2421
0
        case GDT_CInt32:
2422
0
            *pdfReal = reinterpret_cast<GInt32 *>(pabySrc)[iSrcOffset * 2];
2423
0
            *pdfImag = reinterpret_cast<GInt32 *>(pabySrc)[iSrcOffset * 2 + 1];
2424
0
            break;
2425
2426
0
        case GDT_CFloat16:
2427
0
            *pdfReal = reinterpret_cast<GFloat16 *>(pabySrc)[iSrcOffset * 2];
2428
0
            *pdfImag =
2429
0
                reinterpret_cast<GFloat16 *>(pabySrc)[iSrcOffset * 2 + 1];
2430
0
            break;
2431
2432
0
        case GDT_CFloat32:
2433
0
            *pdfReal =
2434
0
                double(reinterpret_cast<float *>(pabySrc)[iSrcOffset * 2]);
2435
0
            *pdfImag =
2436
0
                double(reinterpret_cast<float *>(pabySrc)[iSrcOffset * 2 + 1]);
2437
0
            break;
2438
2439
0
        case GDT_CFloat64:
2440
0
            *pdfReal = reinterpret_cast<double *>(pabySrc)[iSrcOffset * 2];
2441
0
            *pdfImag = reinterpret_cast<double *>(pabySrc)[iSrcOffset * 2 + 1];
2442
0
            break;
2443
2444
0
        case GDT_Unknown:
2445
0
        case GDT_TypeCount:
2446
0
            CPLAssert(false);
2447
0
            *pdfDensity = 0.0;
2448
0
            return false;
2449
0
    }
2450
2451
0
    if (poWK->pafUnifiedSrcDensity != nullptr)
2452
0
        *pdfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
2453
0
    else
2454
0
        *pdfDensity = 1.0;
2455
2456
0
    return *pdfDensity != 0.0;
2457
0
}
2458
2459
/************************************************************************/
2460
/*                        GWKGetPixelValueReal()                        */
2461
/************************************************************************/
2462
2463
static bool GWKGetPixelValueReal(const GDALWarpKernel *poWK, int iBand,
2464
                                 GPtrDiff_t iSrcOffset, double *pdfDensity,
2465
                                 double *pdfReal)
2466
2467
0
{
2468
0
    GByte *pabySrc = poWK->papabySrcImage[iBand];
2469
2470
0
    if (poWK->papanBandSrcValid != nullptr &&
2471
0
        poWK->papanBandSrcValid[iBand] != nullptr &&
2472
0
        !CPLMaskGet(poWK->papanBandSrcValid[iBand], iSrcOffset))
2473
0
    {
2474
0
        *pdfDensity = 0.0;
2475
0
        return false;
2476
0
    }
2477
2478
0
    switch (poWK->eWorkingDataType)
2479
0
    {
2480
0
        case GDT_UInt8:
2481
0
            *pdfReal = pabySrc[iSrcOffset];
2482
0
            break;
2483
2484
0
        case GDT_Int8:
2485
0
            *pdfReal = reinterpret_cast<GInt8 *>(pabySrc)[iSrcOffset];
2486
0
            break;
2487
2488
0
        case GDT_Int16:
2489
0
            *pdfReal = reinterpret_cast<GInt16 *>(pabySrc)[iSrcOffset];
2490
0
            break;
2491
2492
0
        case GDT_UInt16:
2493
0
            *pdfReal = reinterpret_cast<GUInt16 *>(pabySrc)[iSrcOffset];
2494
0
            break;
2495
2496
0
        case GDT_Int32:
2497
0
            *pdfReal = reinterpret_cast<GInt32 *>(pabySrc)[iSrcOffset];
2498
0
            break;
2499
2500
0
        case GDT_UInt32:
2501
0
            *pdfReal = reinterpret_cast<GUInt32 *>(pabySrc)[iSrcOffset];
2502
0
            break;
2503
2504
0
        case GDT_Int64:
2505
0
            *pdfReal = static_cast<double>(
2506
0
                reinterpret_cast<std::int64_t *>(pabySrc)[iSrcOffset]);
2507
0
            break;
2508
2509
0
        case GDT_UInt64:
2510
0
            *pdfReal = static_cast<double>(
2511
0
                reinterpret_cast<std::uint64_t *>(pabySrc)[iSrcOffset]);
2512
0
            break;
2513
2514
0
        case GDT_Float16:
2515
0
            *pdfReal = reinterpret_cast<GFloat16 *>(pabySrc)[iSrcOffset];
2516
0
            break;
2517
2518
0
        case GDT_Float32:
2519
0
            *pdfReal = double(reinterpret_cast<float *>(pabySrc)[iSrcOffset]);
2520
0
            break;
2521
2522
0
        case GDT_Float64:
2523
0
            *pdfReal = reinterpret_cast<double *>(pabySrc)[iSrcOffset];
2524
0
            break;
2525
2526
0
        case GDT_CInt16:
2527
0
        case GDT_CInt32:
2528
0
        case GDT_CFloat16:
2529
0
        case GDT_CFloat32:
2530
0
        case GDT_CFloat64:
2531
0
        case GDT_Unknown:
2532
0
        case GDT_TypeCount:
2533
0
            CPLAssert(false);
2534
0
            return false;
2535
0
    }
2536
2537
0
    if (poWK->pafUnifiedSrcDensity != nullptr)
2538
0
        *pdfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
2539
0
    else
2540
0
        *pdfDensity = 1.0;
2541
2542
0
    return *pdfDensity != 0.0;
2543
0
}
2544
2545
/************************************************************************/
2546
/*                           GWKGetPixelRow()                           */
2547
/************************************************************************/
2548
2549
/* It is assumed that adfImag[] is set to 0 by caller code for non-complex */
2550
/* data-types. */
2551
2552
static bool GWKGetPixelRow(const GDALWarpKernel *poWK, int iBand,
2553
                           GPtrDiff_t iSrcOffset, int nHalfSrcLen,
2554
                           double *padfDensity, double adfReal[],
2555
                           double *padfImag)
2556
0
{
2557
    // We know that nSrcLen is even, so we can *always* unroll loops 2x.
2558
0
    const int nSrcLen = nHalfSrcLen * 2;
2559
0
    bool bHasValid = false;
2560
2561
0
    if (padfDensity != nullptr)
2562
0
    {
2563
        // Init the density.
2564
0
        for (int i = 0; i < nSrcLen; i += 2)
2565
0
        {
2566
0
            padfDensity[i] = 1.0;
2567
0
            padfDensity[i + 1] = 1.0;
2568
0
        }
2569
2570
0
        if (poWK->panUnifiedSrcValid != nullptr)
2571
0
        {
2572
0
            for (int i = 0; i < nSrcLen; i += 2)
2573
0
            {
2574
0
                if (CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset + i))
2575
0
                    bHasValid = true;
2576
0
                else
2577
0
                    padfDensity[i] = 0.0;
2578
2579
0
                if (CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset + i + 1))
2580
0
                    bHasValid = true;
2581
0
                else
2582
0
                    padfDensity[i + 1] = 0.0;
2583
0
            }
2584
2585
            // Reset or fail as needed.
2586
0
            if (bHasValid)
2587
0
                bHasValid = false;
2588
0
            else
2589
0
                return false;
2590
0
        }
2591
2592
0
        if (poWK->papanBandSrcValid != nullptr &&
2593
0
            poWK->papanBandSrcValid[iBand] != nullptr)
2594
0
        {
2595
0
            for (int i = 0; i < nSrcLen; i += 2)
2596
0
            {
2597
0
                if (CPLMaskGet(poWK->papanBandSrcValid[iBand], iSrcOffset + i))
2598
0
                    bHasValid = true;
2599
0
                else
2600
0
                    padfDensity[i] = 0.0;
2601
2602
0
                if (CPLMaskGet(poWK->papanBandSrcValid[iBand],
2603
0
                               iSrcOffset + i + 1))
2604
0
                    bHasValid = true;
2605
0
                else
2606
0
                    padfDensity[i + 1] = 0.0;
2607
0
            }
2608
2609
            // Reset or fail as needed.
2610
0
            if (bHasValid)
2611
0
                bHasValid = false;
2612
0
            else
2613
0
                return false;
2614
0
        }
2615
0
    }
2616
2617
    // TODO(schwehr): Fix casting.
2618
    // Fetch data.
2619
0
    switch (poWK->eWorkingDataType)
2620
0
    {
2621
0
        case GDT_UInt8:
2622
0
        {
2623
0
            GByte *pSrc =
2624
0
                reinterpret_cast<GByte *>(poWK->papabySrcImage[iBand]);
2625
0
            pSrc += iSrcOffset;
2626
0
            for (int i = 0; i < nSrcLen; i += 2)
2627
0
            {
2628
0
                adfReal[i] = pSrc[i];
2629
0
                adfReal[i + 1] = pSrc[i + 1];
2630
0
            }
2631
0
            break;
2632
0
        }
2633
2634
0
        case GDT_Int8:
2635
0
        {
2636
0
            GInt8 *pSrc =
2637
0
                reinterpret_cast<GInt8 *>(poWK->papabySrcImage[iBand]);
2638
0
            pSrc += iSrcOffset;
2639
0
            for (int i = 0; i < nSrcLen; i += 2)
2640
0
            {
2641
0
                adfReal[i] = pSrc[i];
2642
0
                adfReal[i + 1] = pSrc[i + 1];
2643
0
            }
2644
0
            break;
2645
0
        }
2646
2647
0
        case GDT_Int16:
2648
0
        {
2649
0
            GInt16 *pSrc =
2650
0
                reinterpret_cast<GInt16 *>(poWK->papabySrcImage[iBand]);
2651
0
            pSrc += iSrcOffset;
2652
0
            for (int i = 0; i < nSrcLen; i += 2)
2653
0
            {
2654
0
                adfReal[i] = pSrc[i];
2655
0
                adfReal[i + 1] = pSrc[i + 1];
2656
0
            }
2657
0
            break;
2658
0
        }
2659
2660
0
        case GDT_UInt16:
2661
0
        {
2662
0
            GUInt16 *pSrc =
2663
0
                reinterpret_cast<GUInt16 *>(poWK->papabySrcImage[iBand]);
2664
0
            pSrc += iSrcOffset;
2665
0
            for (int i = 0; i < nSrcLen; i += 2)
2666
0
            {
2667
0
                adfReal[i] = pSrc[i];
2668
0
                adfReal[i + 1] = pSrc[i + 1];
2669
0
            }
2670
0
            break;
2671
0
        }
2672
2673
0
        case GDT_Int32:
2674
0
        {
2675
0
            GInt32 *pSrc =
2676
0
                reinterpret_cast<GInt32 *>(poWK->papabySrcImage[iBand]);
2677
0
            pSrc += iSrcOffset;
2678
0
            for (int i = 0; i < nSrcLen; i += 2)
2679
0
            {
2680
0
                adfReal[i] = pSrc[i];
2681
0
                adfReal[i + 1] = pSrc[i + 1];
2682
0
            }
2683
0
            break;
2684
0
        }
2685
2686
0
        case GDT_UInt32:
2687
0
        {
2688
0
            GUInt32 *pSrc =
2689
0
                reinterpret_cast<GUInt32 *>(poWK->papabySrcImage[iBand]);
2690
0
            pSrc += iSrcOffset;
2691
0
            for (int i = 0; i < nSrcLen; i += 2)
2692
0
            {
2693
0
                adfReal[i] = pSrc[i];
2694
0
                adfReal[i + 1] = pSrc[i + 1];
2695
0
            }
2696
0
            break;
2697
0
        }
2698
2699
0
        case GDT_Int64:
2700
0
        {
2701
0
            auto pSrc =
2702
0
                reinterpret_cast<std::int64_t *>(poWK->papabySrcImage[iBand]);
2703
0
            pSrc += iSrcOffset;
2704
0
            for (int i = 0; i < nSrcLen; i += 2)
2705
0
            {
2706
0
                adfReal[i] = static_cast<double>(pSrc[i]);
2707
0
                adfReal[i + 1] = static_cast<double>(pSrc[i + 1]);
2708
0
            }
2709
0
            break;
2710
0
        }
2711
2712
0
        case GDT_UInt64:
2713
0
        {
2714
0
            auto pSrc =
2715
0
                reinterpret_cast<std::uint64_t *>(poWK->papabySrcImage[iBand]);
2716
0
            pSrc += iSrcOffset;
2717
0
            for (int i = 0; i < nSrcLen; i += 2)
2718
0
            {
2719
0
                adfReal[i] = static_cast<double>(pSrc[i]);
2720
0
                adfReal[i + 1] = static_cast<double>(pSrc[i + 1]);
2721
0
            }
2722
0
            break;
2723
0
        }
2724
2725
0
        case GDT_Float16:
2726
0
        {
2727
0
            GFloat16 *pSrc =
2728
0
                reinterpret_cast<GFloat16 *>(poWK->papabySrcImage[iBand]);
2729
0
            pSrc += iSrcOffset;
2730
0
            for (int i = 0; i < nSrcLen; i += 2)
2731
0
            {
2732
0
                adfReal[i] = pSrc[i];
2733
0
                adfReal[i + 1] = pSrc[i + 1];
2734
0
            }
2735
0
            break;
2736
0
        }
2737
2738
0
        case GDT_Float32:
2739
0
        {
2740
0
            float *pSrc =
2741
0
                reinterpret_cast<float *>(poWK->papabySrcImage[iBand]);
2742
0
            pSrc += iSrcOffset;
2743
0
            for (int i = 0; i < nSrcLen; i += 2)
2744
0
            {
2745
0
                adfReal[i] = double(pSrc[i]);
2746
0
                adfReal[i + 1] = double(pSrc[i + 1]);
2747
0
            }
2748
0
            break;
2749
0
        }
2750
2751
0
        case GDT_Float64:
2752
0
        {
2753
0
            double *pSrc =
2754
0
                reinterpret_cast<double *>(poWK->papabySrcImage[iBand]);
2755
0
            pSrc += iSrcOffset;
2756
0
            for (int i = 0; i < nSrcLen; i += 2)
2757
0
            {
2758
0
                adfReal[i] = pSrc[i];
2759
0
                adfReal[i + 1] = pSrc[i + 1];
2760
0
            }
2761
0
            break;
2762
0
        }
2763
2764
0
        case GDT_CInt16:
2765
0
        {
2766
0
            GInt16 *pSrc =
2767
0
                reinterpret_cast<GInt16 *>(poWK->papabySrcImage[iBand]);
2768
0
            pSrc += 2 * iSrcOffset;
2769
0
            for (int i = 0; i < nSrcLen; i += 2)
2770
0
            {
2771
0
                adfReal[i] = pSrc[2 * i];
2772
0
                padfImag[i] = pSrc[2 * i + 1];
2773
2774
0
                adfReal[i + 1] = pSrc[2 * i + 2];
2775
0
                padfImag[i + 1] = pSrc[2 * i + 3];
2776
0
            }
2777
0
            break;
2778
0
        }
2779
2780
0
        case GDT_CInt32:
2781
0
        {
2782
0
            GInt32 *pSrc =
2783
0
                reinterpret_cast<GInt32 *>(poWK->papabySrcImage[iBand]);
2784
0
            pSrc += 2 * iSrcOffset;
2785
0
            for (int i = 0; i < nSrcLen; i += 2)
2786
0
            {
2787
0
                adfReal[i] = pSrc[2 * i];
2788
0
                padfImag[i] = pSrc[2 * i + 1];
2789
2790
0
                adfReal[i + 1] = pSrc[2 * i + 2];
2791
0
                padfImag[i + 1] = pSrc[2 * i + 3];
2792
0
            }
2793
0
            break;
2794
0
        }
2795
2796
0
        case GDT_CFloat16:
2797
0
        {
2798
0
            GFloat16 *pSrc =
2799
0
                reinterpret_cast<GFloat16 *>(poWK->papabySrcImage[iBand]);
2800
0
            pSrc += 2 * iSrcOffset;
2801
0
            for (int i = 0; i < nSrcLen; i += 2)
2802
0
            {
2803
0
                adfReal[i] = pSrc[2 * i];
2804
0
                padfImag[i] = pSrc[2 * i + 1];
2805
2806
0
                adfReal[i + 1] = pSrc[2 * i + 2];
2807
0
                padfImag[i + 1] = pSrc[2 * i + 3];
2808
0
            }
2809
0
            break;
2810
0
        }
2811
2812
0
        case GDT_CFloat32:
2813
0
        {
2814
0
            float *pSrc =
2815
0
                reinterpret_cast<float *>(poWK->papabySrcImage[iBand]);
2816
0
            pSrc += 2 * iSrcOffset;
2817
0
            for (int i = 0; i < nSrcLen; i += 2)
2818
0
            {
2819
0
                adfReal[i] = double(pSrc[2 * i]);
2820
0
                padfImag[i] = double(pSrc[2 * i + 1]);
2821
2822
0
                adfReal[i + 1] = double(pSrc[2 * i + 2]);
2823
0
                padfImag[i + 1] = double(pSrc[2 * i + 3]);
2824
0
            }
2825
0
            break;
2826
0
        }
2827
2828
0
        case GDT_CFloat64:
2829
0
        {
2830
0
            double *pSrc =
2831
0
                reinterpret_cast<double *>(poWK->papabySrcImage[iBand]);
2832
0
            pSrc += 2 * iSrcOffset;
2833
0
            for (int i = 0; i < nSrcLen; i += 2)
2834
0
            {
2835
0
                adfReal[i] = pSrc[2 * i];
2836
0
                padfImag[i] = pSrc[2 * i + 1];
2837
2838
0
                adfReal[i + 1] = pSrc[2 * i + 2];
2839
0
                padfImag[i + 1] = pSrc[2 * i + 3];
2840
0
            }
2841
0
            break;
2842
0
        }
2843
2844
0
        case GDT_Unknown:
2845
0
        case GDT_TypeCount:
2846
0
            CPLAssert(false);
2847
0
            if (padfDensity)
2848
0
                memset(padfDensity, 0, nSrcLen * sizeof(double));
2849
0
            return false;
2850
0
    }
2851
2852
0
    if (padfDensity == nullptr)
2853
0
        return true;
2854
2855
0
    if (poWK->pafUnifiedSrcDensity == nullptr)
2856
0
    {
2857
0
        for (int i = 0; i < nSrcLen; i += 2)
2858
0
        {
2859
            // Take into account earlier calcs.
2860
0
            if (padfDensity[i] > SRC_DENSITY_THRESHOLD_DOUBLE)
2861
0
            {
2862
0
                padfDensity[i] = 1.0;
2863
0
                bHasValid = true;
2864
0
            }
2865
2866
0
            if (padfDensity[i + 1] > SRC_DENSITY_THRESHOLD_DOUBLE)
2867
0
            {
2868
0
                padfDensity[i + 1] = 1.0;
2869
0
                bHasValid = true;
2870
0
            }
2871
0
        }
2872
0
    }
2873
0
    else
2874
0
    {
2875
0
        for (int i = 0; i < nSrcLen; i += 2)
2876
0
        {
2877
0
            if (padfDensity[i] > SRC_DENSITY_THRESHOLD_DOUBLE)
2878
0
                padfDensity[i] =
2879
0
                    double(poWK->pafUnifiedSrcDensity[iSrcOffset + i]);
2880
0
            if (padfDensity[i] > SRC_DENSITY_THRESHOLD_DOUBLE)
2881
0
                bHasValid = true;
2882
2883
0
            if (padfDensity[i + 1] > SRC_DENSITY_THRESHOLD_DOUBLE)
2884
0
                padfDensity[i + 1] =
2885
0
                    double(poWK->pafUnifiedSrcDensity[iSrcOffset + i + 1]);
2886
0
            if (padfDensity[i + 1] > SRC_DENSITY_THRESHOLD_DOUBLE)
2887
0
                bHasValid = true;
2888
0
        }
2889
0
    }
2890
2891
0
    return bHasValid;
2892
0
}
2893
2894
/************************************************************************/
2895
/*                            GWKGetPixelT()                            */
2896
/************************************************************************/
2897
2898
template <class T>
2899
static bool GWKGetPixelT(const GDALWarpKernel *poWK, int iBand,
2900
                         GPtrDiff_t iSrcOffset, double *pdfDensity, T *pValue)
2901
2902
0
{
2903
0
    T *pSrc = reinterpret_cast<T *>(poWK->papabySrcImage[iBand]);
2904
2905
0
    if ((poWK->panUnifiedSrcValid != nullptr &&
2906
0
         !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset)) ||
2907
0
        (poWK->papanBandSrcValid != nullptr &&
2908
0
         poWK->papanBandSrcValid[iBand] != nullptr &&
2909
0
         !CPLMaskGet(poWK->papanBandSrcValid[iBand], iSrcOffset)))
2910
0
    {
2911
0
        *pdfDensity = 0.0;
2912
0
        return false;
2913
0
    }
2914
2915
0
    *pValue = pSrc[iSrcOffset];
2916
2917
0
    if (poWK->pafUnifiedSrcDensity == nullptr)
2918
0
        *pdfDensity = 1.0;
2919
0
    else
2920
0
        *pdfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
2921
2922
0
    return *pdfDensity != 0.0;
2923
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<unsigned char>(GDALWarpKernel const*, int, long long, double*, unsigned char*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<short>(GDALWarpKernel const*, int, long long, double*, short*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<unsigned short>(GDALWarpKernel const*, int, long long, double*, unsigned short*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<float>(GDALWarpKernel const*, int, long long, double*, float*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<int>(GDALWarpKernel const*, int, long long, double*, int*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<unsigned int>(GDALWarpKernel const*, int, long long, double*, unsigned int*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<long>(GDALWarpKernel const*, int, long long, double*, long*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<unsigned long>(GDALWarpKernel const*, int, long long, double*, unsigned long*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<cpl::Float16>(GDALWarpKernel const*, int, long long, double*, cpl::Float16*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKGetPixelT<double>(GDALWarpKernel const*, int, long long, double*, double*)
2924
2925
/************************************************************************/
2926
/*                        GWKBilinearResample()                         */
2927
/*     Set of bilinear interpolators                                    */
2928
/************************************************************************/
2929
2930
static bool GWKBilinearResample4Sample(const GDALWarpKernel *poWK, int iBand,
2931
                                       double dfSrcX, double dfSrcY,
2932
                                       double *pdfDensity, double *pdfReal,
2933
                                       double *pdfImag)
2934
2935
0
{
2936
    // Save as local variables to avoid following pointers.
2937
0
    const int nSrcXSize = poWK->nSrcXSize;
2938
0
    const int nSrcYSize = poWK->nSrcYSize;
2939
2940
0
    int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
2941
0
    int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
2942
0
    double dfRatioX = 1.5 - (dfSrcX - iSrcX);
2943
0
    double dfRatioY = 1.5 - (dfSrcY - iSrcY);
2944
0
    bool bShifted = false;
2945
2946
0
    if (iSrcX == -1)
2947
0
    {
2948
0
        iSrcX = 0;
2949
0
        dfRatioX = 1;
2950
0
    }
2951
0
    if (iSrcY == -1)
2952
0
    {
2953
0
        iSrcY = 0;
2954
0
        dfRatioY = 1;
2955
0
    }
2956
0
    GPtrDiff_t iSrcOffset = iSrcX + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
2957
2958
    // Shift so we don't overrun the array.
2959
0
    if (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize == iSrcOffset + 1 ||
2960
0
        static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize ==
2961
0
            iSrcOffset + nSrcXSize + 1)
2962
0
    {
2963
0
        bShifted = true;
2964
0
        --iSrcOffset;
2965
0
    }
2966
2967
0
    double adfDensity[2] = {0.0, 0.0};
2968
0
    double adfReal[2] = {0.0, 0.0};
2969
0
    double adfImag[2] = {0.0, 0.0};
2970
0
    double dfAccumulatorReal = 0.0;
2971
0
    double dfAccumulatorImag = 0.0;
2972
0
    double dfAccumulatorDensity = 0.0;
2973
0
    double dfAccumulatorDivisor = 0.0;
2974
2975
0
    const GPtrDiff_t nSrcPixels =
2976
0
        static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize;
2977
    // Get pixel row.
2978
0
    if (iSrcY >= 0 && iSrcY < nSrcYSize && iSrcOffset >= 0 &&
2979
0
        iSrcOffset < nSrcPixels &&
2980
0
        GWKGetPixelRow(poWK, iBand, iSrcOffset, 1, adfDensity, adfReal,
2981
0
                       adfImag))
2982
0
    {
2983
0
        double dfMult1 = dfRatioX * dfRatioY;
2984
0
        double dfMult2 = (1.0 - dfRatioX) * dfRatioY;
2985
2986
        // Shifting corrected.
2987
0
        if (bShifted)
2988
0
        {
2989
0
            adfReal[0] = adfReal[1];
2990
0
            adfImag[0] = adfImag[1];
2991
0
            adfDensity[0] = adfDensity[1];
2992
0
        }
2993
2994
        // Upper Left Pixel.
2995
0
        if (iSrcX >= 0 && iSrcX < nSrcXSize &&
2996
0
            adfDensity[0] > SRC_DENSITY_THRESHOLD_DOUBLE)
2997
0
        {
2998
0
            dfAccumulatorDivisor += dfMult1;
2999
3000
0
            dfAccumulatorReal += adfReal[0] * dfMult1;
3001
0
            dfAccumulatorImag += adfImag[0] * dfMult1;
3002
0
            dfAccumulatorDensity += adfDensity[0] * dfMult1;
3003
0
        }
3004
3005
        // Upper Right Pixel.
3006
0
        if (iSrcX + 1 >= 0 && iSrcX + 1 < nSrcXSize &&
3007
0
            adfDensity[1] > SRC_DENSITY_THRESHOLD_DOUBLE)
3008
0
        {
3009
0
            dfAccumulatorDivisor += dfMult2;
3010
3011
0
            dfAccumulatorReal += adfReal[1] * dfMult2;
3012
0
            dfAccumulatorImag += adfImag[1] * dfMult2;
3013
0
            dfAccumulatorDensity += adfDensity[1] * dfMult2;
3014
0
        }
3015
0
    }
3016
3017
    // Get pixel row.
3018
0
    if (iSrcY + 1 >= 0 && iSrcY + 1 < nSrcYSize &&
3019
0
        iSrcOffset + nSrcXSize >= 0 && iSrcOffset + nSrcXSize < nSrcPixels &&
3020
0
        GWKGetPixelRow(poWK, iBand, iSrcOffset + nSrcXSize, 1, adfDensity,
3021
0
                       adfReal, adfImag))
3022
0
    {
3023
0
        double dfMult1 = dfRatioX * (1.0 - dfRatioY);
3024
0
        double dfMult2 = (1.0 - dfRatioX) * (1.0 - dfRatioY);
3025
3026
        // Shifting corrected
3027
0
        if (bShifted)
3028
0
        {
3029
0
            adfReal[0] = adfReal[1];
3030
0
            adfImag[0] = adfImag[1];
3031
0
            adfDensity[0] = adfDensity[1];
3032
0
        }
3033
3034
        // Lower Left Pixel
3035
0
        if (iSrcX >= 0 && iSrcX < nSrcXSize &&
3036
0
            adfDensity[0] > SRC_DENSITY_THRESHOLD_DOUBLE)
3037
0
        {
3038
0
            dfAccumulatorDivisor += dfMult1;
3039
3040
0
            dfAccumulatorReal += adfReal[0] * dfMult1;
3041
0
            dfAccumulatorImag += adfImag[0] * dfMult1;
3042
0
            dfAccumulatorDensity += adfDensity[0] * dfMult1;
3043
0
        }
3044
3045
        // Lower Right Pixel.
3046
0
        if (iSrcX + 1 >= 0 && iSrcX + 1 < nSrcXSize &&
3047
0
            adfDensity[1] > SRC_DENSITY_THRESHOLD_DOUBLE)
3048
0
        {
3049
0
            dfAccumulatorDivisor += dfMult2;
3050
3051
0
            dfAccumulatorReal += adfReal[1] * dfMult2;
3052
0
            dfAccumulatorImag += adfImag[1] * dfMult2;
3053
0
            dfAccumulatorDensity += adfDensity[1] * dfMult2;
3054
0
        }
3055
0
    }
3056
3057
    /* -------------------------------------------------------------------- */
3058
    /*      Return result.                                                  */
3059
    /* -------------------------------------------------------------------- */
3060
0
    if (dfAccumulatorDivisor == 1.0)
3061
0
    {
3062
0
        *pdfReal = dfAccumulatorReal;
3063
0
        *pdfImag = dfAccumulatorImag;
3064
0
        *pdfDensity = dfAccumulatorDensity;
3065
0
        return false;
3066
0
    }
3067
0
    else if (dfAccumulatorDivisor < 0.00001)
3068
0
    {
3069
0
        *pdfReal = 0.0;
3070
0
        *pdfImag = 0.0;
3071
0
        *pdfDensity = 0.0;
3072
0
        return false;
3073
0
    }
3074
0
    else
3075
0
    {
3076
0
        *pdfReal = dfAccumulatorReal / dfAccumulatorDivisor;
3077
0
        *pdfImag = dfAccumulatorImag / dfAccumulatorDivisor;
3078
0
        *pdfDensity = dfAccumulatorDensity / dfAccumulatorDivisor;
3079
0
        return true;
3080
0
    }
3081
0
}
3082
3083
template <class T>
3084
static bool GWKBilinearResampleNoMasks4SampleT(const GDALWarpKernel *poWK,
3085
                                               int iBand, double dfSrcX,
3086
                                               double dfSrcY, T *pValue)
3087
3088
0
{
3089
3090
0
    const int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
3091
0
    const int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
3092
0
    GPtrDiff_t iSrcOffset =
3093
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
3094
0
    const double dfRatioX = 1.5 - (dfSrcX - iSrcX);
3095
0
    const double dfRatioY = 1.5 - (dfSrcY - iSrcY);
3096
3097
0
    const T *const pSrc = reinterpret_cast<T *>(poWK->papabySrcImage[iBand]);
3098
3099
0
    if (iSrcX >= 0 && iSrcX + 1 < poWK->nSrcXSize && iSrcY >= 0 &&
3100
0
        iSrcY + 1 < poWK->nSrcYSize)
3101
0
    {
3102
0
        const double dfAccumulator =
3103
0
            (double(pSrc[iSrcOffset]) * dfRatioX +
3104
0
             double(pSrc[iSrcOffset + 1]) * (1.0 - dfRatioX)) *
3105
0
                dfRatioY +
3106
0
            (double(pSrc[iSrcOffset + poWK->nSrcXSize]) * dfRatioX +
3107
0
             double(pSrc[iSrcOffset + 1 + poWK->nSrcXSize]) *
3108
0
                 (1.0 - dfRatioX)) *
3109
0
                (1.0 - dfRatioY);
3110
3111
0
        *pValue = GWKRoundValueT<T>(dfAccumulator);
3112
3113
0
        return true;
3114
0
    }
3115
3116
0
    double dfAccumulatorDivisor = 0.0;
3117
0
    double dfAccumulator = 0.0;
3118
3119
    // Upper Left Pixel.
3120
0
    if (iSrcX >= 0 && iSrcX < poWK->nSrcXSize && iSrcY >= 0 &&
3121
0
        iSrcY < poWK->nSrcYSize)
3122
0
    {
3123
0
        const double dfMult = dfRatioX * dfRatioY;
3124
3125
0
        dfAccumulatorDivisor += dfMult;
3126
3127
0
        dfAccumulator += double(pSrc[iSrcOffset]) * dfMult;
3128
0
    }
3129
3130
    // Upper Right Pixel.
3131
0
    if (iSrcX + 1 >= 0 && iSrcX + 1 < poWK->nSrcXSize && iSrcY >= 0 &&
3132
0
        iSrcY < poWK->nSrcYSize)
3133
0
    {
3134
0
        const double dfMult = (1.0 - dfRatioX) * dfRatioY;
3135
3136
0
        dfAccumulatorDivisor += dfMult;
3137
3138
0
        dfAccumulator += double(pSrc[iSrcOffset + 1]) * dfMult;
3139
0
    }
3140
3141
    // Lower Right Pixel.
3142
0
    if (iSrcX + 1 >= 0 && iSrcX + 1 < poWK->nSrcXSize && iSrcY + 1 >= 0 &&
3143
0
        iSrcY + 1 < poWK->nSrcYSize)
3144
0
    {
3145
0
        const double dfMult = (1.0 - dfRatioX) * (1.0 - dfRatioY);
3146
3147
0
        dfAccumulatorDivisor += dfMult;
3148
3149
0
        dfAccumulator +=
3150
0
            double(pSrc[iSrcOffset + 1 + poWK->nSrcXSize]) * dfMult;
3151
0
    }
3152
3153
    // Lower Left Pixel.
3154
0
    if (iSrcX >= 0 && iSrcX < poWK->nSrcXSize && iSrcY + 1 >= 0 &&
3155
0
        iSrcY + 1 < poWK->nSrcYSize)
3156
0
    {
3157
0
        const double dfMult = dfRatioX * (1.0 - dfRatioY);
3158
3159
0
        dfAccumulatorDivisor += dfMult;
3160
3161
0
        dfAccumulator += double(pSrc[iSrcOffset + poWK->nSrcXSize]) * dfMult;
3162
0
    }
3163
3164
    /* -------------------------------------------------------------------- */
3165
    /*      Return result.                                                  */
3166
    /* -------------------------------------------------------------------- */
3167
0
    double dfValue = 0.0;
3168
3169
0
    if (dfAccumulatorDivisor < 0.00001)
3170
0
    {
3171
0
        *pValue = 0;
3172
0
        return false;
3173
0
    }
3174
0
    else if (dfAccumulatorDivisor == 1.0)
3175
0
    {
3176
0
        dfValue = dfAccumulator;
3177
0
    }
3178
0
    else
3179
0
    {
3180
0
        dfValue = dfAccumulator / dfAccumulatorDivisor;
3181
0
    }
3182
3183
0
    *pValue = GWKRoundValueT<T>(dfValue);
3184
3185
0
    return true;
3186
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKBilinearResampleNoMasks4SampleT<unsigned char>(GDALWarpKernel const*, int, double, double, unsigned char*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKBilinearResampleNoMasks4SampleT<float>(GDALWarpKernel const*, int, double, double, float*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKBilinearResampleNoMasks4SampleT<short>(GDALWarpKernel const*, int, double, double, short*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKBilinearResampleNoMasks4SampleT<unsigned short>(GDALWarpKernel const*, int, double, double, unsigned short*)
3187
3188
/************************************************************************/
3189
/*                        GWKCubicResample()                            */
3190
/*     Set of bicubic interpolators using cubic convolution.            */
3191
/************************************************************************/
3192
3193
// http://verona.fi-p.unam.mx/boris/practicas/CubConvInterp.pdf Formula 18
3194
// or http://en.wikipedia.org/wiki/Cubic_Hermite_spline : CINTx(p_1,p0,p1,p2)
3195
// http://en.wikipedia.org/wiki/Bicubic_interpolation: matrix notation
3196
3197
template <typename T>
3198
static inline T CubicConvolution(T distance1, T distance2, T distance3, T f0,
3199
                                 T f1, T f2, T f3)
3200
0
{
3201
0
    return (f1 + T(0.5) * (distance1 * (f2 - f0) +
3202
0
                           distance2 * (2 * f0 - 5 * f1 + 4 * f2 - f3) +
3203
0
                           distance3 * (3 * (f1 - f2) + f3 - f0)));
3204
0
}
3205
3206
/************************************************************************/
3207
/*                       GWKCubicComputeWeights()                       */
3208
/************************************************************************/
3209
3210
// adfCoeffs[2] = 1.0 - (adfCoeffs[0] + adfCoeffs[1] - adfCoeffs[3]);
3211
3212
template <typename T>
3213
static inline void GWKCubicComputeWeights(T x, T coeffs[4])
3214
0
{
3215
0
    const T halfX = T(0.5) * x;
3216
0
    const T threeX = T(3.0) * x;
3217
0
    const T halfX2 = halfX * x;
3218
3219
0
    coeffs[0] = halfX * (-1 + x * (2 - x));
3220
0
    coeffs[1] = 1 + halfX2 * (-5 + threeX);
3221
0
    coeffs[2] = halfX * (1 + x * (4 - threeX));
3222
0
    coeffs[3] = halfX2 * (-1 + x);
3223
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKCubicComputeWeights<double>(double, double*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKCubicComputeWeights<float>(float, float*)
3224
3225
template <typename T> inline double CONVOL4(const double v1[4], const T v2[4])
3226
0
{
3227
0
    return v1[0] * double(v2[0]) + v1[1] * double(v2[1]) +
3228
0
           v1[2] * double(v2[2]) + v1[3] * double(v2[3]);
3229
0
}
Unexecuted instantiation: double CONVOL4<double>(double const*, double const*)
Unexecuted instantiation: double CONVOL4<float>(double const*, float const*)
Unexecuted instantiation: double CONVOL4<unsigned char>(double const*, unsigned char const*)
Unexecuted instantiation: double CONVOL4<unsigned short>(double const*, unsigned short const*)
Unexecuted instantiation: double CONVOL4<short>(double const*, short const*)
3230
3231
#if 0
3232
// Optimal (in theory...) for max 2 convolutions: 14 multiplications
3233
// instead of 17.
3234
// TODO(schwehr): Use an inline function.
3235
#define GWKCubicComputeWeights_Optim2MAX(dfX_, adfCoeffs, dfHalfX)             \
3236
    {                                                                          \
3237
        const double dfX = dfX_;                                               \
3238
        dfHalfX = 0.5 * dfX;                                                   \
3239
        const double dfThreeX = 3.0 * dfX;                                     \
3240
        const double dfXMinus1 = dfX - 1;                                      \
3241
                                                                               \
3242
        adfCoeffs[0] = -1 + dfX * (2 - dfX);                                   \
3243
        adfCoeffs[1] = dfX * (-5 + dfThreeX);                                  \
3244
        /*adfCoeffs[2] = 1 + dfX * (4 - dfThreeX);*/                           \
3245
        adfCoeffs[2] = -dfXMinus1 - adfCoeffs[1];                              \
3246
        /*adfCoeffs[3] = dfX * (-1 + dfX); */                                  \
3247
        adfCoeffs[3] = dfXMinus1 - adfCoeffs[0];                               \
3248
    }
3249
3250
// TODO(schwehr): Use an inline function.
3251
#define CONVOL4_Optim2MAX(adfCoeffs, v, dfHalfX)                               \
3252
    ((v)[1] + (dfHalfX) * ((adfCoeffs)[0] * (v)[0] + (adfCoeffs)[1] * (v)[1] + \
3253
                           (adfCoeffs)[2] * (v)[2] + (adfCoeffs)[3] * (v)[3]))
3254
#endif
3255
3256
static bool GWKCubicResample4Sample(const GDALWarpKernel *poWK, int iBand,
3257
                                    double dfSrcX, double dfSrcY,
3258
                                    double *pdfDensity, double *pdfReal,
3259
                                    double *pdfImag)
3260
3261
0
{
3262
0
    const int iSrcX = static_cast<int>(dfSrcX - 0.5);
3263
0
    const int iSrcY = static_cast<int>(dfSrcY - 0.5);
3264
0
    GPtrDiff_t iSrcOffset =
3265
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
3266
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
3267
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
3268
0
    double adfDensity[4] = {};
3269
0
    double adfReal[4] = {};
3270
0
    double adfImag[4] = {};
3271
3272
    // Get the bilinear interpolation at the image borders.
3273
0
    if (iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize || iSrcY - 1 < 0 ||
3274
0
        iSrcY + 2 >= poWK->nSrcYSize)
3275
0
        return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3276
0
                                          pdfDensity, pdfReal, pdfImag);
3277
3278
0
    double adfValueDens[4] = {};
3279
0
    double adfValueReal[4] = {};
3280
0
    double adfValueImag[4] = {};
3281
3282
0
    double adfCoeffsX[4] = {};
3283
0
    GWKCubicComputeWeights(dfDeltaX, adfCoeffsX);
3284
3285
0
    for (GPtrDiff_t i = -1; i < 3; i++)
3286
0
    {
3287
0
        if (!GWKGetPixelRow(poWK, iBand, iSrcOffset + i * poWK->nSrcXSize - 1,
3288
0
                            2, adfDensity, adfReal, adfImag) ||
3289
0
            adfDensity[0] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3290
0
            adfDensity[1] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3291
0
            adfDensity[2] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3292
0
            adfDensity[3] < SRC_DENSITY_THRESHOLD_DOUBLE)
3293
0
        {
3294
0
            return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3295
0
                                              pdfDensity, pdfReal, pdfImag);
3296
0
        }
3297
3298
0
        adfValueDens[i + 1] = CONVOL4(adfCoeffsX, adfDensity);
3299
0
        adfValueReal[i + 1] = CONVOL4(adfCoeffsX, adfReal);
3300
0
        adfValueImag[i + 1] = CONVOL4(adfCoeffsX, adfImag);
3301
0
    }
3302
3303
    /* -------------------------------------------------------------------- */
3304
    /*      For now, if we have any pixels missing in the kernel area,      */
3305
    /*      we fallback on using bilinear interpolation.  Ideally we        */
3306
    /*      should do "weight adjustment" of our results similarly to       */
3307
    /*      what is done for the cubic spline and lanc. interpolators.      */
3308
    /* -------------------------------------------------------------------- */
3309
3310
0
    double adfCoeffsY[4] = {};
3311
0
    GWKCubicComputeWeights(dfDeltaY, adfCoeffsY);
3312
3313
0
    *pdfDensity = CONVOL4(adfCoeffsY, adfValueDens);
3314
0
    *pdfReal = CONVOL4(adfCoeffsY, adfValueReal);
3315
0
    *pdfImag = CONVOL4(adfCoeffsY, adfValueImag);
3316
3317
0
    return true;
3318
0
}
3319
3320
#ifdef USE_SSE2
3321
3322
/************************************************************************/
3323
/*                           XMMLoad4Values()                           */
3324
/*                                                                      */
3325
/*  Load 4 packed byte or uint16, cast them to float and put them in a  */
3326
/*  m128 register.                                                      */
3327
/************************************************************************/
3328
3329
static CPL_INLINE __m128 XMMLoad4Values(const GByte *ptr)
3330
0
{
3331
0
    unsigned int i;
3332
0
    memcpy(&i, ptr, 4);
3333
0
    __m128i xmm_i = _mm_cvtsi32_si128(i);
3334
    // Zero extend 4 packed unsigned 8-bit integers in a to packed
3335
    // 32-bit integers.
3336
#if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
3337
    xmm_i = _mm_cvtepu8_epi32(xmm_i);
3338
#else
3339
0
    xmm_i = _mm_unpacklo_epi8(xmm_i, _mm_setzero_si128());
3340
0
    xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128());
3341
0
#endif
3342
0
    return _mm_cvtepi32_ps(xmm_i);
3343
0
}
3344
3345
static CPL_INLINE __m128 XMMLoad4Values(const GUInt16 *ptr)
3346
0
{
3347
0
    GUInt64 i;
3348
0
    memcpy(&i, ptr, 8);
3349
0
    __m128i xmm_i = _mm_cvtsi64_si128(i);
3350
    // Zero extend 4 packed unsigned 16-bit integers in a to packed
3351
    // 32-bit integers.
3352
#if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
3353
    xmm_i = _mm_cvtepu16_epi32(xmm_i);
3354
#else
3355
0
    xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128());
3356
0
#endif
3357
0
    return _mm_cvtepi32_ps(xmm_i);
3358
0
}
3359
3360
/************************************************************************/
3361
/*                           XMMHorizontalAdd()                         */
3362
/*                                                                      */
3363
/*  Return the sum of the 4 floating points of the register.            */
3364
/************************************************************************/
3365
3366
#if defined(__SSE3__) || defined(USE_NEON_OPTIMIZATIONS)
3367
static CPL_INLINE float XMMHorizontalAdd(__m128 v)
3368
{
3369
    __m128 shuf = _mm_movehdup_ps(v);   // (v3   , v3   , v1   , v1)
3370
    __m128 sums = _mm_add_ps(v, shuf);  // (v3+v3, v3+v2, v1+v1, v1+v0)
3371
    shuf = _mm_movehl_ps(shuf, sums);   // (v3   , v3   , v3+v3, v3+v2)
3372
    sums = _mm_add_ss(sums, shuf);      // (v1+v0)+(v3+v2)
3373
    return _mm_cvtss_f32(sums);
3374
}
3375
#else
3376
static CPL_INLINE float XMMHorizontalAdd(__m128 v)
3377
0
{
3378
0
    __m128 shuf = _mm_movehl_ps(v, v);     // (v3   , v2   , v3   , v2)
3379
0
    __m128 sums = _mm_add_ps(v, shuf);     // (v3+v3, v2+v2, v3+v1, v2+v0)
3380
0
    shuf = _mm_shuffle_ps(sums, sums, 1);  // (v2+v0, v2+v0, v2+v0, v3+v1)
3381
0
    sums = _mm_add_ss(sums, shuf);         // (v2+v0)+(v3+v1)
3382
0
    return _mm_cvtss_f32(sums);
3383
0
}
3384
#endif
3385
3386
#endif  // define USE_SSE2
3387
3388
/************************************************************************/
3389
/*            GWKCubicResampleSrcMaskIsDensity4SampleRealT()            */
3390
/************************************************************************/
3391
3392
// Note: if USE_SSE_CUBIC_IMPL, only instantiate that for Byte and UInt16,
3393
// because there are a few assumptions above those types.
3394
// We do not define USE_SSE_CUBIC_IMPL since in practice, it gives zero
3395
// perf benefit.
3396
3397
template <class T>
3398
static CPL_INLINE bool GWKCubicResampleSrcMaskIsDensity4SampleRealT(
3399
    const GDALWarpKernel *poWK, int iBand, double dfSrcX, double dfSrcY,
3400
    double *pdfDensity, double *pdfReal)
3401
0
{
3402
0
    const int iSrcX = static_cast<int>(dfSrcX - 0.5);
3403
0
    const int iSrcY = static_cast<int>(dfSrcY - 0.5);
3404
0
    const GPtrDiff_t iSrcOffset =
3405
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
3406
3407
    // Get the bilinear interpolation at the image borders.
3408
0
    if (iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize || iSrcY - 1 < 0 ||
3409
0
        iSrcY + 2 >= poWK->nSrcYSize)
3410
0
    {
3411
0
        double adfImagIgnored[4] = {};
3412
0
        return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3413
0
                                          pdfDensity, pdfReal, adfImagIgnored);
3414
0
    }
3415
3416
#if defined(USE_SSE_CUBIC_IMPL) && defined(USE_SSE2)
3417
    const float fDeltaX = static_cast<float>(dfSrcX) - 0.5f - iSrcX;
3418
    const float fDeltaY = static_cast<float>(dfSrcY) - 0.5f - iSrcY;
3419
3420
    // TODO(schwehr): Explain the magic numbers.
3421
    float afTemp[4 + 4 + 4 + 1];
3422
    float *pafAligned =
3423
        reinterpret_cast<float *>(afTemp + ((size_t)afTemp & 0xf));
3424
    float *pafCoeffs = pafAligned;
3425
    float *pafDensity = pafAligned + 4;
3426
    float *pafValue = pafAligned + 8;
3427
3428
    const float fHalfDeltaX = 0.5f * fDeltaX;
3429
    const float fThreeDeltaX = 3.0f * fDeltaX;
3430
    const float fHalfDeltaX2 = fHalfDeltaX * fDeltaX;
3431
3432
    pafCoeffs[0] = fHalfDeltaX * (-1 + fDeltaX * (2 - fDeltaX));
3433
    pafCoeffs[1] = 1 + fHalfDeltaX2 * (-5 + fThreeDeltaX);
3434
    pafCoeffs[2] = fHalfDeltaX * (1 + fDeltaX * (4 - fThreeDeltaX));
3435
    pafCoeffs[3] = fHalfDeltaX2 * (-1 + fDeltaX);
3436
    __m128 xmmCoeffs = _mm_load_ps(pafCoeffs);
3437
    const __m128 xmmThreshold = _mm_load1_ps(&SRC_DENSITY_THRESHOLD_FLOAT);
3438
3439
    __m128 xmmMaskLowDensity = _mm_setzero_ps();
3440
    for (GPtrDiff_t i = -1, iOffset = iSrcOffset - poWK->nSrcXSize - 1; i < 3;
3441
         i++, iOffset += poWK->nSrcXSize)
3442
    {
3443
        const __m128 xmmDensity =
3444
            _mm_loadu_ps(poWK->pafUnifiedSrcDensity + iOffset);
3445
        xmmMaskLowDensity = _mm_or_ps(xmmMaskLowDensity,
3446
                                      _mm_cmplt_ps(xmmDensity, xmmThreshold));
3447
        pafDensity[i + 1] = XMMHorizontalAdd(_mm_mul_ps(xmmCoeffs, xmmDensity));
3448
3449
        const __m128 xmmValues =
3450
            XMMLoad4Values(((T *)poWK->papabySrcImage[iBand]) + iOffset);
3451
        pafValue[i + 1] = XMMHorizontalAdd(_mm_mul_ps(xmmCoeffs, xmmValues));
3452
    }
3453
    if (_mm_movemask_ps(xmmMaskLowDensity))
3454
    {
3455
        double adfImagIgnored[4] = {};
3456
        return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3457
                                          pdfDensity, pdfReal, adfImagIgnored);
3458
    }
3459
3460
    const float fHalfDeltaY = 0.5f * fDeltaY;
3461
    const float fThreeDeltaY = 3.0f * fDeltaY;
3462
    const float fHalfDeltaY2 = fHalfDeltaY * fDeltaY;
3463
3464
    pafCoeffs[0] = fHalfDeltaY * (-1 + fDeltaY * (2 - fDeltaY));
3465
    pafCoeffs[1] = 1 + fHalfDeltaY2 * (-5 + fThreeDeltaY);
3466
    pafCoeffs[2] = fHalfDeltaY * (1 + fDeltaY * (4 - fThreeDeltaY));
3467
    pafCoeffs[3] = fHalfDeltaY2 * (-1 + fDeltaY);
3468
3469
    xmmCoeffs = _mm_load_ps(pafCoeffs);
3470
3471
    const __m128 xmmDensity = _mm_load_ps(pafDensity);
3472
    const __m128 xmmValue = _mm_load_ps(pafValue);
3473
    *pdfDensity = XMMHorizontalAdd(_mm_mul_ps(xmmCoeffs, xmmDensity));
3474
    *pdfReal = XMMHorizontalAdd(_mm_mul_ps(xmmCoeffs, xmmValue));
3475
3476
    // We did all above computations on float32 whereas the general case is
3477
    // float64. Not sure if one is fundamentally more correct than the other
3478
    // one, but we want our optimization to give the same result as the
3479
    // general case as much as possible, so if the resulting value is
3480
    // close to some_int_value + 0.5, redo the computation with the general
3481
    // case.
3482
    // Note: If other types than Byte or UInt16, will need changes.
3483
    if (fabs(*pdfReal - static_cast<int>(*pdfReal) - 0.5) > .007)
3484
        return true;
3485
3486
#endif  // defined(USE_SSE_CUBIC_IMPL) && defined(USE_SSE2)
3487
3488
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
3489
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
3490
3491
0
    double adfValueDens[4] = {};
3492
0
    double adfValueReal[4] = {};
3493
3494
0
    double adfCoeffsX[4] = {};
3495
0
    GWKCubicComputeWeights(dfDeltaX, adfCoeffsX);
3496
3497
0
    double adfCoeffsY[4] = {};
3498
0
    GWKCubicComputeWeights(dfDeltaY, adfCoeffsY);
3499
3500
0
    for (GPtrDiff_t i = -1; i < 3; i++)
3501
0
    {
3502
0
        const GPtrDiff_t iOffset = iSrcOffset + i * poWK->nSrcXSize - 1;
3503
0
#if !(defined(USE_SSE_CUBIC_IMPL) && defined(USE_SSE2))
3504
0
        if (poWK->pafUnifiedSrcDensity[iOffset + 0] <
3505
0
                SRC_DENSITY_THRESHOLD_FLOAT ||
3506
0
            poWK->pafUnifiedSrcDensity[iOffset + 1] <
3507
0
                SRC_DENSITY_THRESHOLD_FLOAT ||
3508
0
            poWK->pafUnifiedSrcDensity[iOffset + 2] <
3509
0
                SRC_DENSITY_THRESHOLD_FLOAT ||
3510
0
            poWK->pafUnifiedSrcDensity[iOffset + 3] <
3511
0
                SRC_DENSITY_THRESHOLD_FLOAT)
3512
0
        {
3513
0
            double adfImagIgnored[4] = {};
3514
0
            return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3515
0
                                              pdfDensity, pdfReal,
3516
0
                                              adfImagIgnored);
3517
0
        }
3518
0
#endif
3519
3520
0
        adfValueDens[i + 1] =
3521
0
            CONVOL4(adfCoeffsX, poWK->pafUnifiedSrcDensity + iOffset);
3522
3523
0
        adfValueReal[i + 1] = CONVOL4(
3524
0
            adfCoeffsX,
3525
0
            reinterpret_cast<T *>(poWK->papabySrcImage[iBand]) + iOffset);
3526
0
    }
3527
3528
0
    *pdfDensity = CONVOL4(adfCoeffsY, adfValueDens);
3529
0
    *pdfReal = CONVOL4(adfCoeffsY, adfValueReal);
3530
3531
0
    return true;
3532
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleSrcMaskIsDensity4SampleRealT<unsigned char>(GDALWarpKernel const*, int, double, double, double*, double*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleSrcMaskIsDensity4SampleRealT<unsigned short>(GDALWarpKernel const*, int, double, double, double*, double*)
3533
3534
/************************************************************************/
3535
/*              GWKCubicResampleSrcMaskIsDensity4SampleReal()             */
3536
/*     Bi-cubic when source has and only has pafUnifiedSrcDensity.      */
3537
/************************************************************************/
3538
3539
static bool GWKCubicResampleSrcMaskIsDensity4SampleReal(
3540
    const GDALWarpKernel *poWK, int iBand, double dfSrcX, double dfSrcY,
3541
    double *pdfDensity, double *pdfReal)
3542
3543
0
{
3544
0
    const int iSrcX = static_cast<int>(dfSrcX - 0.5);
3545
0
    const int iSrcY = static_cast<int>(dfSrcY - 0.5);
3546
0
    const GPtrDiff_t iSrcOffset =
3547
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
3548
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
3549
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
3550
3551
    // Get the bilinear interpolation at the image borders.
3552
0
    if (iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize || iSrcY - 1 < 0 ||
3553
0
        iSrcY + 2 >= poWK->nSrcYSize)
3554
0
    {
3555
0
        double adfImagIgnored[4] = {};
3556
0
        return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3557
0
                                          pdfDensity, pdfReal, adfImagIgnored);
3558
0
    }
3559
3560
0
    double adfCoeffsX[4] = {};
3561
0
    GWKCubicComputeWeights(dfDeltaX, adfCoeffsX);
3562
3563
0
    double adfCoeffsY[4] = {};
3564
0
    GWKCubicComputeWeights(dfDeltaY, adfCoeffsY);
3565
3566
0
    double adfValueDens[4] = {};
3567
0
    double adfValueReal[4] = {};
3568
0
    double adfDensity[4] = {};
3569
0
    double adfReal[4] = {};
3570
0
    double adfImagIgnored[4] = {};
3571
3572
0
    for (GPtrDiff_t i = -1; i < 3; i++)
3573
0
    {
3574
0
        if (!GWKGetPixelRow(poWK, iBand, iSrcOffset + i * poWK->nSrcXSize - 1,
3575
0
                            2, adfDensity, adfReal, adfImagIgnored) ||
3576
0
            adfDensity[0] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3577
0
            adfDensity[1] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3578
0
            adfDensity[2] < SRC_DENSITY_THRESHOLD_DOUBLE ||
3579
0
            adfDensity[3] < SRC_DENSITY_THRESHOLD_DOUBLE)
3580
0
        {
3581
0
            return GWKBilinearResample4Sample(poWK, iBand, dfSrcX, dfSrcY,
3582
0
                                              pdfDensity, pdfReal,
3583
0
                                              adfImagIgnored);
3584
0
        }
3585
3586
0
        adfValueDens[i + 1] = CONVOL4(adfCoeffsX, adfDensity);
3587
0
        adfValueReal[i + 1] = CONVOL4(adfCoeffsX, adfReal);
3588
0
    }
3589
3590
0
    *pdfDensity = CONVOL4(adfCoeffsY, adfValueDens);
3591
0
    *pdfReal = CONVOL4(adfCoeffsY, adfValueReal);
3592
3593
0
    return true;
3594
0
}
3595
3596
template <class T>
3597
static bool GWKCubicResampleNoMasks4SampleT(const GDALWarpKernel *poWK,
3598
                                            int iBand, double dfSrcX,
3599
                                            double dfSrcY, T *pValue)
3600
3601
0
{
3602
0
    const int iSrcX = static_cast<int>(dfSrcX - 0.5);
3603
0
    const int iSrcY = static_cast<int>(dfSrcY - 0.5);
3604
0
    const GPtrDiff_t iSrcOffset =
3605
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
3606
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
3607
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
3608
0
    const double dfDeltaY2 = dfDeltaY * dfDeltaY;
3609
0
    const double dfDeltaY3 = dfDeltaY2 * dfDeltaY;
3610
3611
    // Get the bilinear interpolation at the image borders.
3612
0
    if (iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize || iSrcY - 1 < 0 ||
3613
0
        iSrcY + 2 >= poWK->nSrcYSize)
3614
0
        return GWKBilinearResampleNoMasks4SampleT(poWK, iBand, dfSrcX, dfSrcY,
3615
0
                                                  pValue);
3616
3617
0
    double adfCoeffs[4] = {};
3618
0
    GWKCubicComputeWeights(dfDeltaX, adfCoeffs);
3619
3620
0
    double adfValue[4] = {};
3621
3622
0
    for (GPtrDiff_t i = -1; i < 3; i++)
3623
0
    {
3624
0
        const GPtrDiff_t iOffset = iSrcOffset + i * poWK->nSrcXSize - 1;
3625
3626
0
        adfValue[i + 1] = CONVOL4(
3627
0
            adfCoeffs,
3628
0
            reinterpret_cast<T *>(poWK->papabySrcImage[iBand]) + iOffset);
3629
0
    }
3630
3631
0
    const double dfValue =
3632
0
        CubicConvolution(dfDeltaY, dfDeltaY2, dfDeltaY3, adfValue[0],
3633
0
                         adfValue[1], adfValue[2], adfValue[3]);
3634
3635
0
    *pValue = GWKClampValueT<T>(dfValue);
3636
3637
0
    return true;
3638
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleNoMasks4SampleT<unsigned char>(GDALWarpKernel const*, int, double, double, unsigned char*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleNoMasks4SampleT<float>(GDALWarpKernel const*, int, double, double, float*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleNoMasks4SampleT<short>(GDALWarpKernel const*, int, double, double, short*)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKCubicResampleNoMasks4SampleT<unsigned short>(GDALWarpKernel const*, int, double, double, unsigned short*)
3639
3640
/************************************************************************/
3641
/*                           GWKLanczosSinc()                           */
3642
/************************************************************************/
3643
3644
/*
3645
 * Lanczos windowed sinc interpolation kernel with radius r.
3646
 *        /
3647
 *        | sinc(x) * sinc(x/r), if |x| < r
3648
 * L(x) = | 1, if x = 0                     ,
3649
 *        | 0, otherwise
3650
 *        \
3651
 *
3652
 * where sinc(x) = sin(PI * x) / (PI * x).
3653
 */
3654
3655
static double GWKLanczosSinc(double dfX)
3656
0
{
3657
0
    if (dfX == 0.0)
3658
0
        return 1.0;
3659
3660
0
    const double dfPIX = M_PI * dfX;
3661
0
    const double dfPIXoverR = dfPIX / 3;
3662
0
    const double dfPIX2overR = dfPIX * dfPIXoverR;
3663
    // Given that sin(3x) = 3 sin(x) - 4 sin^3 (x)
3664
    // we can compute sin(dfSinPIX) from sin(dfPIXoverR)
3665
0
    const double dfSinPIXoverR = sin(dfPIXoverR);
3666
0
    const double dfSinPIXoverRSquared = dfSinPIXoverR * dfSinPIXoverR;
3667
0
    const double dfSinPIXMulSinPIXoverR =
3668
0
        (3 - 4 * dfSinPIXoverRSquared) * dfSinPIXoverRSquared;
3669
0
    return dfSinPIXMulSinPIXoverR / dfPIX2overR;
3670
0
}
3671
3672
static double GWKLanczosSinc4Values(double *padfValues)
3673
0
{
3674
0
    for (int i = 0; i < 4; i++)
3675
0
    {
3676
0
        if (padfValues[i] == 0.0)
3677
0
        {
3678
0
            padfValues[i] = 1.0;
3679
0
        }
3680
0
        else
3681
0
        {
3682
0
            const double dfPIX = M_PI * padfValues[i];
3683
0
            const double dfPIXoverR = dfPIX / 3;
3684
0
            const double dfPIX2overR = dfPIX * dfPIXoverR;
3685
            // Given that sin(3x) = 3 sin(x) - 4 sin^3 (x)
3686
            // we can compute sin(dfSinPIX) from sin(dfPIXoverR)
3687
0
            const double dfSinPIXoverR = sin(dfPIXoverR);
3688
0
            const double dfSinPIXoverRSquared = dfSinPIXoverR * dfSinPIXoverR;
3689
0
            const double dfSinPIXMulSinPIXoverR =
3690
0
                (3 - 4 * dfSinPIXoverRSquared) * dfSinPIXoverRSquared;
3691
0
            padfValues[i] = dfSinPIXMulSinPIXoverR / dfPIX2overR;
3692
0
        }
3693
0
    }
3694
0
    return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3];
3695
0
}
3696
3697
/************************************************************************/
3698
/*                            GWKBilinear()                             */
3699
/************************************************************************/
3700
3701
static double GWKBilinear(double dfX)
3702
0
{
3703
0
    double dfAbsX = fabs(dfX);
3704
0
    if (dfAbsX <= 1.0)
3705
0
        return 1 - dfAbsX;
3706
0
    else
3707
0
        return 0.0;
3708
0
}
3709
3710
static double GWKBilinear4Values(double *padfValues)
3711
0
{
3712
0
    double dfAbsX0 = fabs(padfValues[0]);
3713
0
    double dfAbsX1 = fabs(padfValues[1]);
3714
0
    double dfAbsX2 = fabs(padfValues[2]);
3715
0
    double dfAbsX3 = fabs(padfValues[3]);
3716
0
    if (dfAbsX0 <= 1.0)
3717
0
        padfValues[0] = 1 - dfAbsX0;
3718
0
    else
3719
0
        padfValues[0] = 0.0;
3720
0
    if (dfAbsX1 <= 1.0)
3721
0
        padfValues[1] = 1 - dfAbsX1;
3722
0
    else
3723
0
        padfValues[1] = 0.0;
3724
0
    if (dfAbsX2 <= 1.0)
3725
0
        padfValues[2] = 1 - dfAbsX2;
3726
0
    else
3727
0
        padfValues[2] = 0.0;
3728
0
    if (dfAbsX3 <= 1.0)
3729
0
        padfValues[3] = 1 - dfAbsX3;
3730
0
    else
3731
0
        padfValues[3] = 0.0;
3732
0
    return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3];
3733
0
}
3734
3735
/************************************************************************/
3736
/*                              GWKCubic()                              */
3737
/************************************************************************/
3738
3739
static double GWKCubic(double dfX)
3740
0
{
3741
0
    return CubicKernel(dfX);
3742
0
}
3743
3744
static double GWKCubic4Values(double *padfValues)
3745
0
{
3746
0
    const double dfAbsX_0 = fabs(padfValues[0]);
3747
0
    const double dfAbsX_1 = fabs(padfValues[1]);
3748
0
    const double dfAbsX_2 = fabs(padfValues[2]);
3749
0
    const double dfAbsX_3 = fabs(padfValues[3]);
3750
0
    const double dfX2_0 = padfValues[0] * padfValues[0];
3751
0
    const double dfX2_1 = padfValues[1] * padfValues[1];
3752
0
    const double dfX2_2 = padfValues[2] * padfValues[2];
3753
0
    const double dfX2_3 = padfValues[3] * padfValues[3];
3754
3755
0
    double dfVal0 = 0.0;
3756
0
    if (dfAbsX_0 <= 1.0)
3757
0
        dfVal0 = dfX2_0 * (1.5 * dfAbsX_0 - 2.5) + 1.0;
3758
0
    else if (dfAbsX_0 <= 2.0)
3759
0
        dfVal0 = dfX2_0 * (-0.5 * dfAbsX_0 + 2.5) - 4.0 * dfAbsX_0 + 2.0;
3760
3761
0
    double dfVal1 = 0.0;
3762
0
    if (dfAbsX_1 <= 1.0)
3763
0
        dfVal1 = dfX2_1 * (1.5 * dfAbsX_1 - 2.5) + 1.0;
3764
0
    else if (dfAbsX_1 <= 2.0)
3765
0
        dfVal1 = dfX2_1 * (-0.5 * dfAbsX_1 + 2.5) - 4.0 * dfAbsX_1 + 2.0;
3766
3767
0
    double dfVal2 = 0.0;
3768
0
    if (dfAbsX_2 <= 1.0)
3769
0
        dfVal2 = dfX2_2 * (1.5 * dfAbsX_2 - 2.5) + 1.0;
3770
0
    else if (dfAbsX_2 <= 2.0)
3771
0
        dfVal2 = dfX2_2 * (-0.5 * dfAbsX_2 + 2.5) - 4.0 * dfAbsX_2 + 2.0;
3772
3773
0
    double dfVal3 = 0.0;
3774
0
    if (dfAbsX_3 <= 1.0)
3775
0
        dfVal3 = dfX2_3 * (1.5 * dfAbsX_3 - 2.5) + 1.0;
3776
0
    else if (dfAbsX_3 <= 2.0)
3777
0
        dfVal3 = dfX2_3 * (-0.5 * dfAbsX_3 + 2.5) - 4.0 * dfAbsX_3 + 2.0;
3778
3779
0
    padfValues[0] = dfVal0;
3780
0
    padfValues[1] = dfVal1;
3781
0
    padfValues[2] = dfVal2;
3782
0
    padfValues[3] = dfVal3;
3783
0
    return dfVal0 + dfVal1 + dfVal2 + dfVal3;
3784
0
}
3785
3786
/************************************************************************/
3787
/*                             GWKBSpline()                             */
3788
/************************************************************************/
3789
3790
// https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
3791
// Equation 8 with (B,C)=(1,0)
3792
// 1/6 * ( 3 * |x|^3 -  6 * |x|^2 + 4) |x| < 1
3793
// 1/6 * ( -|x|^3 + 6 |x|^2  - 12|x| + 8) |x| >= 1 and |x| < 2
3794
3795
static double GWKBSpline(double x)
3796
0
{
3797
0
    const double xp2 = x + 2.0;
3798
0
    const double xp1 = x + 1.0;
3799
0
    const double xm1 = x - 1.0;
3800
3801
    // This will most likely be used, so we'll compute it ahead of time to
3802
    // avoid stalling the processor.
3803
0
    const double xp2c = xp2 * xp2 * xp2;
3804
3805
    // Note that the test is computed only if it is needed.
3806
    // TODO(schwehr): Make this easier to follow.
3807
0
    return xp2 > 0.0
3808
0
               ? ((xp1 > 0.0)
3809
0
                      ? ((x > 0.0)
3810
0
                             ? ((xm1 > 0.0) ? -4.0 * xm1 * xm1 * xm1 : 0.0) +
3811
0
                                   6.0 * x * x * x
3812
0
                             : 0.0) +
3813
0
                            -4.0 * xp1 * xp1 * xp1
3814
0
                      : 0.0) +
3815
0
                     xp2c
3816
0
               : 0.0;  // * 0.166666666666666666666
3817
0
}
3818
3819
static double GWKBSpline4Values(double *padfValues)
3820
0
{
3821
0
    for (int i = 0; i < 4; i++)
3822
0
    {
3823
0
        const double x = padfValues[i];
3824
0
        const double xp2 = x + 2.0;
3825
0
        const double xp1 = x + 1.0;
3826
0
        const double xm1 = x - 1.0;
3827
3828
        // This will most likely be used, so we'll compute it ahead of time to
3829
        // avoid stalling the processor.
3830
0
        const double xp2c = xp2 * xp2 * xp2;
3831
3832
        // Note that the test is computed only if it is needed.
3833
        // TODO(schwehr): Make this easier to follow.
3834
0
        padfValues[i] =
3835
0
            (xp2 > 0.0)
3836
0
                ? ((xp1 > 0.0)
3837
0
                       ? ((x > 0.0)
3838
0
                              ? ((xm1 > 0.0) ? -4.0 * xm1 * xm1 * xm1 : 0.0) +
3839
0
                                    6.0 * x * x * x
3840
0
                              : 0.0) +
3841
0
                             -4.0 * xp1 * xp1 * xp1
3842
0
                       : 0.0) +
3843
0
                      xp2c
3844
0
                : 0.0;  // * 0.166666666666666666666
3845
0
    }
3846
0
    return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3];
3847
0
}
3848
/************************************************************************/
3849
/*                         GWKResampleWrkStruct                         */
3850
/************************************************************************/
3851
3852
typedef struct _GWKResampleWrkStruct GWKResampleWrkStruct;
3853
3854
typedef bool (*pfnGWKResampleType)(const GDALWarpKernel *poWK, int iBand,
3855
                                   double dfSrcX, double dfSrcY,
3856
                                   double *pdfDensity, double *pdfReal,
3857
                                   double *pdfImag,
3858
                                   GWKResampleWrkStruct *psWrkStruct);
3859
3860
struct _GWKResampleWrkStruct
3861
{
3862
    pfnGWKResampleType pfnGWKResample;
3863
3864
    // Space for saved X weights.
3865
    double *padfWeightsX;
3866
    bool *pabCalcX;
3867
3868
    double *padfWeightsY;       // Only used by GWKResampleOptimizedLanczos.
3869
    int iLastSrcX;              // Only used by GWKResampleOptimizedLanczos.
3870
    int iLastSrcY;              // Only used by GWKResampleOptimizedLanczos.
3871
    double dfLastDeltaX;        // Only used by GWKResampleOptimizedLanczos.
3872
    double dfLastDeltaY;        // Only used by GWKResampleOptimizedLanczos.
3873
    double dfCosPiXScale;       // Only used by GWKResampleOptimizedLanczos.
3874
    double dfSinPiXScale;       // Only used by GWKResampleOptimizedLanczos.
3875
    double dfCosPiXScaleOver3;  // Only used by GWKResampleOptimizedLanczos.
3876
    double dfSinPiXScaleOver3;  // Only used by GWKResampleOptimizedLanczos.
3877
    double dfCosPiYScale;       // Only used by GWKResampleOptimizedLanczos.
3878
    double dfSinPiYScale;       // Only used by GWKResampleOptimizedLanczos.
3879
    double dfCosPiYScaleOver3;  // Only used by GWKResampleOptimizedLanczos.
3880
    double dfSinPiYScaleOver3;  // Only used by GWKResampleOptimizedLanczos.
3881
3882
    // Space for saving a row of pixels.
3883
    double *padfRowDensity;
3884
    double *padfRowReal;
3885
    double *padfRowImag;
3886
};
3887
3888
/************************************************************************/
3889
/*                     GWKResampleCreateWrkStruct()                     */
3890
/************************************************************************/
3891
3892
static bool GWKResample(const GDALWarpKernel *poWK, int iBand, double dfSrcX,
3893
                        double dfSrcY, double *pdfDensity, double *pdfReal,
3894
                        double *pdfImag, GWKResampleWrkStruct *psWrkStruct);
3895
3896
static bool GWKResampleOptimizedLanczos(const GDALWarpKernel *poWK, int iBand,
3897
                                        double dfSrcX, double dfSrcY,
3898
                                        double *pdfDensity, double *pdfReal,
3899
                                        double *pdfImag,
3900
                                        GWKResampleWrkStruct *psWrkStruct);
3901
3902
static GWKResampleWrkStruct *GWKResampleCreateWrkStruct(GDALWarpKernel *poWK)
3903
0
{
3904
0
    const int nXDist = (poWK->nXRadius + 1) * 2;
3905
0
    const int nYDist = (poWK->nYRadius + 1) * 2;
3906
3907
0
    GWKResampleWrkStruct *psWrkStruct = static_cast<GWKResampleWrkStruct *>(
3908
0
        CPLCalloc(1, sizeof(GWKResampleWrkStruct)));
3909
3910
    // Alloc space for saved X weights.
3911
0
    psWrkStruct->padfWeightsX =
3912
0
        static_cast<double *>(CPLCalloc(nXDist, sizeof(double)));
3913
0
    psWrkStruct->pabCalcX =
3914
0
        static_cast<bool *>(CPLMalloc(nXDist * sizeof(bool)));
3915
3916
0
    psWrkStruct->padfWeightsY =
3917
0
        static_cast<double *>(CPLCalloc(nYDist, sizeof(double)));
3918
0
    psWrkStruct->iLastSrcX = -10;
3919
0
    psWrkStruct->iLastSrcY = -10;
3920
0
    psWrkStruct->dfLastDeltaX = -10;
3921
0
    psWrkStruct->dfLastDeltaY = -10;
3922
3923
    // Alloc space for saving a row of pixels.
3924
0
    if (poWK->pafUnifiedSrcDensity == nullptr &&
3925
0
        poWK->panUnifiedSrcValid == nullptr &&
3926
0
        poWK->papanBandSrcValid == nullptr)
3927
0
    {
3928
0
        psWrkStruct->padfRowDensity = nullptr;
3929
0
    }
3930
0
    else
3931
0
    {
3932
0
        psWrkStruct->padfRowDensity =
3933
0
            static_cast<double *>(CPLCalloc(nXDist, sizeof(double)));
3934
0
    }
3935
0
    psWrkStruct->padfRowReal =
3936
0
        static_cast<double *>(CPLCalloc(nXDist, sizeof(double)));
3937
0
    psWrkStruct->padfRowImag =
3938
0
        static_cast<double *>(CPLCalloc(nXDist, sizeof(double)));
3939
3940
0
    if (poWK->eResample == GRA_Lanczos)
3941
0
    {
3942
0
        psWrkStruct->pfnGWKResample = GWKResampleOptimizedLanczos;
3943
3944
0
        if (poWK->dfXScale < 1)
3945
0
        {
3946
0
            psWrkStruct->dfCosPiXScaleOver3 = cos(M_PI / 3 * poWK->dfXScale);
3947
0
            psWrkStruct->dfSinPiXScaleOver3 =
3948
0
                sqrt(1 - psWrkStruct->dfCosPiXScaleOver3 *
3949
0
                             psWrkStruct->dfCosPiXScaleOver3);
3950
            // "Naive":
3951
            // const double dfCosPiXScale = cos(  M_PI * dfXScale );
3952
            // const double dfSinPiXScale = sin(  M_PI * dfXScale );
3953
            // but given that cos(3x) = 4 cos^3(x) - 3 cos(x) and x between 0 and M_PI
3954
0
            psWrkStruct->dfCosPiXScale = (4 * psWrkStruct->dfCosPiXScaleOver3 *
3955
0
                                              psWrkStruct->dfCosPiXScaleOver3 -
3956
0
                                          3) *
3957
0
                                         psWrkStruct->dfCosPiXScaleOver3;
3958
0
            psWrkStruct->dfSinPiXScale = sqrt(
3959
0
                1 - psWrkStruct->dfCosPiXScale * psWrkStruct->dfCosPiXScale);
3960
0
        }
3961
3962
0
        if (poWK->dfYScale < 1)
3963
0
        {
3964
0
            psWrkStruct->dfCosPiYScaleOver3 = cos(M_PI / 3 * poWK->dfYScale);
3965
0
            psWrkStruct->dfSinPiYScaleOver3 =
3966
0
                sqrt(1 - psWrkStruct->dfCosPiYScaleOver3 *
3967
0
                             psWrkStruct->dfCosPiYScaleOver3);
3968
            // "Naive":
3969
            // const double dfCosPiYScale = cos(  M_PI * dfYScale );
3970
            // const double dfSinPiYScale = sin(  M_PI * dfYScale );
3971
            // but given that cos(3x) = 4 cos^3(x) - 3 cos(x) and x between 0 and M_PI
3972
0
            psWrkStruct->dfCosPiYScale = (4 * psWrkStruct->dfCosPiYScaleOver3 *
3973
0
                                              psWrkStruct->dfCosPiYScaleOver3 -
3974
0
                                          3) *
3975
0
                                         psWrkStruct->dfCosPiYScaleOver3;
3976
0
            psWrkStruct->dfSinPiYScale = sqrt(
3977
0
                1 - psWrkStruct->dfCosPiYScale * psWrkStruct->dfCosPiYScale);
3978
0
        }
3979
0
    }
3980
0
    else
3981
0
        psWrkStruct->pfnGWKResample = GWKResample;
3982
3983
0
    return psWrkStruct;
3984
0
}
3985
3986
/************************************************************************/
3987
/*                     GWKResampleDeleteWrkStruct()                     */
3988
/************************************************************************/
3989
3990
static void GWKResampleDeleteWrkStruct(GWKResampleWrkStruct *psWrkStruct)
3991
0
{
3992
0
    CPLFree(psWrkStruct->padfWeightsX);
3993
0
    CPLFree(psWrkStruct->padfWeightsY);
3994
0
    CPLFree(psWrkStruct->pabCalcX);
3995
0
    CPLFree(psWrkStruct->padfRowDensity);
3996
0
    CPLFree(psWrkStruct->padfRowReal);
3997
0
    CPLFree(psWrkStruct->padfRowImag);
3998
0
    CPLFree(psWrkStruct);
3999
0
}
4000
4001
/************************************************************************/
4002
/*                            GWKResample()                             */
4003
/************************************************************************/
4004
4005
static bool GWKResample(const GDALWarpKernel *poWK, int iBand, double dfSrcX,
4006
                        double dfSrcY, double *pdfDensity, double *pdfReal,
4007
                        double *pdfImag, GWKResampleWrkStruct *psWrkStruct)
4008
4009
0
{
4010
    // Save as local variables to avoid following pointers in loops.
4011
0
    const int nSrcXSize = poWK->nSrcXSize;
4012
0
    const int nSrcYSize = poWK->nSrcYSize;
4013
4014
0
    double dfAccumulatorReal = 0.0;
4015
0
    double dfAccumulatorImag = 0.0;
4016
0
    double dfAccumulatorDensity = 0.0;
4017
0
    double dfAccumulatorWeight = 0.0;
4018
0
    const int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
4019
0
    const int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
4020
0
    const GPtrDiff_t iSrcOffset =
4021
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
4022
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
4023
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
4024
4025
0
    const double dfXScale = poWK->dfXScale;
4026
0
    const double dfYScale = poWK->dfYScale;
4027
4028
0
    const int nXDist = (poWK->nXRadius + 1) * 2;
4029
4030
    // Space for saved X weights.
4031
0
    double *padfWeightsX = psWrkStruct->padfWeightsX;
4032
0
    bool *pabCalcX = psWrkStruct->pabCalcX;
4033
4034
    // Space for saving a row of pixels.
4035
0
    double *padfRowDensity = psWrkStruct->padfRowDensity;
4036
0
    double *padfRowReal = psWrkStruct->padfRowReal;
4037
0
    double *padfRowImag = psWrkStruct->padfRowImag;
4038
4039
    // Mark as needing calculation (don't calculate the weights yet,
4040
    // because a mask may render it unnecessary).
4041
0
    memset(pabCalcX, false, nXDist * sizeof(bool));
4042
4043
0
    FilterFuncType pfnGetWeight = apfGWKFilter[poWK->eResample];
4044
0
    CPLAssert(pfnGetWeight);
4045
4046
    // Skip sampling over edge of image.
4047
0
    int j = poWK->nFiltInitY;
4048
0
    int jMax = poWK->nYRadius;
4049
0
    if (iSrcY + j < 0)
4050
0
        j = -iSrcY;
4051
0
    if (iSrcY + jMax >= nSrcYSize)
4052
0
        jMax = nSrcYSize - iSrcY - 1;
4053
4054
0
    int iMin = poWK->nFiltInitX;
4055
0
    int iMax = poWK->nXRadius;
4056
0
    if (iSrcX + iMin < 0)
4057
0
        iMin = -iSrcX;
4058
0
    if (iSrcX + iMax >= nSrcXSize)
4059
0
        iMax = nSrcXSize - iSrcX - 1;
4060
4061
0
    const int bXScaleBelow1 = (dfXScale < 1.0);
4062
0
    const int bYScaleBelow1 = (dfYScale < 1.0);
4063
4064
0
    GPtrDiff_t iRowOffset =
4065
0
        iSrcOffset + static_cast<GPtrDiff_t>(j - 1) * nSrcXSize + iMin;
4066
4067
    // Loop over pixel rows in the kernel.
4068
0
    for (; j <= jMax; ++j)
4069
0
    {
4070
0
        iRowOffset += nSrcXSize;
4071
4072
        // Get pixel values.
4073
        // We can potentially read extra elements after the "normal" end of the
4074
        // source arrays, but the contract of papabySrcImage[iBand],
4075
        // papanBandSrcValid[iBand], panUnifiedSrcValid and pafUnifiedSrcDensity
4076
        // is to have WARP_EXTRA_ELTS reserved at their end.
4077
0
        if (!GWKGetPixelRow(poWK, iBand, iRowOffset, (iMax - iMin + 2) / 2,
4078
0
                            padfRowDensity, padfRowReal, padfRowImag))
4079
0
            continue;
4080
4081
        // Calculate the Y weight.
4082
0
        double dfWeight1 = (bYScaleBelow1)
4083
0
                               ? pfnGetWeight((j - dfDeltaY) * dfYScale)
4084
0
                               : pfnGetWeight(j - dfDeltaY);
4085
4086
        // Iterate over pixels in row.
4087
0
        double dfAccumulatorRealLocal = 0.0;
4088
0
        double dfAccumulatorImagLocal = 0.0;
4089
0
        double dfAccumulatorDensityLocal = 0.0;
4090
0
        double dfAccumulatorWeightLocal = 0.0;
4091
4092
0
        for (int i = iMin; i <= iMax; ++i)
4093
0
        {
4094
            // Skip sampling if pixel has zero density.
4095
0
            if (padfRowDensity != nullptr &&
4096
0
                padfRowDensity[i - iMin] < SRC_DENSITY_THRESHOLD_DOUBLE)
4097
0
                continue;
4098
4099
0
            double dfWeight2 = 0.0;
4100
4101
            // Make or use a cached set of weights for this row.
4102
0
            if (pabCalcX[i - iMin])
4103
0
            {
4104
                // Use saved weight value instead of recomputing it.
4105
0
                dfWeight2 = padfWeightsX[i - iMin];
4106
0
            }
4107
0
            else
4108
0
            {
4109
                // Calculate & save the X weight.
4110
0
                padfWeightsX[i - iMin] = dfWeight2 =
4111
0
                    (bXScaleBelow1) ? pfnGetWeight((i - dfDeltaX) * dfXScale)
4112
0
                                    : pfnGetWeight(i - dfDeltaX);
4113
4114
0
                pabCalcX[i - iMin] = true;
4115
0
            }
4116
4117
            // Accumulate!
4118
0
            dfAccumulatorRealLocal += padfRowReal[i - iMin] * dfWeight2;
4119
0
            dfAccumulatorImagLocal += padfRowImag[i - iMin] * dfWeight2;
4120
0
            if (padfRowDensity != nullptr)
4121
0
                dfAccumulatorDensityLocal +=
4122
0
                    padfRowDensity[i - iMin] * dfWeight2;
4123
0
            dfAccumulatorWeightLocal += dfWeight2;
4124
0
        }
4125
4126
0
        dfAccumulatorReal += dfAccumulatorRealLocal * dfWeight1;
4127
0
        dfAccumulatorImag += dfAccumulatorImagLocal * dfWeight1;
4128
0
        dfAccumulatorDensity += dfAccumulatorDensityLocal * dfWeight1;
4129
0
        dfAccumulatorWeight += dfAccumulatorWeightLocal * dfWeight1;
4130
0
    }
4131
4132
0
    if (dfAccumulatorWeight < 0.000001 ||
4133
0
        (padfRowDensity != nullptr && dfAccumulatorDensity < 0.000001))
4134
0
    {
4135
0
        *pdfDensity = 0.0;
4136
0
        return false;
4137
0
    }
4138
4139
    // Calculate the output taking into account weighting.
4140
0
    if (dfAccumulatorWeight < 0.99999 || dfAccumulatorWeight > 1.00001)
4141
0
    {
4142
0
        *pdfReal = dfAccumulatorReal / dfAccumulatorWeight;
4143
0
        *pdfImag = dfAccumulatorImag / dfAccumulatorWeight;
4144
0
        if (padfRowDensity != nullptr)
4145
0
            *pdfDensity = dfAccumulatorDensity / dfAccumulatorWeight;
4146
0
        else
4147
0
            *pdfDensity = 1.0;
4148
0
    }
4149
0
    else
4150
0
    {
4151
0
        *pdfReal = dfAccumulatorReal;
4152
0
        *pdfImag = dfAccumulatorImag;
4153
0
        if (padfRowDensity != nullptr)
4154
0
            *pdfDensity = dfAccumulatorDensity;
4155
0
        else
4156
0
            *pdfDensity = 1.0;
4157
0
    }
4158
4159
0
    return true;
4160
0
}
4161
4162
/************************************************************************/
4163
/*                    GWKResampleOptimizedLanczos()                     */
4164
/************************************************************************/
4165
4166
static bool GWKResampleOptimizedLanczos(const GDALWarpKernel *poWK, int iBand,
4167
                                        double dfSrcX, double dfSrcY,
4168
                                        double *pdfDensity, double *pdfReal,
4169
                                        double *pdfImag,
4170
                                        GWKResampleWrkStruct *psWrkStruct)
4171
4172
0
{
4173
    // Save as local variables to avoid following pointers in loops.
4174
0
    const int nSrcXSize = poWK->nSrcXSize;
4175
0
    const int nSrcYSize = poWK->nSrcYSize;
4176
4177
0
    double dfAccumulatorReal = 0.0;
4178
0
    double dfAccumulatorImag = 0.0;
4179
0
    double dfAccumulatorDensity = 0.0;
4180
0
    double dfAccumulatorWeight = 0.0;
4181
0
    const int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
4182
0
    const int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
4183
0
    const GPtrDiff_t iSrcOffset =
4184
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
4185
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
4186
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
4187
4188
0
    const double dfXScale = poWK->dfXScale;
4189
0
    const double dfYScale = poWK->dfYScale;
4190
4191
    // Space for saved X weights.
4192
0
    double *const padfWeightsXShifted =
4193
0
        psWrkStruct->padfWeightsX - poWK->nFiltInitX;
4194
0
    double *const padfWeightsYShifted =
4195
0
        psWrkStruct->padfWeightsY - poWK->nFiltInitY;
4196
4197
    // Space for saving a row of pixels.
4198
0
    double *const padfRowDensity = psWrkStruct->padfRowDensity;
4199
0
    double *const padfRowReal = psWrkStruct->padfRowReal;
4200
0
    double *const padfRowImag = psWrkStruct->padfRowImag;
4201
4202
    // Skip sampling over edge of image.
4203
0
    int jMin = poWK->nFiltInitY;
4204
0
    int jMax = poWK->nYRadius;
4205
0
    if (iSrcY + jMin < 0)
4206
0
        jMin = -iSrcY;
4207
0
    if (iSrcY + jMax >= nSrcYSize)
4208
0
        jMax = nSrcYSize - iSrcY - 1;
4209
4210
0
    int iMin = poWK->nFiltInitX;
4211
0
    int iMax = poWK->nXRadius;
4212
0
    if (iSrcX + iMin < 0)
4213
0
        iMin = -iSrcX;
4214
0
    if (iSrcX + iMax >= nSrcXSize)
4215
0
        iMax = nSrcXSize - iSrcX - 1;
4216
4217
0
    if (dfXScale < 1.0)
4218
0
    {
4219
0
        while ((iMin - dfDeltaX) * dfXScale < -3.0)
4220
0
            iMin++;
4221
0
        while ((iMax - dfDeltaX) * dfXScale > 3.0)
4222
0
            iMax--;
4223
4224
        // clang-format off
4225
        /*
4226
        Naive version:
4227
        for (int i = iMin; i <= iMax; ++i)
4228
        {
4229
            psWrkStruct->padfWeightsXShifted[i] =
4230
                GWKLanczosSinc((i - dfDeltaX) * dfXScale);
4231
        }
4232
4233
        but given that:
4234
4235
        GWKLanczosSinc(x):
4236
            if (dfX == 0.0)
4237
                return 1.0;
4238
4239
            const double dfPIX = M_PI * dfX;
4240
            const double dfPIXoverR = dfPIX / 3;
4241
            const double dfPIX2overR = dfPIX * dfPIXoverR;
4242
            return sin(dfPIX) * sin(dfPIXoverR) / dfPIX2overR;
4243
4244
        and
4245
            sin (a + b) = sin a cos b + cos a sin b.
4246
            cos (a + b) = cos a cos b - sin a sin b.
4247
4248
        we can skip any sin() computation within the loop
4249
        */
4250
        // clang-format on
4251
4252
0
        if (iSrcX != psWrkStruct->iLastSrcX ||
4253
0
            dfDeltaX != psWrkStruct->dfLastDeltaX)
4254
0
        {
4255
0
            double dfX = (iMin - dfDeltaX) * dfXScale;
4256
4257
0
            double dfPIXover3 = M_PI / 3 * dfX;
4258
0
            double dfCosOver3 = cos(dfPIXover3);
4259
0
            double dfSinOver3 = sin(dfPIXover3);
4260
4261
            // "Naive":
4262
            // double dfSin = sin( M_PI * dfX );
4263
            // double dfCos = cos( M_PI * dfX );
4264
            // but given that cos(3x) = 4 cos^3(x) - 3 cos(x) and sin(3x) = 3 sin(x) - 4 sin^3 (x).
4265
0
            double dfSin = (3 - 4 * dfSinOver3 * dfSinOver3) * dfSinOver3;
4266
0
            double dfCos = (4 * dfCosOver3 * dfCosOver3 - 3) * dfCosOver3;
4267
4268
0
            const double dfCosPiXScaleOver3 = psWrkStruct->dfCosPiXScaleOver3;
4269
0
            const double dfSinPiXScaleOver3 = psWrkStruct->dfSinPiXScaleOver3;
4270
0
            const double dfCosPiXScale = psWrkStruct->dfCosPiXScale;
4271
0
            const double dfSinPiXScale = psWrkStruct->dfSinPiXScale;
4272
0
            constexpr double THREE_PI_PI = 3 * M_PI * M_PI;
4273
0
            padfWeightsXShifted[iMin] =
4274
0
                dfX == 0 ? 1.0 : THREE_PI_PI * dfSin * dfSinOver3 / (dfX * dfX);
4275
0
            for (int i = iMin + 1; i <= iMax; ++i)
4276
0
            {
4277
0
                dfX += dfXScale;
4278
0
                const double dfNewSin =
4279
0
                    dfSin * dfCosPiXScale + dfCos * dfSinPiXScale;
4280
0
                const double dfNewSinOver3 = dfSinOver3 * dfCosPiXScaleOver3 +
4281
0
                                             dfCosOver3 * dfSinPiXScaleOver3;
4282
0
                padfWeightsXShifted[i] =
4283
0
                    dfX == 0
4284
0
                        ? 1.0
4285
0
                        : THREE_PI_PI * dfNewSin * dfNewSinOver3 / (dfX * dfX);
4286
0
                const double dfNewCos =
4287
0
                    dfCos * dfCosPiXScale - dfSin * dfSinPiXScale;
4288
0
                const double dfNewCosOver3 = dfCosOver3 * dfCosPiXScaleOver3 -
4289
0
                                             dfSinOver3 * dfSinPiXScaleOver3;
4290
0
                dfSin = dfNewSin;
4291
0
                dfCos = dfNewCos;
4292
0
                dfSinOver3 = dfNewSinOver3;
4293
0
                dfCosOver3 = dfNewCosOver3;
4294
0
            }
4295
4296
0
            psWrkStruct->iLastSrcX = iSrcX;
4297
0
            psWrkStruct->dfLastDeltaX = dfDeltaX;
4298
0
        }
4299
0
    }
4300
0
    else
4301
0
    {
4302
0
        while (iMin - dfDeltaX < -3.0)
4303
0
            iMin++;
4304
0
        while (iMax - dfDeltaX > 3.0)
4305
0
            iMax--;
4306
4307
0
        if (iSrcX != psWrkStruct->iLastSrcX ||
4308
0
            dfDeltaX != psWrkStruct->dfLastDeltaX)
4309
0
        {
4310
            // Optimisation of GWKLanczosSinc(i - dfDeltaX) based on the
4311
            // following trigonometric formulas.
4312
4313
            // TODO(schwehr): Move this somewhere where it can be rendered at
4314
            // LaTeX.
4315
            // clang-format off
4316
            // sin(M_PI * (dfBase + k)) = sin(M_PI * dfBase) * cos(M_PI * k) +
4317
            //                            cos(M_PI * dfBase) * sin(M_PI * k)
4318
            // sin(M_PI * (dfBase + k)) = dfSinPIBase * cos(M_PI * k) + dfCosPIBase * sin(M_PI * k)
4319
            // sin(M_PI * (dfBase + k)) = dfSinPIBase * cos(M_PI * k)
4320
            // sin(M_PI * (dfBase + k)) = dfSinPIBase * (((k % 2) == 0) ? 1 : -1)
4321
4322
            // sin(M_PI / dfR * (dfBase + k)) = sin(M_PI / dfR * dfBase) * cos(M_PI / dfR * k) +
4323
            //                                  cos(M_PI / dfR * dfBase) * sin(M_PI / dfR * k)
4324
            // sin(M_PI / dfR * (dfBase + k)) = dfSinPIBaseOverR * cos(M_PI / dfR * k) + dfCosPIBaseOverR * sin(M_PI / dfR * k)
4325
            // clang-format on
4326
4327
0
            const double dfSinPIDeltaXOver3 = sin((-M_PI / 3.0) * dfDeltaX);
4328
0
            const double dfSin2PIDeltaXOver3 =
4329
0
                dfSinPIDeltaXOver3 * dfSinPIDeltaXOver3;
4330
            // Ok to use sqrt(1-sin^2) since M_PI / 3 * dfDeltaX < PI/2.
4331
0
            const double dfCosPIDeltaXOver3 = sqrt(1.0 - dfSin2PIDeltaXOver3);
4332
0
            const double dfSinPIDeltaX =
4333
0
                (3.0 - 4 * dfSin2PIDeltaXOver3) * dfSinPIDeltaXOver3;
4334
0
            const double dfInvPI2Over3 = 3.0 / (M_PI * M_PI);
4335
0
            const double dfInvPI2Over3xSinPIDeltaX =
4336
0
                dfInvPI2Over3 * dfSinPIDeltaX;
4337
0
            const double dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 =
4338
0
                -0.5 * dfInvPI2Over3xSinPIDeltaX * dfSinPIDeltaXOver3;
4339
0
            const double dfSinPIOver3 = 0.8660254037844386;
4340
0
            const double dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3 =
4341
0
                dfSinPIOver3 * dfInvPI2Over3xSinPIDeltaX * dfCosPIDeltaXOver3;
4342
0
            const double padfCst[] = {
4343
0
                dfInvPI2Over3xSinPIDeltaX * dfSinPIDeltaXOver3,
4344
0
                dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 -
4345
0
                    dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3,
4346
0
                dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 +
4347
0
                    dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3};
4348
4349
0
            for (int i = iMin; i <= iMax; ++i)
4350
0
            {
4351
0
                const double dfX = i - dfDeltaX;
4352
0
                if (dfX == 0.0)
4353
0
                    padfWeightsXShifted[i] = 1.0;
4354
0
                else
4355
0
                    padfWeightsXShifted[i] = padfCst[(i + 3) % 3] / (dfX * dfX);
4356
#if DEBUG_VERBOSE
4357
                // TODO(schwehr): AlmostEqual.
4358
                // CPLAssert(fabs(padfWeightsX[i-poWK->nFiltInitX] -
4359
                //               GWKLanczosSinc(dfX, 3.0)) < 1e-10);
4360
#endif
4361
0
            }
4362
4363
0
            psWrkStruct->iLastSrcX = iSrcX;
4364
0
            psWrkStruct->dfLastDeltaX = dfDeltaX;
4365
0
        }
4366
0
    }
4367
4368
0
    if (dfYScale < 1.0)
4369
0
    {
4370
0
        while ((jMin - dfDeltaY) * dfYScale < -3.0)
4371
0
            jMin++;
4372
0
        while ((jMax - dfDeltaY) * dfYScale > 3.0)
4373
0
            jMax--;
4374
4375
        // clang-format off
4376
        /*
4377
        Naive version:
4378
        for (int j = jMin; j <= jMax; ++j)
4379
        {
4380
            padfWeightsYShifted[j] =
4381
                GWKLanczosSinc((j - dfDeltaY) * dfYScale);
4382
        }
4383
        */
4384
        // clang-format on
4385
4386
0
        if (iSrcY != psWrkStruct->iLastSrcY ||
4387
0
            dfDeltaY != psWrkStruct->dfLastDeltaY)
4388
0
        {
4389
0
            double dfY = (jMin - dfDeltaY) * dfYScale;
4390
4391
0
            double dfPIYover3 = M_PI / 3 * dfY;
4392
0
            double dfCosOver3 = cos(dfPIYover3);
4393
0
            double dfSinOver3 = sin(dfPIYover3);
4394
4395
            // "Naive":
4396
            // double dfSin = sin( M_PI * dfY );
4397
            // double dfCos = cos( M_PI * dfY );
4398
            // but given that cos(3x) = 4 cos^3(x) - 3 cos(x) and sin(3x) = 3 sin(x) - 4 sin^3 (x).
4399
0
            double dfSin = (3 - 4 * dfSinOver3 * dfSinOver3) * dfSinOver3;
4400
0
            double dfCos = (4 * dfCosOver3 * dfCosOver3 - 3) * dfCosOver3;
4401
4402
0
            const double dfCosPiYScaleOver3 = psWrkStruct->dfCosPiYScaleOver3;
4403
0
            const double dfSinPiYScaleOver3 = psWrkStruct->dfSinPiYScaleOver3;
4404
0
            const double dfCosPiYScale = psWrkStruct->dfCosPiYScale;
4405
0
            const double dfSinPiYScale = psWrkStruct->dfSinPiYScale;
4406
0
            constexpr double THREE_PI_PI = 3 * M_PI * M_PI;
4407
0
            padfWeightsYShifted[jMin] =
4408
0
                dfY == 0 ? 1.0 : THREE_PI_PI * dfSin * dfSinOver3 / (dfY * dfY);
4409
0
            for (int j = jMin + 1; j <= jMax; ++j)
4410
0
            {
4411
0
                dfY += dfYScale;
4412
0
                const double dfNewSin =
4413
0
                    dfSin * dfCosPiYScale + dfCos * dfSinPiYScale;
4414
0
                const double dfNewSinOver3 = dfSinOver3 * dfCosPiYScaleOver3 +
4415
0
                                             dfCosOver3 * dfSinPiYScaleOver3;
4416
0
                padfWeightsYShifted[j] =
4417
0
                    dfY == 0
4418
0
                        ? 1.0
4419
0
                        : THREE_PI_PI * dfNewSin * dfNewSinOver3 / (dfY * dfY);
4420
0
                const double dfNewCos =
4421
0
                    dfCos * dfCosPiYScale - dfSin * dfSinPiYScale;
4422
0
                const double dfNewCosOver3 = dfCosOver3 * dfCosPiYScaleOver3 -
4423
0
                                             dfSinOver3 * dfSinPiYScaleOver3;
4424
0
                dfSin = dfNewSin;
4425
0
                dfCos = dfNewCos;
4426
0
                dfSinOver3 = dfNewSinOver3;
4427
0
                dfCosOver3 = dfNewCosOver3;
4428
0
            }
4429
4430
0
            psWrkStruct->iLastSrcY = iSrcY;
4431
0
            psWrkStruct->dfLastDeltaY = dfDeltaY;
4432
0
        }
4433
0
    }
4434
0
    else
4435
0
    {
4436
0
        while (jMin - dfDeltaY < -3.0)
4437
0
            jMin++;
4438
0
        while (jMax - dfDeltaY > 3.0)
4439
0
            jMax--;
4440
4441
0
        if (iSrcY != psWrkStruct->iLastSrcY ||
4442
0
            dfDeltaY != psWrkStruct->dfLastDeltaY)
4443
0
        {
4444
0
            const double dfSinPIDeltaYOver3 = sin((-M_PI / 3.0) * dfDeltaY);
4445
0
            const double dfSin2PIDeltaYOver3 =
4446
0
                dfSinPIDeltaYOver3 * dfSinPIDeltaYOver3;
4447
            // Ok to use sqrt(1-sin^2) since M_PI / 3 * dfDeltaY < PI/2.
4448
0
            const double dfCosPIDeltaYOver3 = sqrt(1.0 - dfSin2PIDeltaYOver3);
4449
0
            const double dfSinPIDeltaY =
4450
0
                (3.0 - 4.0 * dfSin2PIDeltaYOver3) * dfSinPIDeltaYOver3;
4451
0
            const double dfInvPI2Over3 = 3.0 / (M_PI * M_PI);
4452
0
            const double dfInvPI2Over3xSinPIDeltaY =
4453
0
                dfInvPI2Over3 * dfSinPIDeltaY;
4454
0
            const double dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 =
4455
0
                -0.5 * dfInvPI2Over3xSinPIDeltaY * dfSinPIDeltaYOver3;
4456
0
            const double dfSinPIOver3 = 0.8660254037844386;
4457
0
            const double dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3 =
4458
0
                dfSinPIOver3 * dfInvPI2Over3xSinPIDeltaY * dfCosPIDeltaYOver3;
4459
0
            const double padfCst[] = {
4460
0
                dfInvPI2Over3xSinPIDeltaY * dfSinPIDeltaYOver3,
4461
0
                dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 -
4462
0
                    dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3,
4463
0
                dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 +
4464
0
                    dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3};
4465
4466
0
            for (int j = jMin; j <= jMax; ++j)
4467
0
            {
4468
0
                const double dfY = j - dfDeltaY;
4469
0
                if (dfY == 0.0)
4470
0
                    padfWeightsYShifted[j] = 1.0;
4471
0
                else
4472
0
                    padfWeightsYShifted[j] = padfCst[(j + 3) % 3] / (dfY * dfY);
4473
#if DEBUG_VERBOSE
4474
                // TODO(schwehr): AlmostEqual.
4475
                // CPLAssert(fabs(padfWeightsYShifted[j] -
4476
                //               GWKLanczosSinc(dfY, 3.0)) < 1e-10);
4477
#endif
4478
0
            }
4479
4480
0
            psWrkStruct->iLastSrcY = iSrcY;
4481
0
            psWrkStruct->dfLastDeltaY = dfDeltaY;
4482
0
        }
4483
0
    }
4484
4485
    // If we have no density information, we can simply compute the
4486
    // accumulated weight.
4487
0
    if (padfRowDensity == nullptr)
4488
0
    {
4489
0
        double dfRowAccWeight = 0.0;
4490
0
        for (int i = iMin; i <= iMax; ++i)
4491
0
        {
4492
0
            dfRowAccWeight += padfWeightsXShifted[i];
4493
0
        }
4494
0
        double dfColAccWeight = 0.0;
4495
0
        for (int j = jMin; j <= jMax; ++j)
4496
0
        {
4497
0
            dfColAccWeight += padfWeightsYShifted[j];
4498
0
        }
4499
0
        dfAccumulatorWeight = dfRowAccWeight * dfColAccWeight;
4500
0
    }
4501
4502
    // Loop over pixel rows in the kernel.
4503
4504
0
    if (poWK->eWorkingDataType == GDT_UInt8 && !poWK->panUnifiedSrcValid &&
4505
0
        !poWK->papanBandSrcValid && !poWK->pafUnifiedSrcDensity &&
4506
0
        !padfRowDensity)
4507
0
    {
4508
        // Optimization for Byte case without any masking/alpha
4509
4510
0
        if (dfAccumulatorWeight < 0.000001)
4511
0
        {
4512
0
            *pdfDensity = 0.0;
4513
0
            return false;
4514
0
        }
4515
4516
0
        const GByte *pSrc =
4517
0
            reinterpret_cast<const GByte *>(poWK->papabySrcImage[iBand]);
4518
0
        pSrc += iSrcOffset + static_cast<GPtrDiff_t>(jMin) * nSrcXSize;
4519
4520
0
#if defined(USE_SSE2)
4521
0
        if (iMax - iMin + 1 == 6)
4522
0
        {
4523
            // This is just an optimized version of the general case in
4524
            // the else clause.
4525
4526
0
            pSrc += iMin;
4527
0
            int j = jMin;
4528
0
            const auto fourXWeights =
4529
0
                XMMReg4Double::Load4Val(padfWeightsXShifted + iMin);
4530
4531
            // Process 2 lines at the same time.
4532
0
            for (; j < jMax; j += 2)
4533
0
            {
4534
0
                const XMMReg4Double v_acc =
4535
0
                    XMMReg4Double::Load4Val(pSrc) * fourXWeights;
4536
0
                const XMMReg4Double v_acc2 =
4537
0
                    XMMReg4Double::Load4Val(pSrc + nSrcXSize) * fourXWeights;
4538
0
                const double dfRowAcc = v_acc.GetHorizSum();
4539
0
                const double dfRowAccEnd =
4540
0
                    pSrc[4] * padfWeightsXShifted[iMin + 4] +
4541
0
                    pSrc[5] * padfWeightsXShifted[iMin + 5];
4542
0
                dfAccumulatorReal +=
4543
0
                    (dfRowAcc + dfRowAccEnd) * padfWeightsYShifted[j];
4544
0
                const double dfRowAcc2 = v_acc2.GetHorizSum();
4545
0
                const double dfRowAcc2End =
4546
0
                    pSrc[nSrcXSize + 4] * padfWeightsXShifted[iMin + 4] +
4547
0
                    pSrc[nSrcXSize + 5] * padfWeightsXShifted[iMin + 5];
4548
0
                dfAccumulatorReal +=
4549
0
                    (dfRowAcc2 + dfRowAcc2End) * padfWeightsYShifted[j + 1];
4550
0
                pSrc += 2 * nSrcXSize;
4551
0
            }
4552
0
            if (j == jMax)
4553
0
            {
4554
                // Process last line if there's an odd number of them.
4555
4556
0
                const XMMReg4Double v_acc =
4557
0
                    XMMReg4Double::Load4Val(pSrc) * fourXWeights;
4558
0
                const double dfRowAcc = v_acc.GetHorizSum();
4559
0
                const double dfRowAccEnd =
4560
0
                    pSrc[4] * padfWeightsXShifted[iMin + 4] +
4561
0
                    pSrc[5] * padfWeightsXShifted[iMin + 5];
4562
0
                dfAccumulatorReal +=
4563
0
                    (dfRowAcc + dfRowAccEnd) * padfWeightsYShifted[j];
4564
0
            }
4565
0
        }
4566
0
        else
4567
0
#endif
4568
0
        {
4569
0
            for (int j = jMin; j <= jMax; ++j)
4570
0
            {
4571
0
                int i = iMin;
4572
0
                double dfRowAcc1 = 0.0;
4573
0
                double dfRowAcc2 = 0.0;
4574
                // A bit of loop unrolling
4575
0
                for (; i < iMax; i += 2)
4576
0
                {
4577
0
                    dfRowAcc1 += pSrc[i] * padfWeightsXShifted[i];
4578
0
                    dfRowAcc2 += pSrc[i + 1] * padfWeightsXShifted[i + 1];
4579
0
                }
4580
0
                if (i == iMax)
4581
0
                {
4582
                    // Process last column if there's an odd number of them.
4583
0
                    dfRowAcc1 += pSrc[i] * padfWeightsXShifted[i];
4584
0
                }
4585
4586
0
                dfAccumulatorReal +=
4587
0
                    (dfRowAcc1 + dfRowAcc2) * padfWeightsYShifted[j];
4588
0
                pSrc += nSrcXSize;
4589
0
            }
4590
0
        }
4591
4592
        // Calculate the output taking into account weighting.
4593
0
        if (dfAccumulatorWeight < 0.99999 || dfAccumulatorWeight > 1.00001)
4594
0
        {
4595
0
            const double dfInvAcc = 1.0 / dfAccumulatorWeight;
4596
0
            *pdfReal = dfAccumulatorReal * dfInvAcc;
4597
0
            *pdfDensity = 1.0;
4598
0
        }
4599
0
        else
4600
0
        {
4601
0
            *pdfReal = dfAccumulatorReal;
4602
0
            *pdfDensity = 1.0;
4603
0
        }
4604
4605
0
        return true;
4606
0
    }
4607
4608
0
    GPtrDiff_t iRowOffset =
4609
0
        iSrcOffset + static_cast<GPtrDiff_t>(jMin - 1) * nSrcXSize + iMin;
4610
4611
0
    int nCountValid = 0;
4612
0
    const bool bIsNonComplex = !GDALDataTypeIsComplex(poWK->eWorkingDataType);
4613
4614
0
    for (int j = jMin; j <= jMax; ++j)
4615
0
    {
4616
0
        iRowOffset += nSrcXSize;
4617
4618
        // Get pixel values.
4619
        // We can potentially read extra elements after the "normal" end of the
4620
        // source arrays, but the contract of papabySrcImage[iBand],
4621
        // papanBandSrcValid[iBand], panUnifiedSrcValid and pafUnifiedSrcDensity
4622
        // is to have WARP_EXTRA_ELTS reserved at their end.
4623
0
        if (!GWKGetPixelRow(poWK, iBand, iRowOffset, (iMax - iMin + 2) / 2,
4624
0
                            padfRowDensity, padfRowReal, padfRowImag))
4625
0
            continue;
4626
4627
0
        const double dfWeight1 = padfWeightsYShifted[j];
4628
4629
        // Iterate over pixels in row.
4630
0
        if (padfRowDensity != nullptr)
4631
0
        {
4632
0
            for (int i = iMin; i <= iMax; ++i)
4633
0
            {
4634
                // Skip sampling if pixel has zero density.
4635
0
                if (padfRowDensity[i - iMin] < SRC_DENSITY_THRESHOLD_DOUBLE)
4636
0
                    continue;
4637
4638
0
                nCountValid++;
4639
4640
                //  Use a cached set of weights for this row.
4641
0
                const double dfWeight2 = dfWeight1 * padfWeightsXShifted[i];
4642
4643
                // Accumulate!
4644
0
                dfAccumulatorReal += padfRowReal[i - iMin] * dfWeight2;
4645
0
                dfAccumulatorImag += padfRowImag[i - iMin] * dfWeight2;
4646
0
                dfAccumulatorDensity += padfRowDensity[i - iMin] * dfWeight2;
4647
0
                dfAccumulatorWeight += dfWeight2;
4648
0
            }
4649
0
        }
4650
0
        else if (bIsNonComplex)
4651
0
        {
4652
0
            double dfRowAccReal = 0.0;
4653
0
            for (int i = iMin; i <= iMax; ++i)
4654
0
            {
4655
0
                const double dfWeight2 = padfWeightsXShifted[i];
4656
4657
                // Accumulate!
4658
0
                dfRowAccReal += padfRowReal[i - iMin] * dfWeight2;
4659
0
            }
4660
4661
0
            dfAccumulatorReal += dfRowAccReal * dfWeight1;
4662
0
        }
4663
0
        else
4664
0
        {
4665
0
            double dfRowAccReal = 0.0;
4666
0
            double dfRowAccImag = 0.0;
4667
0
            for (int i = iMin; i <= iMax; ++i)
4668
0
            {
4669
0
                const double dfWeight2 = padfWeightsXShifted[i];
4670
4671
                // Accumulate!
4672
0
                dfRowAccReal += padfRowReal[i - iMin] * dfWeight2;
4673
0
                dfRowAccImag += padfRowImag[i - iMin] * dfWeight2;
4674
0
            }
4675
4676
0
            dfAccumulatorReal += dfRowAccReal * dfWeight1;
4677
0
            dfAccumulatorImag += dfRowAccImag * dfWeight1;
4678
0
        }
4679
0
    }
4680
4681
0
    if (dfAccumulatorWeight < 0.000001 ||
4682
0
        (padfRowDensity != nullptr &&
4683
0
         (dfAccumulatorDensity < 0.000001 ||
4684
0
          nCountValid < (jMax - jMin + 1) * (iMax - iMin + 1) / 2)))
4685
0
    {
4686
0
        *pdfDensity = 0.0;
4687
0
        return false;
4688
0
    }
4689
4690
    // Calculate the output taking into account weighting.
4691
0
    if (dfAccumulatorWeight < 0.99999 || dfAccumulatorWeight > 1.00001)
4692
0
    {
4693
0
        const double dfInvAcc = 1.0 / dfAccumulatorWeight;
4694
0
        *pdfReal = dfAccumulatorReal * dfInvAcc;
4695
0
        *pdfImag = dfAccumulatorImag * dfInvAcc;
4696
0
        if (padfRowDensity != nullptr)
4697
0
            *pdfDensity = dfAccumulatorDensity * dfInvAcc;
4698
0
        else
4699
0
            *pdfDensity = 1.0;
4700
0
    }
4701
0
    else
4702
0
    {
4703
0
        *pdfReal = dfAccumulatorReal;
4704
0
        *pdfImag = dfAccumulatorImag;
4705
0
        if (padfRowDensity != nullptr)
4706
0
            *pdfDensity = dfAccumulatorDensity;
4707
0
        else
4708
0
            *pdfDensity = 1.0;
4709
0
    }
4710
4711
0
    return true;
4712
0
}
4713
4714
/************************************************************************/
4715
/*                         GWKComputeWeights()                          */
4716
/************************************************************************/
4717
4718
static void GWKComputeWeights(GDALResampleAlg eResample, int iMin, int iMax,
4719
                              double dfDeltaX, double dfXScale, int jMin,
4720
                              int jMax, double dfDeltaY, double dfYScale,
4721
                              double *padfWeightsHorizontal,
4722
                              double *padfWeightsVertical, double &dfInvWeights)
4723
0
{
4724
4725
0
    const FilterFuncType pfnGetWeight = apfGWKFilter[eResample];
4726
0
    CPLAssert(pfnGetWeight);
4727
0
    const FilterFunc4ValuesType pfnGetWeight4Values =
4728
0
        apfGWKFilter4Values[eResample];
4729
0
    CPLAssert(pfnGetWeight4Values);
4730
4731
0
    int i = iMin;  // Used after for.
4732
0
    int iC = 0;    // Used after for.
4733
    // Not zero, but as close as possible to it, to avoid potential division by
4734
    // zero at end of function
4735
0
    double dfAccumulatorWeightHorizontal = cpl::NumericLimits<double>::min();
4736
0
    for (; i + 2 < iMax; i += 4, iC += 4)
4737
0
    {
4738
0
        padfWeightsHorizontal[iC] = (i - dfDeltaX) * dfXScale;
4739
0
        padfWeightsHorizontal[iC + 1] = padfWeightsHorizontal[iC] + dfXScale;
4740
0
        padfWeightsHorizontal[iC + 2] =
4741
0
            padfWeightsHorizontal[iC + 1] + dfXScale;
4742
0
        padfWeightsHorizontal[iC + 3] =
4743
0
            padfWeightsHorizontal[iC + 2] + dfXScale;
4744
0
        dfAccumulatorWeightHorizontal +=
4745
0
            pfnGetWeight4Values(padfWeightsHorizontal + iC);
4746
0
    }
4747
0
    for (; i <= iMax; ++i, ++iC)
4748
0
    {
4749
0
        const double dfWeight = pfnGetWeight((i - dfDeltaX) * dfXScale);
4750
0
        padfWeightsHorizontal[iC] = dfWeight;
4751
0
        dfAccumulatorWeightHorizontal += dfWeight;
4752
0
    }
4753
4754
0
    int j = jMin;  // Used after for.
4755
0
    int jC = 0;    // Used after for.
4756
    // Not zero, but as close as possible to it, to avoid potential division by
4757
    // zero at end of function
4758
0
    double dfAccumulatorWeightVertical = cpl::NumericLimits<double>::min();
4759
0
    for (; j + 2 < jMax; j += 4, jC += 4)
4760
0
    {
4761
0
        padfWeightsVertical[jC] = (j - dfDeltaY) * dfYScale;
4762
0
        padfWeightsVertical[jC + 1] = padfWeightsVertical[jC] + dfYScale;
4763
0
        padfWeightsVertical[jC + 2] = padfWeightsVertical[jC + 1] + dfYScale;
4764
0
        padfWeightsVertical[jC + 3] = padfWeightsVertical[jC + 2] + dfYScale;
4765
0
        dfAccumulatorWeightVertical +=
4766
0
            pfnGetWeight4Values(padfWeightsVertical + jC);
4767
0
    }
4768
0
    for (; j <= jMax; ++j, ++jC)
4769
0
    {
4770
0
        const double dfWeight = pfnGetWeight((j - dfDeltaY) * dfYScale);
4771
0
        padfWeightsVertical[jC] = dfWeight;
4772
0
        dfAccumulatorWeightVertical += dfWeight;
4773
0
    }
4774
4775
0
    dfInvWeights =
4776
0
        1. / (dfAccumulatorWeightHorizontal * dfAccumulatorWeightVertical);
4777
0
}
4778
4779
/************************************************************************/
4780
/*                        GWKResampleNoMasksT()                         */
4781
/************************************************************************/
4782
4783
template <class T>
4784
static bool
4785
GWKResampleNoMasksT(const GDALWarpKernel *poWK, int iBand, double dfSrcX,
4786
                    double dfSrcY, T *pValue, double *padfWeightsHorizontal,
4787
                    double *padfWeightsVertical, double &dfInvWeights)
4788
4789
{
4790
    // Commonly used; save locally.
4791
    const int nSrcXSize = poWK->nSrcXSize;
4792
    const int nSrcYSize = poWK->nSrcYSize;
4793
4794
    const int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
4795
    const int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
4796
    const GPtrDiff_t iSrcOffset =
4797
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
4798
4799
    const int nXRadius = poWK->nXRadius;
4800
    const int nYRadius = poWK->nYRadius;
4801
4802
    // Politely refuse to process invalid coordinates or obscenely small image.
4803
    if (iSrcX >= nSrcXSize || iSrcY >= nSrcYSize || nXRadius > nSrcXSize ||
4804
        nYRadius > nSrcYSize)
4805
        return GWKBilinearResampleNoMasks4SampleT(poWK, iBand, dfSrcX, dfSrcY,
4806
                                                  pValue);
4807
4808
    T *pSrcBand = reinterpret_cast<T *>(poWK->papabySrcImage[iBand]);
4809
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
4810
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
4811
4812
    const double dfXScale = std::min(poWK->dfXScale, 1.0);
4813
    const double dfYScale = std::min(poWK->dfYScale, 1.0);
4814
4815
    int iMin = 1 - nXRadius;
4816
    if (iSrcX + iMin < 0)
4817
        iMin = -iSrcX;
4818
    int iMax = nXRadius;
4819
    if (iSrcX + iMax >= nSrcXSize - 1)
4820
        iMax = nSrcXSize - 1 - iSrcX;
4821
4822
    int jMin = 1 - nYRadius;
4823
    if (iSrcY + jMin < 0)
4824
        jMin = -iSrcY;
4825
    int jMax = nYRadius;
4826
    if (iSrcY + jMax >= nSrcYSize - 1)
4827
        jMax = nSrcYSize - 1 - iSrcY;
4828
4829
    if (iBand == 0)
4830
    {
4831
        GWKComputeWeights(poWK->eResample, iMin, iMax, dfDeltaX, dfXScale, jMin,
4832
                          jMax, dfDeltaY, dfYScale, padfWeightsHorizontal,
4833
                          padfWeightsVertical, dfInvWeights);
4834
    }
4835
4836
    // Loop over all rows in the kernel.
4837
    double dfAccumulator = 0.0;
4838
    for (int jC = 0, j = jMin; j <= jMax; ++j, ++jC)
4839
    {
4840
        const GPtrDiff_t iSampJ =
4841
            iSrcOffset + static_cast<GPtrDiff_t>(j) * nSrcXSize;
4842
4843
        // Loop over all pixels in the row.
4844
        double dfAccumulatorLocal = 0.0;
4845
        double dfAccumulatorLocal2 = 0.0;
4846
        int iC = 0;
4847
        int i = iMin;
4848
        // Process by chunk of 4 cols.
4849
        for (; i + 2 < iMax; i += 4, iC += 4)
4850
        {
4851
            // Retrieve the pixel & accumulate.
4852
            dfAccumulatorLocal +=
4853
                double(pSrcBand[i + iSampJ]) * padfWeightsHorizontal[iC];
4854
            dfAccumulatorLocal += double(pSrcBand[i + 1 + iSampJ]) *
4855
                                  padfWeightsHorizontal[iC + 1];
4856
            dfAccumulatorLocal2 += double(pSrcBand[i + 2 + iSampJ]) *
4857
                                   padfWeightsHorizontal[iC + 2];
4858
            dfAccumulatorLocal2 += double(pSrcBand[i + 3 + iSampJ]) *
4859
                                   padfWeightsHorizontal[iC + 3];
4860
        }
4861
        dfAccumulatorLocal += dfAccumulatorLocal2;
4862
        if (i < iMax)
4863
        {
4864
            dfAccumulatorLocal +=
4865
                double(pSrcBand[i + iSampJ]) * padfWeightsHorizontal[iC];
4866
            dfAccumulatorLocal += double(pSrcBand[i + 1 + iSampJ]) *
4867
                                  padfWeightsHorizontal[iC + 1];
4868
            i += 2;
4869
            iC += 2;
4870
        }
4871
        if (i == iMax)
4872
        {
4873
            dfAccumulatorLocal +=
4874
                double(pSrcBand[i + iSampJ]) * padfWeightsHorizontal[iC];
4875
        }
4876
4877
        dfAccumulator += padfWeightsVertical[jC] * dfAccumulatorLocal;
4878
    }
4879
4880
    *pValue = GWKClampValueT<T>(dfAccumulator * dfInvWeights);
4881
4882
    return true;
4883
}
4884
4885
/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
4886
/* Could possibly be used too on 32bit, but we would need to check at runtime */
4887
#if defined(USE_SSE2)
4888
4889
/************************************************************************/
4890
/*                     GWKResampleNoMasks_SSE2_T()                      */
4891
/************************************************************************/
4892
4893
template <class T>
4894
static bool GWKResampleNoMasks_SSE2_T(const GDALWarpKernel *poWK, int iBand,
4895
                                      double dfSrcX, double dfSrcY, T *pValue,
4896
                                      double *padfWeightsHorizontal,
4897
                                      double *padfWeightsVertical,
4898
                                      double &dfInvWeights)
4899
0
{
4900
    // Commonly used; save locally.
4901
0
    const int nSrcXSize = poWK->nSrcXSize;
4902
0
    const int nSrcYSize = poWK->nSrcYSize;
4903
4904
0
    const int iSrcX = static_cast<int>(floor(dfSrcX - 0.5));
4905
0
    const int iSrcY = static_cast<int>(floor(dfSrcY - 0.5));
4906
0
    const GPtrDiff_t iSrcOffset =
4907
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
4908
0
    const int nXRadius = poWK->nXRadius;
4909
0
    const int nYRadius = poWK->nYRadius;
4910
4911
    // Politely refuse to process invalid coordinates or obscenely small image.
4912
0
    if (iSrcX >= nSrcXSize || iSrcY >= nSrcYSize || nXRadius > nSrcXSize ||
4913
0
        nYRadius > nSrcYSize)
4914
0
        return GWKBilinearResampleNoMasks4SampleT(poWK, iBand, dfSrcX, dfSrcY,
4915
0
                                                  pValue);
4916
4917
0
    const T *pSrcBand =
4918
0
        reinterpret_cast<const T *>(poWK->papabySrcImage[iBand]);
4919
4920
0
    const double dfDeltaX = dfSrcX - 0.5 - iSrcX;
4921
0
    const double dfDeltaY = dfSrcY - 0.5 - iSrcY;
4922
0
    const double dfXScale = std::min(poWK->dfXScale, 1.0);
4923
0
    const double dfYScale = std::min(poWK->dfYScale, 1.0);
4924
4925
0
    int iMin = 1 - nXRadius;
4926
0
    if (iSrcX + iMin < 0)
4927
0
        iMin = -iSrcX;
4928
0
    int iMax = nXRadius;
4929
0
    if (iSrcX + iMax >= nSrcXSize - 1)
4930
0
        iMax = nSrcXSize - 1 - iSrcX;
4931
4932
0
    int jMin = 1 - nYRadius;
4933
0
    if (iSrcY + jMin < 0)
4934
0
        jMin = -iSrcY;
4935
0
    int jMax = nYRadius;
4936
0
    if (iSrcY + jMax >= nSrcYSize - 1)
4937
0
        jMax = nSrcYSize - 1 - iSrcY;
4938
4939
0
    if (iBand == 0)
4940
0
    {
4941
0
        GWKComputeWeights(poWK->eResample, iMin, iMax, dfDeltaX, dfXScale, jMin,
4942
0
                          jMax, dfDeltaY, dfYScale, padfWeightsHorizontal,
4943
0
                          padfWeightsVertical, dfInvWeights);
4944
0
    }
4945
4946
0
    GPtrDiff_t iSampJ = iSrcOffset + static_cast<GPtrDiff_t>(jMin) * nSrcXSize;
4947
    // Process by chunk of 4 rows.
4948
0
    int jC = 0;
4949
0
    int j = jMin;
4950
0
    double dfAccumulator = 0.0;
4951
0
    for (; j + 2 < jMax; j += 4, iSampJ += 4 * nSrcXSize, jC += 4)
4952
0
    {
4953
        // Loop over all pixels in the row.
4954
0
        int iC = 0;
4955
0
        int i = iMin;
4956
        // Process by chunk of 4 cols.
4957
0
        XMMReg4Double v_acc_1 = XMMReg4Double::Zero();
4958
0
        XMMReg4Double v_acc_2 = XMMReg4Double::Zero();
4959
0
        XMMReg4Double v_acc_3 = XMMReg4Double::Zero();
4960
0
        XMMReg4Double v_acc_4 = XMMReg4Double::Zero();
4961
0
        for (; i + 2 < iMax; i += 4, iC += 4)
4962
0
        {
4963
            // Retrieve the pixel & accumulate.
4964
0
            XMMReg4Double v_pixels_1 =
4965
0
                XMMReg4Double::Load4Val(pSrcBand + i + iSampJ);
4966
0
            XMMReg4Double v_pixels_2 =
4967
0
                XMMReg4Double::Load4Val(pSrcBand + i + iSampJ + nSrcXSize);
4968
0
            XMMReg4Double v_pixels_3 =
4969
0
                XMMReg4Double::Load4Val(pSrcBand + i + iSampJ + 2 * nSrcXSize);
4970
0
            XMMReg4Double v_pixels_4 =
4971
0
                XMMReg4Double::Load4Val(pSrcBand + i + iSampJ + 3 * nSrcXSize);
4972
4973
0
            XMMReg4Double v_padfWeight =
4974
0
                XMMReg4Double::Load4Val(padfWeightsHorizontal + iC);
4975
4976
0
            v_acc_1 += v_pixels_1 * v_padfWeight;
4977
0
            v_acc_2 += v_pixels_2 * v_padfWeight;
4978
0
            v_acc_3 += v_pixels_3 * v_padfWeight;
4979
0
            v_acc_4 += v_pixels_4 * v_padfWeight;
4980
0
        }
4981
4982
0
        if (i < iMax)
4983
0
        {
4984
0
            XMMReg2Double v_pixels_1 =
4985
0
                XMMReg2Double::Load2Val(pSrcBand + i + iSampJ);
4986
0
            XMMReg2Double v_pixels_2 =
4987
0
                XMMReg2Double::Load2Val(pSrcBand + i + iSampJ + nSrcXSize);
4988
0
            XMMReg2Double v_pixels_3 =
4989
0
                XMMReg2Double::Load2Val(pSrcBand + i + iSampJ + 2 * nSrcXSize);
4990
0
            XMMReg2Double v_pixels_4 =
4991
0
                XMMReg2Double::Load2Val(pSrcBand + i + iSampJ + 3 * nSrcXSize);
4992
4993
0
            XMMReg2Double v_padfWeight =
4994
0
                XMMReg2Double::Load2Val(padfWeightsHorizontal + iC);
4995
4996
0
            v_acc_1.AddToLow(v_pixels_1 * v_padfWeight);
4997
0
            v_acc_2.AddToLow(v_pixels_2 * v_padfWeight);
4998
0
            v_acc_3.AddToLow(v_pixels_3 * v_padfWeight);
4999
0
            v_acc_4.AddToLow(v_pixels_4 * v_padfWeight);
5000
5001
0
            i += 2;
5002
0
            iC += 2;
5003
0
        }
5004
5005
0
        double dfAccumulatorLocal_1 = v_acc_1.GetHorizSum();
5006
0
        double dfAccumulatorLocal_2 = v_acc_2.GetHorizSum();
5007
0
        double dfAccumulatorLocal_3 = v_acc_3.GetHorizSum();
5008
0
        double dfAccumulatorLocal_4 = v_acc_4.GetHorizSum();
5009
5010
0
        if (i == iMax)
5011
0
        {
5012
0
            dfAccumulatorLocal_1 += static_cast<double>(pSrcBand[i + iSampJ]) *
5013
0
                                    padfWeightsHorizontal[iC];
5014
0
            dfAccumulatorLocal_2 +=
5015
0
                static_cast<double>(pSrcBand[i + iSampJ + nSrcXSize]) *
5016
0
                padfWeightsHorizontal[iC];
5017
0
            dfAccumulatorLocal_3 +=
5018
0
                static_cast<double>(pSrcBand[i + iSampJ + 2 * nSrcXSize]) *
5019
0
                padfWeightsHorizontal[iC];
5020
0
            dfAccumulatorLocal_4 +=
5021
0
                static_cast<double>(pSrcBand[i + iSampJ + 3 * nSrcXSize]) *
5022
0
                padfWeightsHorizontal[iC];
5023
0
        }
5024
5025
0
        dfAccumulator += padfWeightsVertical[jC] * dfAccumulatorLocal_1;
5026
0
        dfAccumulator += padfWeightsVertical[jC + 1] * dfAccumulatorLocal_2;
5027
0
        dfAccumulator += padfWeightsVertical[jC + 2] * dfAccumulatorLocal_3;
5028
0
        dfAccumulator += padfWeightsVertical[jC + 3] * dfAccumulatorLocal_4;
5029
0
    }
5030
0
    for (; j <= jMax; ++j, iSampJ += nSrcXSize, ++jC)
5031
0
    {
5032
        // Loop over all pixels in the row.
5033
0
        int iC = 0;
5034
0
        int i = iMin;
5035
        // Process by chunk of 4 cols.
5036
0
        XMMReg4Double v_acc = XMMReg4Double::Zero();
5037
0
        for (; i + 2 < iMax; i += 4, iC += 4)
5038
0
        {
5039
            // Retrieve the pixel & accumulate.
5040
0
            XMMReg4Double v_pixels =
5041
0
                XMMReg4Double::Load4Val(pSrcBand + i + iSampJ);
5042
0
            XMMReg4Double v_padfWeight =
5043
0
                XMMReg4Double::Load4Val(padfWeightsHorizontal + iC);
5044
5045
0
            v_acc += v_pixels * v_padfWeight;
5046
0
        }
5047
5048
0
        double dfAccumulatorLocal = v_acc.GetHorizSum();
5049
5050
0
        if (i < iMax)
5051
0
        {
5052
0
            dfAccumulatorLocal +=
5053
0
                double(pSrcBand[i + iSampJ]) * padfWeightsHorizontal[iC];
5054
0
            dfAccumulatorLocal += double(pSrcBand[i + 1 + iSampJ]) *
5055
0
                                  padfWeightsHorizontal[iC + 1];
5056
0
            i += 2;
5057
0
            iC += 2;
5058
0
        }
5059
0
        if (i == iMax)
5060
0
        {
5061
0
            dfAccumulatorLocal += static_cast<double>(pSrcBand[i + iSampJ]) *
5062
0
                                  padfWeightsHorizontal[iC];
5063
0
        }
5064
5065
0
        dfAccumulator += padfWeightsVertical[jC] * dfAccumulatorLocal;
5066
0
    }
5067
5068
0
    *pValue = GWKClampValueT<T>(dfAccumulator * dfInvWeights);
5069
5070
0
    return true;
5071
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKResampleNoMasks_SSE2_T<unsigned char>(GDALWarpKernel const*, int, double, double, unsigned char*, double*, double*, double&)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKResampleNoMasks_SSE2_T<float>(GDALWarpKernel const*, int, double, double, float*, double*, double*, double&)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKResampleNoMasks_SSE2_T<short>(GDALWarpKernel const*, int, double, double, short*, double*, double*, double&)
Unexecuted instantiation: gdalwarpkernel.cpp:bool GWKResampleNoMasks_SSE2_T<unsigned short>(GDALWarpKernel const*, int, double, double, unsigned short*, double*, double*, double&)
5072
5073
/************************************************************************/
5074
/*                     GWKResampleNoMasksT<GByte>()                     */
5075
/************************************************************************/
5076
5077
template <>
5078
bool GWKResampleNoMasksT<GByte>(const GDALWarpKernel *poWK, int iBand,
5079
                                double dfSrcX, double dfSrcY, GByte *pValue,
5080
                                double *padfWeightsHorizontal,
5081
                                double *padfWeightsVertical,
5082
                                double &dfInvWeights)
5083
0
{
5084
0
    return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue,
5085
0
                                     padfWeightsHorizontal, padfWeightsVertical,
5086
0
                                     dfInvWeights);
5087
0
}
5088
5089
/************************************************************************/
5090
/*                    GWKResampleNoMasksT<GInt16>()                     */
5091
/************************************************************************/
5092
5093
template <>
5094
bool GWKResampleNoMasksT<GInt16>(const GDALWarpKernel *poWK, int iBand,
5095
                                 double dfSrcX, double dfSrcY, GInt16 *pValue,
5096
                                 double *padfWeightsHorizontal,
5097
                                 double *padfWeightsVertical,
5098
                                 double &dfInvWeights)
5099
0
{
5100
0
    return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue,
5101
0
                                     padfWeightsHorizontal, padfWeightsVertical,
5102
0
                                     dfInvWeights);
5103
0
}
5104
5105
/************************************************************************/
5106
/*                    GWKResampleNoMasksT<GUInt16>()                    */
5107
/************************************************************************/
5108
5109
template <>
5110
bool GWKResampleNoMasksT<GUInt16>(const GDALWarpKernel *poWK, int iBand,
5111
                                  double dfSrcX, double dfSrcY, GUInt16 *pValue,
5112
                                  double *padfWeightsHorizontal,
5113
                                  double *padfWeightsVertical,
5114
                                  double &dfInvWeights)
5115
0
{
5116
0
    return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue,
5117
0
                                     padfWeightsHorizontal, padfWeightsVertical,
5118
0
                                     dfInvWeights);
5119
0
}
5120
5121
/************************************************************************/
5122
/*                     GWKResampleNoMasksT<float>()                     */
5123
/************************************************************************/
5124
5125
template <>
5126
bool GWKResampleNoMasksT<float>(const GDALWarpKernel *poWK, int iBand,
5127
                                double dfSrcX, double dfSrcY, float *pValue,
5128
                                double *padfWeightsHorizontal,
5129
                                double *padfWeightsVertical,
5130
                                double &dfInvWeights)
5131
0
{
5132
0
    return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue,
5133
0
                                     padfWeightsHorizontal, padfWeightsVertical,
5134
0
                                     dfInvWeights);
5135
0
}
5136
5137
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
5138
5139
/************************************************************************/
5140
/*                    GWKResampleNoMasksT<double>()                     */
5141
/************************************************************************/
5142
5143
template <>
5144
bool GWKResampleNoMasksT<double>(const GDALWarpKernel *poWK, int iBand,
5145
                                 double dfSrcX, double dfSrcY, double *pValue,
5146
                                 double *padfWeightsHorizontal,
5147
                                 double *padfWeightsVertical,
5148
                                 double &dfInvWeights)
5149
{
5150
    return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue,
5151
                                     padfWeightsHorizontal, padfWeightsVertical,
5152
                                     dfInvWeights);
5153
}
5154
5155
#endif /* INSTANTIATE_FLOAT64_SSE2_IMPL */
5156
5157
#endif /* defined(USE_SSE2) */
5158
5159
/************************************************************************/
5160
/*                     GWKRoundSourceCoordinates()                      */
5161
/************************************************************************/
5162
5163
static void GWKRoundSourceCoordinates(
5164
    int nDstXSize, double *padfX, double *padfY, double *padfZ, int *pabSuccess,
5165
    double dfSrcCoordPrecision, double dfErrorThreshold,
5166
    GDALTransformerFunc pfnTransformer, void *pTransformerArg, double dfDstXOff,
5167
    double dfDstY)
5168
0
{
5169
0
    double dfPct = 0.8;
5170
0
    if (dfErrorThreshold > 0 && dfSrcCoordPrecision / dfErrorThreshold >= 10.0)
5171
0
    {
5172
0
        dfPct = 1.0 - 2 * 1.0 / (dfSrcCoordPrecision / dfErrorThreshold);
5173
0
    }
5174
0
    const double dfExactTransformThreshold = 0.5 * dfPct * dfSrcCoordPrecision;
5175
5176
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5177
0
    {
5178
0
        const double dfXBefore = padfX[iDstX];
5179
0
        const double dfYBefore = padfY[iDstX];
5180
0
        padfX[iDstX] = floor(padfX[iDstX] / dfSrcCoordPrecision + 0.5) *
5181
0
                       dfSrcCoordPrecision;
5182
0
        padfY[iDstX] = floor(padfY[iDstX] / dfSrcCoordPrecision + 0.5) *
5183
0
                       dfSrcCoordPrecision;
5184
5185
        // If we are in an uncertainty zone, go to non-approximated
5186
        // transformation.
5187
        // Due to the 80% of half-precision threshold, dfSrcCoordPrecision must
5188
        // be at least 10 times greater than the approximation error.
5189
0
        if (fabs(dfXBefore - padfX[iDstX]) > dfExactTransformThreshold ||
5190
0
            fabs(dfYBefore - padfY[iDstX]) > dfExactTransformThreshold)
5191
0
        {
5192
0
            padfX[iDstX] = iDstX + dfDstXOff;
5193
0
            padfY[iDstX] = dfDstY;
5194
0
            padfZ[iDstX] = 0.0;
5195
0
            pfnTransformer(pTransformerArg, TRUE, 1, padfX + iDstX,
5196
0
                           padfY + iDstX, padfZ + iDstX, pabSuccess + iDstX);
5197
0
            padfX[iDstX] = floor(padfX[iDstX] / dfSrcCoordPrecision + 0.5) *
5198
0
                           dfSrcCoordPrecision;
5199
0
            padfY[iDstX] = floor(padfY[iDstX] / dfSrcCoordPrecision + 0.5) *
5200
0
                           dfSrcCoordPrecision;
5201
0
        }
5202
0
    }
5203
0
}
5204
5205
/************************************************************************/
5206
/*                    GWKCheckAndComputeSrcOffsets()                    */
5207
/************************************************************************/
5208
static CPL_INLINE bool
5209
GWKCheckAndComputeSrcOffsets(GWKJobStruct *psJob, int *_pabSuccess, int _iDstX,
5210
                             int _iDstY, double *_padfX, double *_padfY,
5211
                             int _nSrcXSize, int _nSrcYSize,
5212
                             GPtrDiff_t &iSrcOffset)
5213
0
{
5214
0
    const GDALWarpKernel *_poWK = psJob->poWK;
5215
0
    for (int iTry = 0; iTry < 2; ++iTry)
5216
0
    {
5217
0
        if (iTry == 1)
5218
0
        {
5219
            // If the source coordinate is slightly outside of the source raster
5220
            // retry to transform it alone, so that the exact coordinate
5221
            // transformer is used.
5222
5223
0
            _padfX[_iDstX] = _iDstX + 0.5 + _poWK->nDstXOff;
5224
0
            _padfY[_iDstX] = _iDstY + 0.5 + _poWK->nDstYOff;
5225
0
            double dfZ = 0;
5226
0
            _poWK->pfnTransformer(psJob->pTransformerArg, TRUE, 1,
5227
0
                                  _padfX + _iDstX, _padfY + _iDstX, &dfZ,
5228
0
                                  _pabSuccess + _iDstX);
5229
0
        }
5230
0
        if (!_pabSuccess[_iDstX])
5231
0
            return false;
5232
5233
        // If this happens this is likely the symptom of a bug somewhere.
5234
0
        if (std::isnan(_padfX[_iDstX]) || std::isnan(_padfY[_iDstX]))
5235
0
        {
5236
0
            static bool bNanCoordFound = false;
5237
0
            if (!bNanCoordFound)
5238
0
            {
5239
0
                CPLDebug("WARP",
5240
0
                         "GWKCheckAndComputeSrcOffsets(): "
5241
0
                         "NaN coordinate found on point %d.",
5242
0
                         _iDstX);
5243
0
                bNanCoordFound = true;
5244
0
            }
5245
0
            return false;
5246
0
        }
5247
5248
        /* --------------------------------------------------------------------
5249
         */
5250
        /*      Figure out what pixel we want in our source raster, and skip */
5251
        /*      further processing if it is well off the source image. */
5252
        /* --------------------------------------------------------------------
5253
         */
5254
        /* We test against the value before casting to avoid the */
5255
        /* problem of asymmetric truncation effects around zero.  That is */
5256
        /* -0.5 will be 0 when cast to an int. */
5257
0
        if (_padfX[_iDstX] < _poWK->nSrcXOff)
5258
0
        {
5259
            // If the source coordinate is slightly outside of the source raster
5260
            // retry to transform it alone, so that the exact coordinate
5261
            // transformer is used.
5262
0
            if (iTry == 0 && _padfX[_iDstX] > _poWK->nSrcXOff - 1)
5263
0
                continue;
5264
0
            return false;
5265
0
        }
5266
5267
0
        if (_padfY[_iDstX] < _poWK->nSrcYOff)
5268
0
        {
5269
            // If the source coordinate is slightly outside of the source raster
5270
            // retry to transform it alone, so that the exact coordinate
5271
            // transformer is used.
5272
0
            if (iTry == 0 && _padfY[_iDstX] > _poWK->nSrcYOff - 1)
5273
0
                continue;
5274
0
            return false;
5275
0
        }
5276
5277
        // Check for potential overflow when casting from float to int, (if
5278
        // operating outside natural projection area, padfX/Y can be a very huge
5279
        // positive number before doing the actual conversion), as such cast is
5280
        // undefined behavior that can trigger exception with some compilers
5281
        // (see #6753)
5282
0
        if (_padfX[_iDstX] + 1e-10 > _nSrcXSize + _poWK->nSrcXOff)
5283
0
        {
5284
            // If the source coordinate is slightly outside of the source raster
5285
            // retry to transform it alone, so that the exact coordinate
5286
            // transformer is used.
5287
0
            if (iTry == 0 && _padfX[_iDstX] < _nSrcXSize + _poWK->nSrcXOff + 1)
5288
0
                continue;
5289
0
            return false;
5290
0
        }
5291
0
        if (_padfY[_iDstX] + 1e-10 > _nSrcYSize + _poWK->nSrcYOff)
5292
0
        {
5293
            // If the source coordinate is slightly outside of the source raster
5294
            // retry to transform it alone, so that the exact coordinate
5295
            // transformer is used.
5296
0
            if (iTry == 0 && _padfY[_iDstX] < _nSrcYSize + _poWK->nSrcYOff + 1)
5297
0
                continue;
5298
0
            return false;
5299
0
        }
5300
5301
0
        break;
5302
0
    }
5303
5304
0
    int iSrcX = static_cast<int>(_padfX[_iDstX] + 1.0e-10) - _poWK->nSrcXOff;
5305
0
    int iSrcY = static_cast<int>(_padfY[_iDstX] + 1.0e-10) - _poWK->nSrcYOff;
5306
0
    if (iSrcX == _nSrcXSize)
5307
0
        iSrcX--;
5308
0
    if (iSrcY == _nSrcYSize)
5309
0
        iSrcY--;
5310
5311
    // Those checks should normally be OK given the previous ones.
5312
0
    CPLAssert(iSrcX >= 0);
5313
0
    CPLAssert(iSrcY >= 0);
5314
0
    CPLAssert(iSrcX < _nSrcXSize);
5315
0
    CPLAssert(iSrcY < _nSrcYSize);
5316
5317
0
    iSrcOffset = iSrcX + static_cast<GPtrDiff_t>(iSrcY) * _nSrcXSize;
5318
5319
0
    return true;
5320
0
}
5321
5322
/************************************************************************/
5323
/*                 GWKOneSourceCornerFailsToReproject()                 */
5324
/************************************************************************/
5325
5326
static bool GWKOneSourceCornerFailsToReproject(GWKJobStruct *psJob)
5327
0
{
5328
0
    GDALWarpKernel *poWK = psJob->poWK;
5329
0
    for (int iY = 0; iY <= 1; ++iY)
5330
0
    {
5331
0
        for (int iX = 0; iX <= 1; ++iX)
5332
0
        {
5333
0
            double dfXTmp = poWK->nSrcXOff + iX * poWK->nSrcXSize;
5334
0
            double dfYTmp = poWK->nSrcYOff + iY * poWK->nSrcYSize;
5335
0
            double dfZTmp = 0;
5336
0
            int nSuccess = FALSE;
5337
0
            poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp,
5338
0
                                 &dfYTmp, &dfZTmp, &nSuccess);
5339
0
            if (!nSuccess)
5340
0
                return true;
5341
0
        }
5342
0
    }
5343
0
    return false;
5344
0
}
5345
5346
/************************************************************************/
5347
/*                      GWKAdjustSrcOffsetOnEdge()                      */
5348
/************************************************************************/
5349
5350
static bool GWKAdjustSrcOffsetOnEdge(GWKJobStruct *psJob,
5351
                                     GPtrDiff_t &iSrcOffset)
5352
0
{
5353
0
    GDALWarpKernel *poWK = psJob->poWK;
5354
0
    const int nSrcXSize = poWK->nSrcXSize;
5355
0
    const int nSrcYSize = poWK->nSrcYSize;
5356
5357
    // Check if the computed source position slightly altered
5358
    // fails to reproject. If so, then we are at the edge of
5359
    // the validity area, and it is worth checking neighbour
5360
    // source pixels for validity.
5361
0
    int nSuccess = FALSE;
5362
0
    {
5363
0
        double dfXTmp =
5364
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize);
5365
0
        double dfYTmp =
5366
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize);
5367
0
        double dfZTmp = 0;
5368
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5369
0
                             &dfZTmp, &nSuccess);
5370
0
    }
5371
0
    if (nSuccess)
5372
0
    {
5373
0
        double dfXTmp =
5374
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize);
5375
0
        double dfYTmp =
5376
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize) + 1;
5377
0
        double dfZTmp = 0;
5378
0
        nSuccess = FALSE;
5379
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5380
0
                             &dfZTmp, &nSuccess);
5381
0
    }
5382
0
    if (nSuccess)
5383
0
    {
5384
0
        double dfXTmp =
5385
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize) + 1;
5386
0
        double dfYTmp =
5387
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize);
5388
0
        double dfZTmp = 0;
5389
0
        nSuccess = FALSE;
5390
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5391
0
                             &dfZTmp, &nSuccess);
5392
0
    }
5393
5394
0
    if (!nSuccess && (iSrcOffset % nSrcXSize) + 1 < nSrcXSize &&
5395
0
        CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset + 1))
5396
0
    {
5397
0
        iSrcOffset++;
5398
0
        return true;
5399
0
    }
5400
0
    else if (!nSuccess && (iSrcOffset / nSrcXSize) + 1 < nSrcYSize &&
5401
0
             CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset + nSrcXSize))
5402
0
    {
5403
0
        iSrcOffset += nSrcXSize;
5404
0
        return true;
5405
0
    }
5406
0
    else if (!nSuccess && (iSrcOffset % nSrcXSize) > 0 &&
5407
0
             CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset - 1))
5408
0
    {
5409
0
        iSrcOffset--;
5410
0
        return true;
5411
0
    }
5412
0
    else if (!nSuccess && (iSrcOffset / nSrcXSize) > 0 &&
5413
0
             CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset - nSrcXSize))
5414
0
    {
5415
0
        iSrcOffset -= nSrcXSize;
5416
0
        return true;
5417
0
    }
5418
5419
0
    return false;
5420
0
}
5421
5422
/************************************************************************/
5423
/*             GWKAdjustSrcOffsetOnEdgeUnifiedSrcDensity()              */
5424
/************************************************************************/
5425
5426
static bool GWKAdjustSrcOffsetOnEdgeUnifiedSrcDensity(GWKJobStruct *psJob,
5427
                                                      GPtrDiff_t &iSrcOffset)
5428
0
{
5429
0
    GDALWarpKernel *poWK = psJob->poWK;
5430
0
    const int nSrcXSize = poWK->nSrcXSize;
5431
0
    const int nSrcYSize = poWK->nSrcYSize;
5432
5433
    // Check if the computed source position slightly altered
5434
    // fails to reproject. If so, then we are at the edge of
5435
    // the validity area, and it is worth checking neighbour
5436
    // source pixels for validity.
5437
0
    int nSuccess = FALSE;
5438
0
    {
5439
0
        double dfXTmp =
5440
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize);
5441
0
        double dfYTmp =
5442
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize);
5443
0
        double dfZTmp = 0;
5444
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5445
0
                             &dfZTmp, &nSuccess);
5446
0
    }
5447
0
    if (nSuccess)
5448
0
    {
5449
0
        double dfXTmp =
5450
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize);
5451
0
        double dfYTmp =
5452
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize) + 1;
5453
0
        double dfZTmp = 0;
5454
0
        nSuccess = FALSE;
5455
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5456
0
                             &dfZTmp, &nSuccess);
5457
0
    }
5458
0
    if (nSuccess)
5459
0
    {
5460
0
        double dfXTmp =
5461
0
            poWK->nSrcXOff + static_cast<int>(iSrcOffset % nSrcXSize) + 1;
5462
0
        double dfYTmp =
5463
0
            poWK->nSrcYOff + static_cast<int>(iSrcOffset / nSrcXSize);
5464
0
        double dfZTmp = 0;
5465
0
        nSuccess = FALSE;
5466
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1, &dfXTmp, &dfYTmp,
5467
0
                             &dfZTmp, &nSuccess);
5468
0
    }
5469
5470
0
    if (!nSuccess && (iSrcOffset % nSrcXSize) + 1 < nSrcXSize &&
5471
0
        poWK->pafUnifiedSrcDensity[iSrcOffset + 1] >=
5472
0
            SRC_DENSITY_THRESHOLD_FLOAT)
5473
0
    {
5474
0
        iSrcOffset++;
5475
0
        return true;
5476
0
    }
5477
0
    else if (!nSuccess && (iSrcOffset / nSrcXSize) + 1 < nSrcYSize &&
5478
0
             poWK->pafUnifiedSrcDensity[iSrcOffset + nSrcXSize] >=
5479
0
                 SRC_DENSITY_THRESHOLD_FLOAT)
5480
0
    {
5481
0
        iSrcOffset += nSrcXSize;
5482
0
        return true;
5483
0
    }
5484
0
    else if (!nSuccess && (iSrcOffset % nSrcXSize) > 0 &&
5485
0
             poWK->pafUnifiedSrcDensity[iSrcOffset - 1] >=
5486
0
                 SRC_DENSITY_THRESHOLD_FLOAT)
5487
0
    {
5488
0
        iSrcOffset--;
5489
0
        return true;
5490
0
    }
5491
0
    else if (!nSuccess && (iSrcOffset / nSrcXSize) > 0 &&
5492
0
             poWK->pafUnifiedSrcDensity[iSrcOffset - nSrcXSize] >=
5493
0
                 SRC_DENSITY_THRESHOLD_FLOAT)
5494
0
    {
5495
0
        iSrcOffset -= nSrcXSize;
5496
0
        return true;
5497
0
    }
5498
5499
0
    return false;
5500
0
}
5501
5502
/************************************************************************/
5503
/*                           GWKGeneralCase()                           */
5504
/*                                                                      */
5505
/*      This is the most general case.  It attempts to handle all       */
5506
/*      possible features with relatively little concern for            */
5507
/*      efficiency.                                                     */
5508
/************************************************************************/
5509
5510
static void GWKGeneralCaseThread(void *pData)
5511
0
{
5512
0
    GWKJobStruct *psJob = reinterpret_cast<GWKJobStruct *>(pData);
5513
0
    GDALWarpKernel *poWK = psJob->poWK;
5514
0
    const int iYMin = psJob->iYMin;
5515
0
    const int iYMax = psJob->iYMax;
5516
0
    const double dfMultFactorVerticalShiftPipeline =
5517
0
        poWK->bApplyVerticalShift
5518
0
            ? CPLAtof(CSLFetchNameValueDef(
5519
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
5520
0
                  "1.0"))
5521
0
            : 0.0;
5522
0
    const bool bAvoidNoDataSingleBand =
5523
0
        poWK->nBands == 1 ||
5524
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
5525
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
5526
5527
0
    int nDstXSize = poWK->nDstXSize;
5528
0
    int nSrcXSize = poWK->nSrcXSize;
5529
0
    int nSrcYSize = poWK->nSrcYSize;
5530
5531
    /* -------------------------------------------------------------------- */
5532
    /*      Allocate x,y,z coordinate arrays for transformation ... one     */
5533
    /*      scanlines worth of positions.                                   */
5534
    /* -------------------------------------------------------------------- */
5535
    // For x, 2 *, because we cache the precomputed values at the end.
5536
0
    double *padfX =
5537
0
        static_cast<double *>(CPLMalloc(2 * sizeof(double) * nDstXSize));
5538
0
    double *padfY =
5539
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
5540
0
    double *padfZ =
5541
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
5542
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
5543
5544
0
    const bool bUse4SamplesFormula = CanUse4SamplesFormula(poWK);
5545
5546
0
    GWKResampleWrkStruct *psWrkStruct = nullptr;
5547
0
    if (poWK->eResample != GRA_NearestNeighbour)
5548
0
    {
5549
0
        psWrkStruct = GWKResampleCreateWrkStruct(poWK);
5550
0
    }
5551
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
5552
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
5553
0
    const double dfErrorThreshold = CPLAtof(
5554
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
5555
5556
0
    const bool bOneSourceCornerFailsToReproject =
5557
0
        GWKOneSourceCornerFailsToReproject(psJob);
5558
5559
    // Precompute values.
5560
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5561
0
        padfX[nDstXSize + iDstX] = iDstX + 0.5 + poWK->nDstXOff;
5562
5563
    /* ==================================================================== */
5564
    /*      Loop over output lines.                                         */
5565
    /* ==================================================================== */
5566
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
5567
0
    {
5568
        /* --------------------------------------------------------------------
5569
         */
5570
        /*      Setup points to transform to source image space. */
5571
        /* --------------------------------------------------------------------
5572
         */
5573
0
        memcpy(padfX, padfX + nDstXSize, sizeof(double) * nDstXSize);
5574
0
        const double dfY = iDstY + 0.5 + poWK->nDstYOff;
5575
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5576
0
            padfY[iDstX] = dfY;
5577
0
        memset(padfZ, 0, sizeof(double) * nDstXSize);
5578
5579
        /* --------------------------------------------------------------------
5580
         */
5581
        /*      Transform the points from destination pixel/line coordinates */
5582
        /*      to source pixel/line coordinates. */
5583
        /* --------------------------------------------------------------------
5584
         */
5585
0
        poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX,
5586
0
                             padfY, padfZ, pabSuccess);
5587
0
        if (dfSrcCoordPrecision > 0.0)
5588
0
        {
5589
0
            GWKRoundSourceCoordinates(
5590
0
                nDstXSize, padfX, padfY, padfZ, pabSuccess, dfSrcCoordPrecision,
5591
0
                dfErrorThreshold, poWK->pfnTransformer, psJob->pTransformerArg,
5592
0
                0.5 + poWK->nDstXOff, iDstY + 0.5 + poWK->nDstYOff);
5593
0
        }
5594
5595
        /* ====================================================================
5596
         */
5597
        /*      Loop over pixels in output scanline. */
5598
        /* ====================================================================
5599
         */
5600
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5601
0
        {
5602
0
            GPtrDiff_t iSrcOffset = 0;
5603
0
            if (!GWKCheckAndComputeSrcOffsets(psJob, pabSuccess, iDstX, iDstY,
5604
0
                                              padfX, padfY, nSrcXSize,
5605
0
                                              nSrcYSize, iSrcOffset))
5606
0
                continue;
5607
5608
            /* --------------------------------------------------------------------
5609
             */
5610
            /*      Do not try to apply transparent/invalid source pixels to the
5611
             */
5612
            /*      destination.  This currently ignores the multi-pixel input
5613
             */
5614
            /*      of bilinear and cubic resamples. */
5615
            /* --------------------------------------------------------------------
5616
             */
5617
0
            double dfDensity = 1.0;
5618
5619
0
            if (poWK->pafUnifiedSrcDensity != nullptr)
5620
0
            {
5621
0
                dfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
5622
0
                if (dfDensity < SRC_DENSITY_THRESHOLD_DOUBLE)
5623
0
                {
5624
0
                    if (!bOneSourceCornerFailsToReproject)
5625
0
                    {
5626
0
                        continue;
5627
0
                    }
5628
0
                    else if (GWKAdjustSrcOffsetOnEdgeUnifiedSrcDensity(
5629
0
                                 psJob, iSrcOffset))
5630
0
                    {
5631
0
                        dfDensity =
5632
0
                            double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
5633
0
                    }
5634
0
                    else
5635
0
                    {
5636
0
                        continue;
5637
0
                    }
5638
0
                }
5639
0
            }
5640
5641
0
            if (poWK->panUnifiedSrcValid != nullptr &&
5642
0
                !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
5643
0
            {
5644
0
                if (!bOneSourceCornerFailsToReproject)
5645
0
                {
5646
0
                    continue;
5647
0
                }
5648
0
                else if (!GWKAdjustSrcOffsetOnEdge(psJob, iSrcOffset))
5649
0
                {
5650
0
                    continue;
5651
0
                }
5652
0
            }
5653
5654
            /* ====================================================================
5655
             */
5656
            /*      Loop processing each band. */
5657
            /* ====================================================================
5658
             */
5659
0
            bool bHasFoundDensity = false;
5660
5661
0
            const GPtrDiff_t iDstOffset =
5662
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
5663
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
5664
0
            {
5665
0
                double dfBandDensity = 0.0;
5666
0
                double dfValueReal = 0.0;
5667
0
                double dfValueImag = 0.0;
5668
5669
                /* --------------------------------------------------------------------
5670
                 */
5671
                /*      Collect the source value. */
5672
                /* --------------------------------------------------------------------
5673
                 */
5674
0
                if (poWK->eResample == GRA_NearestNeighbour || nSrcXSize == 1 ||
5675
0
                    nSrcYSize == 1)
5676
0
                {
5677
                    // FALSE is returned if dfBandDensity == 0, which is
5678
                    // checked below.
5679
0
                    CPL_IGNORE_RET_VAL(GWKGetPixelValue(
5680
0
                        poWK, iBand, iSrcOffset, &dfBandDensity, &dfValueReal,
5681
0
                        &dfValueImag));
5682
0
                }
5683
0
                else if (poWK->eResample == GRA_Bilinear && bUse4SamplesFormula)
5684
0
                {
5685
0
                    GWKBilinearResample4Sample(
5686
0
                        poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5687
0
                        padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5688
0
                        &dfValueReal, &dfValueImag);
5689
0
                }
5690
0
                else if (poWK->eResample == GRA_Cubic && bUse4SamplesFormula)
5691
0
                {
5692
0
                    GWKCubicResample4Sample(
5693
0
                        poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5694
0
                        padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5695
0
                        &dfValueReal, &dfValueImag);
5696
0
                }
5697
0
                else
5698
0
#ifdef DEBUG
5699
                    // Only useful for clang static analyzer.
5700
0
                    if (psWrkStruct != nullptr)
5701
0
#endif
5702
0
                    {
5703
0
                        psWrkStruct->pfnGWKResample(
5704
0
                            poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5705
0
                            padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5706
0
                            &dfValueReal, &dfValueImag, psWrkStruct);
5707
0
                    }
5708
5709
                // If we didn't find any valid inputs skip to next band.
5710
0
                if (dfBandDensity < BAND_DENSITY_THRESHOLD)
5711
0
                    continue;
5712
5713
0
                if (poWK->bApplyVerticalShift)
5714
0
                {
5715
0
                    if (!std::isfinite(padfZ[iDstX]))
5716
0
                        continue;
5717
                    // Subtract padfZ[] since the coordinate transformation is
5718
                    // from target to source
5719
0
                    dfValueReal =
5720
0
                        dfValueReal * poWK->dfMultFactorVerticalShift -
5721
0
                        padfZ[iDstX] * dfMultFactorVerticalShiftPipeline;
5722
0
                }
5723
5724
0
                bHasFoundDensity = true;
5725
5726
                /* --------------------------------------------------------------------
5727
                 */
5728
                /*      We have a computed value from the source.  Now apply it
5729
                 * to      */
5730
                /*      the destination pixel. */
5731
                /* --------------------------------------------------------------------
5732
                 */
5733
0
                GWKSetPixelValue(poWK, iBand, iDstOffset, dfBandDensity,
5734
0
                                 dfValueReal, dfValueImag,
5735
0
                                 bAvoidNoDataSingleBand);
5736
0
            }
5737
5738
0
            if (!bHasFoundDensity)
5739
0
                continue;
5740
5741
0
            if (!bAvoidNoDataSingleBand)
5742
0
            {
5743
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
5744
0
            }
5745
5746
            /* --------------------------------------------------------------------
5747
             */
5748
            /*      Update destination density/validity masks. */
5749
            /* --------------------------------------------------------------------
5750
             */
5751
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
5752
5753
0
            if (poWK->panDstValid != nullptr)
5754
0
            {
5755
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
5756
0
            }
5757
0
        } /* Next iDstX */
5758
5759
        /* --------------------------------------------------------------------
5760
         */
5761
        /*      Report progress to the user, and optionally cancel out. */
5762
        /* --------------------------------------------------------------------
5763
         */
5764
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
5765
0
            break;
5766
0
    }
5767
5768
    /* -------------------------------------------------------------------- */
5769
    /*      Cleanup and return.                                             */
5770
    /* -------------------------------------------------------------------- */
5771
0
    CPLFree(padfX);
5772
0
    CPLFree(padfY);
5773
0
    CPLFree(padfZ);
5774
0
    CPLFree(pabSuccess);
5775
0
    if (psWrkStruct)
5776
0
        GWKResampleDeleteWrkStruct(psWrkStruct);
5777
0
}
5778
5779
static CPLErr GWKGeneralCase(GDALWarpKernel *poWK)
5780
0
{
5781
0
    return GWKRun(poWK, "GWKGeneralCase", GWKGeneralCaseThread);
5782
0
}
5783
5784
/************************************************************************/
5785
/*                            GWKRealCase()                             */
5786
/*                                                                      */
5787
/*      General case for non-complex data types.                        */
5788
/************************************************************************/
5789
5790
static void GWKRealCaseThread(void *pData)
5791
5792
0
{
5793
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
5794
0
    GDALWarpKernel *poWK = psJob->poWK;
5795
0
    const int iYMin = psJob->iYMin;
5796
0
    const int iYMax = psJob->iYMax;
5797
5798
0
    const int nDstXSize = poWK->nDstXSize;
5799
0
    const int nSrcXSize = poWK->nSrcXSize;
5800
0
    const int nSrcYSize = poWK->nSrcYSize;
5801
0
    const double dfMultFactorVerticalShiftPipeline =
5802
0
        poWK->bApplyVerticalShift
5803
0
            ? CPLAtof(CSLFetchNameValueDef(
5804
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
5805
0
                  "1.0"))
5806
0
            : 0.0;
5807
0
    const bool bAvoidNoDataSingleBand =
5808
0
        poWK->nBands == 1 ||
5809
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
5810
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
5811
5812
    /* -------------------------------------------------------------------- */
5813
    /*      Allocate x,y,z coordinate arrays for transformation ... one     */
5814
    /*      scanlines worth of positions.                                   */
5815
    /* -------------------------------------------------------------------- */
5816
5817
    // For x, 2 *, because we cache the precomputed values at the end.
5818
0
    double *padfX =
5819
0
        static_cast<double *>(CPLMalloc(2 * sizeof(double) * nDstXSize));
5820
0
    double *padfY =
5821
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
5822
0
    double *padfZ =
5823
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
5824
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
5825
5826
0
    const bool bUse4SamplesFormula = CanUse4SamplesFormula(poWK);
5827
5828
0
    GWKResampleWrkStruct *psWrkStruct = nullptr;
5829
0
    if (poWK->eResample != GRA_NearestNeighbour)
5830
0
    {
5831
0
        psWrkStruct = GWKResampleCreateWrkStruct(poWK);
5832
0
    }
5833
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
5834
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
5835
0
    const double dfErrorThreshold = CPLAtof(
5836
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
5837
5838
0
    const bool bSrcMaskIsDensity = poWK->panUnifiedSrcValid == nullptr &&
5839
0
                                   poWK->papanBandSrcValid == nullptr &&
5840
0
                                   poWK->pafUnifiedSrcDensity != nullptr;
5841
5842
0
    const bool bOneSourceCornerFailsToReproject =
5843
0
        GWKOneSourceCornerFailsToReproject(psJob);
5844
5845
    // Precompute values.
5846
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5847
0
        padfX[nDstXSize + iDstX] = iDstX + 0.5 + poWK->nDstXOff;
5848
5849
    /* ==================================================================== */
5850
    /*      Loop over output lines.                                         */
5851
    /* ==================================================================== */
5852
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
5853
0
    {
5854
        /* --------------------------------------------------------------------
5855
         */
5856
        /*      Setup points to transform to source image space. */
5857
        /* --------------------------------------------------------------------
5858
         */
5859
0
        memcpy(padfX, padfX + nDstXSize, sizeof(double) * nDstXSize);
5860
0
        const double dfY = iDstY + 0.5 + poWK->nDstYOff;
5861
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5862
0
            padfY[iDstX] = dfY;
5863
0
        memset(padfZ, 0, sizeof(double) * nDstXSize);
5864
5865
        /* --------------------------------------------------------------------
5866
         */
5867
        /*      Transform the points from destination pixel/line coordinates */
5868
        /*      to source pixel/line coordinates. */
5869
        /* --------------------------------------------------------------------
5870
         */
5871
0
        poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX,
5872
0
                             padfY, padfZ, pabSuccess);
5873
0
        if (dfSrcCoordPrecision > 0.0)
5874
0
        {
5875
0
            GWKRoundSourceCoordinates(
5876
0
                nDstXSize, padfX, padfY, padfZ, pabSuccess, dfSrcCoordPrecision,
5877
0
                dfErrorThreshold, poWK->pfnTransformer, psJob->pTransformerArg,
5878
0
                0.5 + poWK->nDstXOff, iDstY + 0.5 + poWK->nDstYOff);
5879
0
        }
5880
5881
        /* ====================================================================
5882
         */
5883
        /*      Loop over pixels in output scanline. */
5884
        /* ====================================================================
5885
         */
5886
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
5887
0
        {
5888
0
            GPtrDiff_t iSrcOffset = 0;
5889
0
            if (!GWKCheckAndComputeSrcOffsets(psJob, pabSuccess, iDstX, iDstY,
5890
0
                                              padfX, padfY, nSrcXSize,
5891
0
                                              nSrcYSize, iSrcOffset))
5892
0
                continue;
5893
5894
            /* --------------------------------------------------------------------
5895
             */
5896
            /*      Do not try to apply transparent/invalid source pixels to the
5897
             */
5898
            /*      destination.  This currently ignores the multi-pixel input
5899
             */
5900
            /*      of bilinear and cubic resamples. */
5901
            /* --------------------------------------------------------------------
5902
             */
5903
0
            double dfDensity = 1.0;
5904
5905
0
            if (poWK->pafUnifiedSrcDensity != nullptr)
5906
0
            {
5907
0
                dfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
5908
0
                if (dfDensity < SRC_DENSITY_THRESHOLD_DOUBLE)
5909
0
                {
5910
0
                    if (!bOneSourceCornerFailsToReproject)
5911
0
                    {
5912
0
                        continue;
5913
0
                    }
5914
0
                    else if (GWKAdjustSrcOffsetOnEdgeUnifiedSrcDensity(
5915
0
                                 psJob, iSrcOffset))
5916
0
                    {
5917
0
                        dfDensity =
5918
0
                            double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
5919
0
                    }
5920
0
                    else
5921
0
                    {
5922
0
                        continue;
5923
0
                    }
5924
0
                }
5925
0
            }
5926
5927
0
            if (poWK->panUnifiedSrcValid != nullptr &&
5928
0
                !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
5929
0
            {
5930
0
                if (!bOneSourceCornerFailsToReproject)
5931
0
                {
5932
0
                    continue;
5933
0
                }
5934
0
                else if (!GWKAdjustSrcOffsetOnEdge(psJob, iSrcOffset))
5935
0
                {
5936
0
                    continue;
5937
0
                }
5938
0
            }
5939
5940
            /* ====================================================================
5941
             */
5942
            /*      Loop processing each band. */
5943
            /* ====================================================================
5944
             */
5945
0
            bool bHasFoundDensity = false;
5946
5947
0
            const GPtrDiff_t iDstOffset =
5948
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
5949
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
5950
0
            {
5951
0
                double dfBandDensity = 0.0;
5952
0
                double dfValueReal = 0.0;
5953
5954
                /* --------------------------------------------------------------------
5955
                 */
5956
                /*      Collect the source value. */
5957
                /* --------------------------------------------------------------------
5958
                 */
5959
0
                if (poWK->eResample == GRA_NearestNeighbour || nSrcXSize == 1 ||
5960
0
                    nSrcYSize == 1)
5961
0
                {
5962
                    // FALSE is returned if dfBandDensity == 0, which is
5963
                    // checked below.
5964
0
                    CPL_IGNORE_RET_VAL(GWKGetPixelValueReal(
5965
0
                        poWK, iBand, iSrcOffset, &dfBandDensity, &dfValueReal));
5966
0
                }
5967
0
                else if (poWK->eResample == GRA_Bilinear && bUse4SamplesFormula)
5968
0
                {
5969
0
                    double dfValueImagIgnored = 0.0;
5970
0
                    GWKBilinearResample4Sample(
5971
0
                        poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5972
0
                        padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5973
0
                        &dfValueReal, &dfValueImagIgnored);
5974
0
                }
5975
0
                else if (poWK->eResample == GRA_Cubic && bUse4SamplesFormula)
5976
0
                {
5977
0
                    if (bSrcMaskIsDensity)
5978
0
                    {
5979
0
                        if (poWK->eWorkingDataType == GDT_UInt8)
5980
0
                        {
5981
0
                            GWKCubicResampleSrcMaskIsDensity4SampleRealT<GByte>(
5982
0
                                poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5983
0
                                padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5984
0
                                &dfValueReal);
5985
0
                        }
5986
0
                        else if (poWK->eWorkingDataType == GDT_UInt16)
5987
0
                        {
5988
0
                            GWKCubicResampleSrcMaskIsDensity4SampleRealT<
5989
0
                                GUInt16>(poWK, iBand,
5990
0
                                         padfX[iDstX] - poWK->nSrcXOff,
5991
0
                                         padfY[iDstX] - poWK->nSrcYOff,
5992
0
                                         &dfBandDensity, &dfValueReal);
5993
0
                        }
5994
0
                        else
5995
0
                        {
5996
0
                            GWKCubicResampleSrcMaskIsDensity4SampleReal(
5997
0
                                poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
5998
0
                                padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
5999
0
                                &dfValueReal);
6000
0
                        }
6001
0
                    }
6002
0
                    else
6003
0
                    {
6004
0
                        double dfValueImagIgnored = 0.0;
6005
0
                        GWKCubicResample4Sample(
6006
0
                            poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
6007
0
                            padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
6008
0
                            &dfValueReal, &dfValueImagIgnored);
6009
0
                    }
6010
0
                }
6011
0
                else
6012
0
#ifdef DEBUG
6013
                    // Only useful for clang static analyzer.
6014
0
                    if (psWrkStruct != nullptr)
6015
0
#endif
6016
0
                    {
6017
0
                        double dfValueImagIgnored = 0.0;
6018
0
                        psWrkStruct->pfnGWKResample(
6019
0
                            poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
6020
0
                            padfY[iDstX] - poWK->nSrcYOff, &dfBandDensity,
6021
0
                            &dfValueReal, &dfValueImagIgnored, psWrkStruct);
6022
0
                    }
6023
6024
                // If we didn't find any valid inputs skip to next band.
6025
0
                if (dfBandDensity < BAND_DENSITY_THRESHOLD)
6026
0
                    continue;
6027
6028
0
                if (poWK->bApplyVerticalShift)
6029
0
                {
6030
0
                    if (!std::isfinite(padfZ[iDstX]))
6031
0
                        continue;
6032
                    // Subtract padfZ[] since the coordinate transformation is
6033
                    // from target to source
6034
0
                    dfValueReal =
6035
0
                        dfValueReal * poWK->dfMultFactorVerticalShift -
6036
0
                        padfZ[iDstX] * dfMultFactorVerticalShiftPipeline;
6037
0
                }
6038
6039
0
                bHasFoundDensity = true;
6040
6041
                /* --------------------------------------------------------------------
6042
                 */
6043
                /*      We have a computed value from the source.  Now apply it
6044
                 * to      */
6045
                /*      the destination pixel. */
6046
                /* --------------------------------------------------------------------
6047
                 */
6048
0
                GWKSetPixelValueReal(poWK, iBand, iDstOffset, dfBandDensity,
6049
0
                                     dfValueReal, bAvoidNoDataSingleBand);
6050
0
            }
6051
6052
0
            if (!bHasFoundDensity)
6053
0
                continue;
6054
6055
0
            if (!bAvoidNoDataSingleBand)
6056
0
            {
6057
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
6058
0
            }
6059
6060
            /* --------------------------------------------------------------------
6061
             */
6062
            /*      Update destination density/validity masks. */
6063
            /* --------------------------------------------------------------------
6064
             */
6065
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
6066
6067
0
            if (poWK->panDstValid != nullptr)
6068
0
            {
6069
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
6070
0
            }
6071
0
        }  // Next iDstX.
6072
6073
        /* --------------------------------------------------------------------
6074
         */
6075
        /*      Report progress to the user, and optionally cancel out. */
6076
        /* --------------------------------------------------------------------
6077
         */
6078
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
6079
0
            break;
6080
0
    }
6081
6082
    /* -------------------------------------------------------------------- */
6083
    /*      Cleanup and return.                                             */
6084
    /* -------------------------------------------------------------------- */
6085
0
    CPLFree(padfX);
6086
0
    CPLFree(padfY);
6087
0
    CPLFree(padfZ);
6088
0
    CPLFree(pabSuccess);
6089
0
    if (psWrkStruct)
6090
0
        GWKResampleDeleteWrkStruct(psWrkStruct);
6091
0
}
6092
6093
static CPLErr GWKRealCase(GDALWarpKernel *poWK)
6094
0
{
6095
0
    return GWKRun(poWK, "GWKRealCase", GWKRealCaseThread);
6096
0
}
6097
6098
/************************************************************************/
6099
/*                 GWKCubicResampleNoMasks4MultiBandT()                 */
6100
/************************************************************************/
6101
6102
/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
6103
/* and enough SSE registries */
6104
#if defined(USE_SSE2)
6105
6106
static inline float Convolute4x4(const __m128 row0, const __m128 row1,
6107
                                 const __m128 row2, const __m128 row3,
6108
                                 const __m128 weightsXY0,
6109
                                 const __m128 weightsXY1,
6110
                                 const __m128 weightsXY2,
6111
                                 const __m128 weightsXY3)
6112
0
{
6113
0
    return XMMHorizontalAdd(_mm_add_ps(
6114
0
        _mm_add_ps(_mm_mul_ps(row0, weightsXY0), _mm_mul_ps(row1, weightsXY1)),
6115
0
        _mm_add_ps(_mm_mul_ps(row2, weightsXY2),
6116
0
                   _mm_mul_ps(row3, weightsXY3))));
6117
0
}
6118
6119
template <class T>
6120
static void GWKCubicResampleNoMasks4MultiBandT(const GDALWarpKernel *poWK,
6121
                                               double dfSrcX, double dfSrcY,
6122
                                               const GPtrDiff_t iDstOffset)
6123
0
{
6124
0
    const double dfSrcXShifted = dfSrcX - 0.5;
6125
0
    const int iSrcX = static_cast<int>(dfSrcXShifted);
6126
0
    const double dfSrcYShifted = dfSrcY - 0.5;
6127
0
    const int iSrcY = static_cast<int>(dfSrcYShifted);
6128
0
    const GPtrDiff_t iSrcOffset =
6129
0
        iSrcX + static_cast<GPtrDiff_t>(iSrcY) * poWK->nSrcXSize;
6130
6131
    // Get the bilinear interpolation at the image borders.
6132
0
    if (iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize || iSrcY - 1 < 0 ||
6133
0
        iSrcY + 2 >= poWK->nSrcYSize)
6134
0
    {
6135
0
        for (int iBand = 0; iBand < poWK->nBands; iBand++)
6136
0
        {
6137
0
            T value;
6138
0
            GWKBilinearResampleNoMasks4SampleT(poWK, iBand, dfSrcX, dfSrcY,
6139
0
                                               &value);
6140
0
            reinterpret_cast<T *>(poWK->papabyDstImage[iBand])[iDstOffset] =
6141
0
                value;
6142
0
        }
6143
0
    }
6144
0
    else
6145
0
    {
6146
0
        const float fDeltaX = static_cast<float>(dfSrcXShifted) - iSrcX;
6147
0
        const float fDeltaY = static_cast<float>(dfSrcYShifted) - iSrcY;
6148
6149
0
        float afCoeffsX[4];
6150
0
        float afCoeffsY[4];
6151
0
        GWKCubicComputeWeights(fDeltaX, afCoeffsX);
6152
0
        GWKCubicComputeWeights(fDeltaY, afCoeffsY);
6153
0
        const auto weightsX = _mm_loadu_ps(afCoeffsX);
6154
0
        const auto weightsXY0 =
6155
0
            _mm_mul_ps(_mm_load1_ps(&afCoeffsY[0]), weightsX);
6156
0
        const auto weightsXY1 =
6157
0
            _mm_mul_ps(_mm_load1_ps(&afCoeffsY[1]), weightsX);
6158
0
        const auto weightsXY2 =
6159
0
            _mm_mul_ps(_mm_load1_ps(&afCoeffsY[2]), weightsX);
6160
0
        const auto weightsXY3 =
6161
0
            _mm_mul_ps(_mm_load1_ps(&afCoeffsY[3]), weightsX);
6162
6163
0
        const GPtrDiff_t iOffset = iSrcOffset - poWK->nSrcXSize - 1;
6164
6165
0
        int iBand = 0;
6166
        // Process 2 bands at a time
6167
0
        for (; iBand + 1 < poWK->nBands; iBand += 2)
6168
0
        {
6169
0
            const T *CPL_RESTRICT pBand0 =
6170
0
                reinterpret_cast<const T *>(poWK->papabySrcImage[iBand]);
6171
0
            const auto row0_0 = XMMLoad4Values(pBand0 + iOffset);
6172
0
            const auto row1_0 =
6173
0
                XMMLoad4Values(pBand0 + iOffset + poWK->nSrcXSize);
6174
0
            const auto row2_0 =
6175
0
                XMMLoad4Values(pBand0 + iOffset + 2 * poWK->nSrcXSize);
6176
0
            const auto row3_0 =
6177
0
                XMMLoad4Values(pBand0 + iOffset + 3 * poWK->nSrcXSize);
6178
6179
0
            const T *CPL_RESTRICT pBand1 =
6180
0
                reinterpret_cast<const T *>(poWK->papabySrcImage[iBand + 1]);
6181
0
            const auto row0_1 = XMMLoad4Values(pBand1 + iOffset);
6182
0
            const auto row1_1 =
6183
0
                XMMLoad4Values(pBand1 + iOffset + poWK->nSrcXSize);
6184
0
            const auto row2_1 =
6185
0
                XMMLoad4Values(pBand1 + iOffset + 2 * poWK->nSrcXSize);
6186
0
            const auto row3_1 =
6187
0
                XMMLoad4Values(pBand1 + iOffset + 3 * poWK->nSrcXSize);
6188
6189
0
            const float fValue_0 =
6190
0
                Convolute4x4(row0_0, row1_0, row2_0, row3_0, weightsXY0,
6191
0
                             weightsXY1, weightsXY2, weightsXY3);
6192
6193
0
            const float fValue_1 =
6194
0
                Convolute4x4(row0_1, row1_1, row2_1, row3_1, weightsXY0,
6195
0
                             weightsXY1, weightsXY2, weightsXY3);
6196
6197
0
            T *CPL_RESTRICT pDstBand0 =
6198
0
                reinterpret_cast<T *>(poWK->papabyDstImage[iBand]);
6199
0
            pDstBand0[iDstOffset] = GWKClampValueT<T>(fValue_0);
6200
6201
0
            T *CPL_RESTRICT pDstBand1 =
6202
0
                reinterpret_cast<T *>(poWK->papabyDstImage[iBand + 1]);
6203
0
            pDstBand1[iDstOffset] = GWKClampValueT<T>(fValue_1);
6204
0
        }
6205
0
        if (iBand < poWK->nBands)
6206
0
        {
6207
0
            const T *CPL_RESTRICT pBand0 =
6208
0
                reinterpret_cast<const T *>(poWK->papabySrcImage[iBand]);
6209
0
            const auto row0 = XMMLoad4Values(pBand0 + iOffset);
6210
0
            const auto row1 =
6211
0
                XMMLoad4Values(pBand0 + iOffset + poWK->nSrcXSize);
6212
0
            const auto row2 =
6213
0
                XMMLoad4Values(pBand0 + iOffset + 2 * poWK->nSrcXSize);
6214
0
            const auto row3 =
6215
0
                XMMLoad4Values(pBand0 + iOffset + 3 * poWK->nSrcXSize);
6216
6217
0
            const float fValue =
6218
0
                Convolute4x4(row0, row1, row2, row3, weightsXY0, weightsXY1,
6219
0
                             weightsXY2, weightsXY3);
6220
6221
0
            T *CPL_RESTRICT pDstBand =
6222
0
                reinterpret_cast<T *>(poWK->papabyDstImage[iBand]);
6223
0
            pDstBand[iDstOffset] = GWKClampValueT<T>(fValue);
6224
0
        }
6225
0
    }
6226
6227
0
    if (poWK->pafDstDensity)
6228
0
        poWK->pafDstDensity[iDstOffset] = 1.0f;
6229
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKCubicResampleNoMasks4MultiBandT<unsigned char>(GDALWarpKernel const*, double, double, long long)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKCubicResampleNoMasks4MultiBandT<unsigned short>(GDALWarpKernel const*, double, double, long long)
6230
6231
#endif  // defined(USE_SSE2)
6232
6233
/************************************************************************/
6234
/*          GWKResampleNoMasksOrDstDensityOnlyThreadInternal()          */
6235
/************************************************************************/
6236
6237
template <class T, GDALResampleAlg eResample, int bUse4SamplesFormula>
6238
static void GWKResampleNoMasksOrDstDensityOnlyThreadInternal(void *pData)
6239
6240
0
{
6241
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
6242
0
    GDALWarpKernel *poWK = psJob->poWK;
6243
0
    const int iYMin = psJob->iYMin;
6244
0
    const int iYMax = psJob->iYMax;
6245
0
    const double dfMultFactorVerticalShiftPipeline =
6246
0
        poWK->bApplyVerticalShift
6247
0
            ? CPLAtof(CSLFetchNameValueDef(
6248
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
6249
0
                  "1.0"))
6250
0
            : 0.0;
6251
6252
0
    const int nDstXSize = poWK->nDstXSize;
6253
0
    const int nSrcXSize = poWK->nSrcXSize;
6254
0
    const int nSrcYSize = poWK->nSrcYSize;
6255
6256
    /* -------------------------------------------------------------------- */
6257
    /*      Allocate x,y,z coordinate arrays for transformation ... one     */
6258
    /*      scanlines worth of positions.                                   */
6259
    /* -------------------------------------------------------------------- */
6260
6261
    // For x, 2 *, because we cache the precomputed values at the end.
6262
0
    double *padfX =
6263
0
        static_cast<double *>(CPLMalloc(2 * sizeof(double) * nDstXSize));
6264
0
    double *padfY =
6265
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
6266
0
    double *padfZ =
6267
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
6268
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
6269
6270
0
    const int nXRadius = poWK->nXRadius;
6271
0
    double *padfWeightsX =
6272
0
        static_cast<double *>(CPLCalloc(1 + nXRadius * 2, sizeof(double)));
6273
0
    double *padfWeightsY = static_cast<double *>(
6274
0
        CPLCalloc(1 + poWK->nYRadius * 2, sizeof(double)));
6275
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
6276
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
6277
0
    const double dfErrorThreshold = CPLAtof(
6278
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
6279
6280
    // Precompute values.
6281
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6282
0
        padfX[nDstXSize + iDstX] = iDstX + 0.5 + poWK->nDstXOff;
6283
6284
    /* ==================================================================== */
6285
    /*      Loop over output lines.                                         */
6286
    /* ==================================================================== */
6287
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
6288
0
    {
6289
        /* --------------------------------------------------------------------
6290
         */
6291
        /*      Setup points to transform to source image space. */
6292
        /* --------------------------------------------------------------------
6293
         */
6294
0
        memcpy(padfX, padfX + nDstXSize, sizeof(double) * nDstXSize);
6295
0
        const double dfY = iDstY + 0.5 + poWK->nDstYOff;
6296
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6297
0
            padfY[iDstX] = dfY;
6298
0
        memset(padfZ, 0, sizeof(double) * nDstXSize);
6299
6300
        /* --------------------------------------------------------------------
6301
         */
6302
        /*      Transform the points from destination pixel/line coordinates */
6303
        /*      to source pixel/line coordinates. */
6304
        /* --------------------------------------------------------------------
6305
         */
6306
0
        poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX,
6307
0
                             padfY, padfZ, pabSuccess);
6308
0
        if (dfSrcCoordPrecision > 0.0)
6309
0
        {
6310
0
            GWKRoundSourceCoordinates(
6311
0
                nDstXSize, padfX, padfY, padfZ, pabSuccess, dfSrcCoordPrecision,
6312
0
                dfErrorThreshold, poWK->pfnTransformer, psJob->pTransformerArg,
6313
0
                0.5 + poWK->nDstXOff, iDstY + 0.5 + poWK->nDstYOff);
6314
0
        }
6315
6316
        /* ====================================================================
6317
         */
6318
        /*      Loop over pixels in output scanline. */
6319
        /* ====================================================================
6320
         */
6321
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6322
0
        {
6323
0
            GPtrDiff_t iSrcOffset = 0;
6324
0
            if (!GWKCheckAndComputeSrcOffsets(psJob, pabSuccess, iDstX, iDstY,
6325
0
                                              padfX, padfY, nSrcXSize,
6326
0
                                              nSrcYSize, iSrcOffset))
6327
0
                continue;
6328
6329
            /* ====================================================================
6330
             */
6331
            /*      Loop processing each band. */
6332
            /* ====================================================================
6333
             */
6334
0
            const GPtrDiff_t iDstOffset =
6335
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
6336
6337
0
#if defined(USE_SSE2)
6338
            if constexpr (bUse4SamplesFormula && eResample == GRA_Cubic &&
6339
                          (std::is_same<T, GByte>::value ||
6340
                           std::is_same<T, GUInt16>::value))
6341
0
            {
6342
0
                if (poWK->nBands > 1 && !poWK->bApplyVerticalShift)
6343
0
                {
6344
0
                    GWKCubicResampleNoMasks4MultiBandT<T>(
6345
0
                        poWK, padfX[iDstX] - poWK->nSrcXOff,
6346
0
                        padfY[iDstX] - poWK->nSrcYOff, iDstOffset);
6347
6348
0
                    continue;
6349
0
                }
6350
0
            }
6351
0
#endif  // defined(USE_SSE2)
6352
6353
0
            [[maybe_unused]] double dfInvWeights = 0;
6354
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
6355
0
            {
6356
0
                T value = 0;
6357
                if constexpr (eResample == GRA_NearestNeighbour)
6358
0
                {
6359
0
                    value = reinterpret_cast<T *>(
6360
0
                        poWK->papabySrcImage[iBand])[iSrcOffset];
6361
                }
6362
                else if constexpr (bUse4SamplesFormula)
6363
0
                {
6364
                    if constexpr (eResample == GRA_Bilinear)
6365
0
                        GWKBilinearResampleNoMasks4SampleT(
6366
0
                            poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
6367
                            padfY[iDstX] - poWK->nSrcYOff, &value);
6368
                    else
6369
0
                        GWKCubicResampleNoMasks4SampleT(
6370
0
                            poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
6371
0
                            padfY[iDstX] - poWK->nSrcYOff, &value);
6372
                }
6373
                else
6374
0
                {
6375
0
                    GWKResampleNoMasksT(
6376
0
                        poWK, iBand, padfX[iDstX] - poWK->nSrcXOff,
6377
0
                        padfY[iDstX] - poWK->nSrcYOff, &value, padfWeightsX,
6378
0
                        padfWeightsY, dfInvWeights);
6379
0
                }
6380
6381
0
                if (poWK->bApplyVerticalShift)
6382
0
                {
6383
0
                    if (!std::isfinite(padfZ[iDstX]))
6384
0
                        continue;
6385
                    // Subtract padfZ[] since the coordinate transformation is
6386
                    // from target to source
6387
0
                    value = GWKClampValueT<T>(
6388
0
                        double(value) * poWK->dfMultFactorVerticalShift -
6389
0
                        padfZ[iDstX] * dfMultFactorVerticalShiftPipeline);
6390
0
                }
6391
6392
0
                if (poWK->pafDstDensity)
6393
0
                    poWK->pafDstDensity[iDstOffset] = 1.0f;
6394
6395
0
                reinterpret_cast<T *>(poWK->papabyDstImage[iBand])[iDstOffset] =
6396
0
                    value;
6397
0
            }
6398
0
        }
6399
6400
        /* --------------------------------------------------------------------
6401
         */
6402
        /*      Report progress to the user, and optionally cancel out. */
6403
        /* --------------------------------------------------------------------
6404
         */
6405
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
6406
0
            break;
6407
0
    }
6408
6409
    /* -------------------------------------------------------------------- */
6410
    /*      Cleanup and return.                                             */
6411
    /* -------------------------------------------------------------------- */
6412
0
    CPLFree(padfX);
6413
0
    CPLFree(padfY);
6414
0
    CPLFree(padfZ);
6415
0
    CPLFree(pabSuccess);
6416
0
    CPLFree(padfWeightsX);
6417
0
    CPLFree(padfWeightsY);
6418
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)0, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)1, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)1, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)2, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)2, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<float, (GDALResampleAlg)2, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<float, (GDALResampleAlg)2, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned char, (GDALResampleAlg)3, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)0, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)1, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)1, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned short, (GDALResampleAlg)1, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned short, (GDALResampleAlg)1, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<float, (GDALResampleAlg)1, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<float, (GDALResampleAlg)1, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)2, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)2, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned short, (GDALResampleAlg)2, 1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned short, (GDALResampleAlg)2, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<short, (GDALResampleAlg)3, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<unsigned short, (GDALResampleAlg)3, 0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThreadInternal<float, (GDALResampleAlg)0, 0>(void*)
6419
6420
template <class T, GDALResampleAlg eResample>
6421
static void GWKResampleNoMasksOrDstDensityOnlyThread(void *pData)
6422
0
{
6423
0
    GWKResampleNoMasksOrDstDensityOnlyThreadInternal<T, eResample, FALSE>(
6424
0
        pData);
6425
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<unsigned char, (GDALResampleAlg)0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<unsigned char, (GDALResampleAlg)3>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<short, (GDALResampleAlg)0>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<short, (GDALResampleAlg)3>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<unsigned short, (GDALResampleAlg)3>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyThread<float, (GDALResampleAlg)0>(void*)
6426
6427
template <class T, GDALResampleAlg eResample>
6428
static void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread(void *pData)
6429
6430
0
{
6431
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
6432
0
    GDALWarpKernel *poWK = psJob->poWK;
6433
0
    static_assert(eResample == GRA_Bilinear || eResample == GRA_Cubic);
6434
0
    const bool bUse4SamplesFormula = CanUse4SamplesFormula(poWK);
6435
0
    if (bUse4SamplesFormula)
6436
0
        GWKResampleNoMasksOrDstDensityOnlyThreadInternal<T, eResample, TRUE>(
6437
0
            pData);
6438
0
    else
6439
0
        GWKResampleNoMasksOrDstDensityOnlyThreadInternal<T, eResample, FALSE>(
6440
0
            pData);
6441
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<unsigned char, (GDALResampleAlg)1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<unsigned char, (GDALResampleAlg)2>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<float, (GDALResampleAlg)2>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<short, (GDALResampleAlg)1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<unsigned short, (GDALResampleAlg)1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<float, (GDALResampleAlg)1>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<short, (GDALResampleAlg)2>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<unsigned short, (GDALResampleAlg)2>(void*)
6442
6443
static CPLErr GWKNearestNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK)
6444
0
{
6445
0
    return GWKRun(
6446
0
        poWK, "GWKNearestNoMasksOrDstDensityOnlyByte",
6447
0
        GWKResampleNoMasksOrDstDensityOnlyThread<GByte, GRA_NearestNeighbour>);
6448
0
}
6449
6450
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK)
6451
0
{
6452
0
    return GWKRun(
6453
0
        poWK, "GWKBilinearNoMasksOrDstDensityOnlyByte",
6454
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GByte,
6455
0
                                                           GRA_Bilinear>);
6456
0
}
6457
6458
static CPLErr GWKCubicNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK)
6459
0
{
6460
0
    return GWKRun(
6461
0
        poWK, "GWKCubicNoMasksOrDstDensityOnlyByte",
6462
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GByte, GRA_Cubic>);
6463
0
}
6464
6465
static CPLErr GWKCubicNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK)
6466
0
{
6467
0
    return GWKRun(
6468
0
        poWK, "GWKCubicNoMasksOrDstDensityOnlyFloat",
6469
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<float, GRA_Cubic>);
6470
0
}
6471
6472
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
6473
6474
static CPLErr GWKCubicNoMasksOrDstDensityOnlyDouble(GDALWarpKernel *poWK)
6475
{
6476
    return GWKRun(
6477
        poWK, "GWKCubicNoMasksOrDstDensityOnlyDouble",
6478
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<double, GRA_Cubic>);
6479
}
6480
#endif
6481
6482
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyByte(GDALWarpKernel *poWK)
6483
0
{
6484
0
    return GWKRun(
6485
0
        poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyByte",
6486
0
        GWKResampleNoMasksOrDstDensityOnlyThread<GByte, GRA_CubicSpline>);
6487
0
}
6488
6489
/************************************************************************/
6490
/*                          GWKNearestByte()                            */
6491
/*                                                                      */
6492
/*      Case for 8bit input data with nearest neighbour resampling      */
6493
/*      using valid flags. Should be as fast as possible for this       */
6494
/*      particular transformation type.                                 */
6495
/************************************************************************/
6496
6497
template <class T> static void GWKNearestThread(void *pData)
6498
6499
0
{
6500
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
6501
0
    GDALWarpKernel *poWK = psJob->poWK;
6502
0
    const int iYMin = psJob->iYMin;
6503
0
    const int iYMax = psJob->iYMax;
6504
0
    const double dfMultFactorVerticalShiftPipeline =
6505
0
        poWK->bApplyVerticalShift
6506
0
            ? CPLAtof(CSLFetchNameValueDef(
6507
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
6508
0
                  "1.0"))
6509
0
            : 0.0;
6510
0
    const bool bAvoidNoDataSingleBand =
6511
0
        poWK->nBands == 1 ||
6512
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
6513
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
6514
6515
0
    const int nDstXSize = poWK->nDstXSize;
6516
0
    const int nSrcXSize = poWK->nSrcXSize;
6517
0
    const int nSrcYSize = poWK->nSrcYSize;
6518
6519
    /* -------------------------------------------------------------------- */
6520
    /*      Allocate x,y,z coordinate arrays for transformation ... one     */
6521
    /*      scanlines worth of positions.                                   */
6522
    /* -------------------------------------------------------------------- */
6523
6524
    // For x, 2 *, because we cache the precomputed values at the end.
6525
0
    double *padfX =
6526
0
        static_cast<double *>(CPLMalloc(2 * sizeof(double) * nDstXSize));
6527
0
    double *padfY =
6528
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
6529
0
    double *padfZ =
6530
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
6531
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
6532
6533
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
6534
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
6535
0
    const double dfErrorThreshold = CPLAtof(
6536
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
6537
6538
0
    const bool bOneSourceCornerFailsToReproject =
6539
0
        GWKOneSourceCornerFailsToReproject(psJob);
6540
6541
    // Precompute values.
6542
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6543
0
        padfX[nDstXSize + iDstX] = iDstX + 0.5 + poWK->nDstXOff;
6544
6545
    /* ==================================================================== */
6546
    /*      Loop over output lines.                                         */
6547
    /* ==================================================================== */
6548
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
6549
0
    {
6550
6551
        /* --------------------------------------------------------------------
6552
         */
6553
        /*      Setup points to transform to source image space. */
6554
        /* --------------------------------------------------------------------
6555
         */
6556
0
        memcpy(padfX, padfX + nDstXSize, sizeof(double) * nDstXSize);
6557
0
        const double dfY = iDstY + 0.5 + poWK->nDstYOff;
6558
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6559
0
            padfY[iDstX] = dfY;
6560
0
        memset(padfZ, 0, sizeof(double) * nDstXSize);
6561
6562
        /* --------------------------------------------------------------------
6563
         */
6564
        /*      Transform the points from destination pixel/line coordinates */
6565
        /*      to source pixel/line coordinates. */
6566
        /* --------------------------------------------------------------------
6567
         */
6568
0
        poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX,
6569
0
                             padfY, padfZ, pabSuccess);
6570
0
        if (dfSrcCoordPrecision > 0.0)
6571
0
        {
6572
0
            GWKRoundSourceCoordinates(
6573
0
                nDstXSize, padfX, padfY, padfZ, pabSuccess, dfSrcCoordPrecision,
6574
0
                dfErrorThreshold, poWK->pfnTransformer, psJob->pTransformerArg,
6575
0
                0.5 + poWK->nDstXOff, iDstY + 0.5 + poWK->nDstYOff);
6576
0
        }
6577
        /* ====================================================================
6578
         */
6579
        /*      Loop over pixels in output scanline. */
6580
        /* ====================================================================
6581
         */
6582
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6583
0
        {
6584
0
            GPtrDiff_t iSrcOffset = 0;
6585
0
            if (!GWKCheckAndComputeSrcOffsets(psJob, pabSuccess, iDstX, iDstY,
6586
0
                                              padfX, padfY, nSrcXSize,
6587
0
                                              nSrcYSize, iSrcOffset))
6588
0
                continue;
6589
6590
            /* --------------------------------------------------------------------
6591
             */
6592
            /*      Do not try to apply invalid source pixels to the dest. */
6593
            /* --------------------------------------------------------------------
6594
             */
6595
0
            if (poWK->panUnifiedSrcValid != nullptr &&
6596
0
                !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
6597
0
            {
6598
0
                if (!bOneSourceCornerFailsToReproject)
6599
0
                {
6600
0
                    continue;
6601
0
                }
6602
0
                else if (!GWKAdjustSrcOffsetOnEdge(psJob, iSrcOffset))
6603
0
                {
6604
0
                    continue;
6605
0
                }
6606
0
            }
6607
6608
            /* --------------------------------------------------------------------
6609
             */
6610
            /*      Do not try to apply transparent source pixels to the
6611
             * destination.*/
6612
            /* --------------------------------------------------------------------
6613
             */
6614
0
            double dfDensity = 1.0;
6615
6616
0
            if (poWK->pafUnifiedSrcDensity != nullptr)
6617
0
            {
6618
0
                dfDensity = double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
6619
0
                if (dfDensity < SRC_DENSITY_THRESHOLD_DOUBLE)
6620
0
                    continue;
6621
0
            }
6622
6623
            /* ====================================================================
6624
             */
6625
            /*      Loop processing each band. */
6626
            /* ====================================================================
6627
             */
6628
6629
0
            const GPtrDiff_t iDstOffset =
6630
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
6631
6632
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
6633
0
            {
6634
0
                T value = 0;
6635
0
                double dfBandDensity = 0.0;
6636
6637
                /* --------------------------------------------------------------------
6638
                 */
6639
                /*      Collect the source value. */
6640
                /* --------------------------------------------------------------------
6641
                 */
6642
0
                if (GWKGetPixelT(poWK, iBand, iSrcOffset, &dfBandDensity,
6643
0
                                 &value))
6644
0
                {
6645
6646
0
                    if (poWK->bApplyVerticalShift)
6647
0
                    {
6648
0
                        if (!std::isfinite(padfZ[iDstX]))
6649
0
                            continue;
6650
                        // Subtract padfZ[] since the coordinate transformation
6651
                        // is from target to source
6652
0
                        value = GWKClampValueT<T>(
6653
0
                            double(value) * poWK->dfMultFactorVerticalShift -
6654
0
                            padfZ[iDstX] * dfMultFactorVerticalShiftPipeline);
6655
0
                    }
6656
6657
0
                    GWKSetPixelValueRealT(poWK, iBand, iDstOffset,
6658
0
                                          dfBandDensity, value,
6659
0
                                          bAvoidNoDataSingleBand);
6660
0
                }
6661
0
            }
6662
6663
            /* --------------------------------------------------------------------
6664
             */
6665
            /*      Mark this pixel valid/opaque in the output. */
6666
            /* --------------------------------------------------------------------
6667
             */
6668
6669
0
            if (!bAvoidNoDataSingleBand)
6670
0
            {
6671
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
6672
0
            }
6673
6674
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
6675
6676
0
            if (poWK->panDstValid != nullptr)
6677
0
            {
6678
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
6679
0
            }
6680
0
        } /* Next iDstX */
6681
6682
        /* --------------------------------------------------------------------
6683
         */
6684
        /*      Report progress to the user, and optionally cancel out. */
6685
        /* --------------------------------------------------------------------
6686
         */
6687
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
6688
0
            break;
6689
0
    }
6690
6691
    /* -------------------------------------------------------------------- */
6692
    /*      Cleanup and return.                                             */
6693
    /* -------------------------------------------------------------------- */
6694
0
    CPLFree(padfX);
6695
0
    CPLFree(padfY);
6696
0
    CPLFree(padfZ);
6697
0
    CPLFree(pabSuccess);
6698
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKNearestThread<unsigned char>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKNearestThread<short>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKNearestThread<unsigned short>(void*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKNearestThread<float>(void*)
6699
6700
static CPLErr GWKNearestByte(GDALWarpKernel *poWK)
6701
0
{
6702
0
    return GWKRun(poWK, "GWKNearestByte", GWKNearestThread<GByte>);
6703
0
}
6704
6705
static CPLErr GWKNearestNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK)
6706
0
{
6707
0
    return GWKRun(
6708
0
        poWK, "GWKNearestNoMasksOrDstDensityOnlyShort",
6709
0
        GWKResampleNoMasksOrDstDensityOnlyThread<GInt16, GRA_NearestNeighbour>);
6710
0
}
6711
6712
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK)
6713
0
{
6714
0
    return GWKRun(
6715
0
        poWK, "GWKBilinearNoMasksOrDstDensityOnlyShort",
6716
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GInt16,
6717
0
                                                           GRA_Bilinear>);
6718
0
}
6719
6720
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *poWK)
6721
0
{
6722
0
    return GWKRun(
6723
0
        poWK, "GWKBilinearNoMasksOrDstDensityOnlyUShort",
6724
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GUInt16,
6725
0
                                                           GRA_Bilinear>);
6726
0
}
6727
6728
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK)
6729
0
{
6730
0
    return GWKRun(
6731
0
        poWK, "GWKBilinearNoMasksOrDstDensityOnlyFloat",
6732
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<float,
6733
0
                                                           GRA_Bilinear>);
6734
0
}
6735
6736
#ifdef INSTANTIATE_FLOAT64_SSE2_IMPL
6737
6738
static CPLErr GWKBilinearNoMasksOrDstDensityOnlyDouble(GDALWarpKernel *poWK)
6739
{
6740
    return GWKRun(
6741
        poWK, "GWKBilinearNoMasksOrDstDensityOnlyDouble",
6742
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<double,
6743
                                                           GRA_Bilinear>);
6744
}
6745
#endif
6746
6747
static CPLErr GWKCubicNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK)
6748
0
{
6749
0
    return GWKRun(
6750
0
        poWK, "GWKCubicNoMasksOrDstDensityOnlyShort",
6751
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GInt16, GRA_Cubic>);
6752
0
}
6753
6754
static CPLErr GWKCubicNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *poWK)
6755
0
{
6756
0
    return GWKRun(
6757
0
        poWK, "GWKCubicNoMasksOrDstDensityOnlyUShort",
6758
0
        GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread<GUInt16, GRA_Cubic>);
6759
0
}
6760
6761
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyShort(GDALWarpKernel *poWK)
6762
0
{
6763
0
    return GWKRun(
6764
0
        poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyShort",
6765
0
        GWKResampleNoMasksOrDstDensityOnlyThread<GInt16, GRA_CubicSpline>);
6766
0
}
6767
6768
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyUShort(GDALWarpKernel *poWK)
6769
0
{
6770
0
    return GWKRun(
6771
0
        poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyUShort",
6772
0
        GWKResampleNoMasksOrDstDensityOnlyThread<GUInt16, GRA_CubicSpline>);
6773
0
}
6774
6775
static CPLErr GWKNearestShort(GDALWarpKernel *poWK)
6776
0
{
6777
0
    return GWKRun(poWK, "GWKNearestShort", GWKNearestThread<GInt16>);
6778
0
}
6779
6780
static CPLErr GWKNearestUnsignedShort(GDALWarpKernel *poWK)
6781
0
{
6782
0
    return GWKRun(poWK, "GWKNearestUnsignedShort", GWKNearestThread<GUInt16>);
6783
0
}
6784
6785
static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat(GDALWarpKernel *poWK)
6786
0
{
6787
0
    return GWKRun(
6788
0
        poWK, "GWKNearestNoMasksOrDstDensityOnlyFloat",
6789
0
        GWKResampleNoMasksOrDstDensityOnlyThread<float, GRA_NearestNeighbour>);
6790
0
}
6791
6792
static CPLErr GWKNearestFloat(GDALWarpKernel *poWK)
6793
0
{
6794
0
    return GWKRun(poWK, "GWKNearestFloat", GWKNearestThread<float>);
6795
0
}
6796
6797
/************************************************************************/
6798
/*                           GWKAverageOrMode()                         */
6799
/*                                                                      */
6800
/************************************************************************/
6801
6802
#define COMPUTE_WEIGHT_Y(iSrcY)                                                \
6803
0
    ((iSrcY == iSrcYMin)                                                       \
6804
0
         ? ((iSrcYMin + 1 == iSrcYMax) ? 1.0 : 1 - (dfYMin - iSrcYMin))        \
6805
0
     : (iSrcY + 1 == iSrcYMax) ? 1 - (iSrcYMax - dfYMax)                       \
6806
0
                               : 1.0)
6807
6808
#define COMPUTE_WEIGHT(iSrcX, dfWeightY)                                       \
6809
0
    ((iSrcX == iSrcXMin)       ? ((iSrcXMin + 1 == iSrcXMax)                   \
6810
0
                                      ? dfWeightY                              \
6811
0
                                      : dfWeightY * (1 - (dfXMin - iSrcXMin))) \
6812
0
     : (iSrcX + 1 == iSrcXMax) ? dfWeightY * (1 - (iSrcXMax - dfXMax))         \
6813
0
                               : dfWeightY)
6814
6815
static void GWKAverageOrModeThread(void *pData);
6816
6817
static CPLErr GWKAverageOrMode(GDALWarpKernel *poWK)
6818
0
{
6819
0
    return GWKRun(poWK, "GWKAverageOrMode", GWKAverageOrModeThread);
6820
0
}
6821
6822
/************************************************************************/
6823
/*                 GWKAverageOrModeComputeLineCoords()                  */
6824
/************************************************************************/
6825
6826
static void GWKAverageOrModeComputeLineCoords(
6827
    const GWKJobStruct *psJob, double *padfX, double *padfX2, double *padfY,
6828
    double *padfY2, double *padfZ, double *padfZ2, int *pabSuccess,
6829
    int *pabSuccess2, int iDstY, double dfSrcCoordPrecision,
6830
    double dfErrorThreshold)
6831
0
{
6832
0
    const GDALWarpKernel *poWK = psJob->poWK;
6833
0
    const int nDstXSize = poWK->nDstXSize;
6834
6835
    // Setup points to transform to source image space.
6836
0
    for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
6837
0
    {
6838
0
        padfX[iDstX] = iDstX + poWK->nDstXOff;
6839
0
        padfY[iDstX] = iDstY + poWK->nDstYOff;
6840
0
        padfZ[iDstX] = 0.0;
6841
0
        padfX2[iDstX] = iDstX + 1.0 + poWK->nDstXOff;
6842
0
        padfY2[iDstX] = iDstY + 1.0 + poWK->nDstYOff;
6843
0
        padfZ2[iDstX] = 0.0;
6844
0
    }
6845
6846
    /* ----------------------------------------------------------------- */
6847
    /*      Transform the points from destination pixel/line coordinates */
6848
    /*      to source pixel/line coordinates.                            */
6849
    /* ----------------------------------------------------------------- */
6850
0
    poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX, padfY,
6851
0
                         padfZ, pabSuccess);
6852
0
    poWK->pfnTransformer(psJob->pTransformerArg, TRUE, nDstXSize, padfX2,
6853
0
                         padfY2, padfZ2, pabSuccess2);
6854
6855
0
    if (dfSrcCoordPrecision > 0.0)
6856
0
    {
6857
0
        GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess,
6858
0
                                  dfSrcCoordPrecision, dfErrorThreshold,
6859
0
                                  poWK->pfnTransformer, psJob->pTransformerArg,
6860
0
                                  poWK->nDstXOff, iDstY + poWK->nDstYOff);
6861
0
        GWKRoundSourceCoordinates(
6862
0
            nDstXSize, padfX2, padfY2, padfZ2, pabSuccess2, dfSrcCoordPrecision,
6863
0
            dfErrorThreshold, poWK->pfnTransformer, psJob->pTransformerArg,
6864
0
            1.0 + poWK->nDstXOff, iDstY + 1.0 + poWK->nDstYOff);
6865
0
    }
6866
0
}
6867
6868
/************************************************************************/
6869
/*                GWKAverageOrModeComputeSourceCoords()                 */
6870
/************************************************************************/
6871
6872
static bool GWKAverageOrModeComputeSourceCoords(
6873
    const GWKJobStruct *psJob, double *padfX, double *padfX2, double *padfY,
6874
    double *padfY2, int iDstX, int iDstY, int nXMargin, int nYMargin,
6875
    // Output:
6876
    bool &bWrapOverX, double &dfXMin, double &dfYMin, double &dfXMax,
6877
    double &dfYMax, int &iSrcXMin, int &iSrcYMin, int &iSrcXMax, int &iSrcYMax)
6878
0
{
6879
0
    const GDALWarpKernel *poWK = psJob->poWK;
6880
0
    const int nSrcXSize = poWK->nSrcXSize;
6881
0
    const int nSrcYSize = poWK->nSrcYSize;
6882
6883
    // Add some checks so that padfX[iDstX] - poWK->nSrcXOff is in
6884
    // reasonable range (https://github.com/OSGeo/gdal/issues/2365)
6885
0
    if (!(padfX[iDstX] - poWK->nSrcXOff >= -nXMargin &&
6886
0
          padfX2[iDstX] - poWK->nSrcXOff >= -nXMargin &&
6887
0
          padfY[iDstX] - poWK->nSrcYOff >= -nYMargin &&
6888
0
          padfY2[iDstX] - poWK->nSrcYOff >= -nYMargin &&
6889
0
          padfX[iDstX] - poWK->nSrcXOff - nSrcXSize <= nXMargin &&
6890
0
          padfX2[iDstX] - poWK->nSrcXOff - nSrcXSize <= nXMargin &&
6891
0
          padfY[iDstX] - poWK->nSrcYOff - nSrcYSize <= nYMargin &&
6892
0
          padfY2[iDstX] - poWK->nSrcYOff - nSrcYSize <= nYMargin))
6893
0
    {
6894
0
        return false;
6895
0
    }
6896
6897
    // Compute corners in source crs.
6898
6899
    // The transformation might not have preserved ordering of
6900
    // coordinates so do the necessary swapping (#5433).
6901
    // NOTE: this is really an approximative fix. To do something
6902
    // more precise we would for example need to compute the
6903
    // transformation of coordinates in the
6904
    // [iDstX,iDstY]x[iDstX+1,iDstY+1] square back to source
6905
    // coordinates, and take the bounding box of the got source
6906
    // coordinates.
6907
6908
0
    if (padfX[iDstX] > padfX2[iDstX])
6909
0
        std::swap(padfX[iDstX], padfX2[iDstX]);
6910
6911
    // Detect situations where the target pixel is close to the
6912
    // antimeridian and when padfX[iDstX] and padfX2[iDstX] are very
6913
    // close to the left-most and right-most columns of the source
6914
    // raster. The 2 value below was experimentally determined to
6915
    // avoid false-positives and false-negatives.
6916
    // Addresses https://github.com/OSGeo/gdal/issues/6478
6917
0
    bWrapOverX = false;
6918
0
    const int nThresholdWrapOverX = std::min(2, nSrcXSize / 10);
6919
0
    if (poWK->nSrcXOff == 0 && iDstX + 1 < poWK->nDstXSize &&
6920
0
        2 * padfX[iDstX] - padfX[iDstX + 1] < nThresholdWrapOverX &&
6921
0
        nSrcXSize - padfX2[iDstX] < nThresholdWrapOverX)
6922
0
    {
6923
        // Check there is a discontinuity by checking at mid-pixel.
6924
        // NOTE: all this remains fragile. To confidently
6925
        // detect antimeridian warping we should probably try to access
6926
        // georeferenced coordinates, and not rely only on tests on
6927
        // image space coordinates. But accessing georeferenced
6928
        // coordinates from here is not trivial, and we would for example
6929
        // have to handle both geographic, Mercator, etc.
6930
        // Let's hope this heuristics is good enough for now.
6931
0
        double x = iDstX + 0.5 + poWK->nDstXOff;
6932
0
        double y = iDstY + poWK->nDstYOff;
6933
0
        double z = 0;
6934
0
        int bSuccess = FALSE;
6935
0
        poWK->pfnTransformer(psJob->pTransformerArg, TRUE, 1, &x, &y, &z,
6936
0
                             &bSuccess);
6937
0
        if (bSuccess && x < padfX[iDstX])
6938
0
        {
6939
0
            bWrapOverX = true;
6940
0
            std::swap(padfX[iDstX], padfX2[iDstX]);
6941
0
            padfX2[iDstX] += nSrcXSize;
6942
0
        }
6943
0
    }
6944
6945
0
    dfXMin = padfX[iDstX] - poWK->nSrcXOff;
6946
0
    dfXMax = padfX2[iDstX] - poWK->nSrcXOff;
6947
0
    constexpr double EPSILON = 1e-10;
6948
    // Check that [dfXMin, dfXMax] intersect with [0,nSrcXSize] with a tolerance
6949
0
    if (!(dfXMax > -EPSILON && dfXMin < nSrcXSize + EPSILON))
6950
0
        return false;
6951
0
    iSrcXMin = static_cast<int>(std::max(floor(dfXMin + EPSILON), 0.0));
6952
0
    iSrcXMax = static_cast<int>(
6953
0
        std::min(ceil(dfXMax - EPSILON), static_cast<double>(INT_MAX)));
6954
0
    if (!bWrapOverX)
6955
0
        iSrcXMax = std::min(iSrcXMax, nSrcXSize);
6956
0
    if (iSrcXMin == iSrcXMax && iSrcXMax < nSrcXSize)
6957
0
        iSrcXMax++;
6958
6959
0
    if (padfY[iDstX] > padfY2[iDstX])
6960
0
        std::swap(padfY[iDstX], padfY2[iDstX]);
6961
0
    dfYMin = padfY[iDstX] - poWK->nSrcYOff;
6962
0
    dfYMax = padfY2[iDstX] - poWK->nSrcYOff;
6963
    // Check that [dfYMin, dfYMax] intersect with [0,nSrcYSize] with a tolerance
6964
0
    if (!(dfYMax > -EPSILON && dfYMin < nSrcYSize + EPSILON))
6965
0
        return false;
6966
0
    iSrcYMin = static_cast<int>(std::max(floor(dfYMin + EPSILON), 0.0));
6967
0
    iSrcYMax = std::min(static_cast<int>(ceil(dfYMax - EPSILON)), nSrcYSize);
6968
0
    if (iSrcYMin == iSrcYMax && iSrcYMax < nSrcYSize)
6969
0
        iSrcYMax++;
6970
6971
0
    return true;
6972
0
}
6973
6974
/************************************************************************/
6975
/*                          GWKModeRealType()                           */
6976
/************************************************************************/
6977
6978
template <class T> static inline bool IsSame(T a, T b)
6979
0
{
6980
0
    return a == b;
6981
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:bool IsSame<int>(int, int)
Unexecuted instantiation: gdalwarpkernel.cpp:bool IsSame<unsigned int>(unsigned int, unsigned int)
Unexecuted instantiation: gdalwarpkernel.cpp:bool IsSame<long>(long, long)
Unexecuted instantiation: gdalwarpkernel.cpp:bool IsSame<unsigned long>(unsigned long, unsigned long)
6982
6983
template <> bool IsSame<GFloat16>(GFloat16 a, GFloat16 b)
6984
0
{
6985
0
    return a == b || (CPLIsNan(a) && CPLIsNan(b));
6986
0
}
6987
6988
template <> bool IsSame<float>(float a, float b)
6989
0
{
6990
0
    return a == b || (std::isnan(a) && std::isnan(b));
6991
0
}
6992
6993
template <> bool IsSame<double>(double a, double b)
6994
0
{
6995
0
    return a == b || (std::isnan(a) && std::isnan(b));
6996
0
}
6997
6998
template <class T> static void GWKModeRealType(GWKJobStruct *psJob)
6999
0
{
7000
0
    const GDALWarpKernel *poWK = psJob->poWK;
7001
0
    const int iYMin = psJob->iYMin;
7002
0
    const int iYMax = psJob->iYMax;
7003
0
    const int nDstXSize = poWK->nDstXSize;
7004
0
    const int nSrcXSize = poWK->nSrcXSize;
7005
0
    const int nSrcYSize = poWK->nSrcYSize;
7006
0
    const GWKTieStrategy eTieStrategy = poWK->eTieStrategy;
7007
7008
0
    T *pVals = nullptr;
7009
0
    float *pafCounts = nullptr;
7010
7011
0
    if (nSrcXSize > 0 && nSrcYSize > 0)
7012
0
    {
7013
0
        pVals = static_cast<T *>(
7014
0
            VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(T)));
7015
0
        pafCounts = static_cast<float *>(
7016
0
            VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float)));
7017
0
        if (pVals == nullptr || pafCounts == nullptr)
7018
0
        {
7019
0
            VSIFree(pVals);
7020
0
            VSIFree(pafCounts);
7021
0
            return;
7022
0
        }
7023
0
    }
7024
7025
    /* -------------------------------------------------------------------- */
7026
    /*      Allocate x,y,z coordinate arrays for transformation ... two     */
7027
    /*      scanlines worth of positions.                                   */
7028
    /* -------------------------------------------------------------------- */
7029
7030
0
    double *padfX =
7031
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7032
0
    double *padfY =
7033
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7034
0
    double *padfZ =
7035
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7036
0
    double *padfX2 =
7037
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7038
0
    double *padfY2 =
7039
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7040
0
    double *padfZ2 =
7041
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7042
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7043
0
    int *pabSuccess2 = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7044
7045
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
7046
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
7047
0
    const double dfErrorThreshold = CPLAtof(
7048
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
7049
0
    const bool bAvoidNoDataSingleBand =
7050
0
        poWK->nBands == 1 ||
7051
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
7052
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
7053
7054
0
    const int nXMargin =
7055
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfXScale)));
7056
0
    const int nYMargin =
7057
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfYScale)));
7058
7059
    /* ==================================================================== */
7060
    /*      Loop over output lines.                                         */
7061
    /* ==================================================================== */
7062
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
7063
0
    {
7064
0
        GWKAverageOrModeComputeLineCoords(
7065
0
            psJob, padfX, padfX2, padfY, padfY2, padfZ, padfZ2, pabSuccess,
7066
0
            pabSuccess2, iDstY, dfSrcCoordPrecision, dfErrorThreshold);
7067
7068
        // Loop over pixels in output scanline.
7069
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
7070
0
        {
7071
0
            GPtrDiff_t iSrcOffset = 0;
7072
0
            double dfDensity = 1.0;
7073
0
            bool bHasFoundDensity = false;
7074
7075
0
            bool bWrapOverX = false;
7076
0
            double dfXMin = 0;
7077
0
            double dfYMin = 0;
7078
0
            double dfXMax = 0;
7079
0
            double dfYMax = 0;
7080
0
            int iSrcXMin = 0;
7081
0
            int iSrcYMin = 0;
7082
0
            int iSrcXMax = 0;
7083
0
            int iSrcYMax = 0;
7084
0
            if (!GWKAverageOrModeComputeSourceCoords(
7085
0
                    psJob, padfX, padfX2, padfY, padfY2, iDstX, iDstY, nXMargin,
7086
0
                    nYMargin, bWrapOverX, dfXMin, dfYMin, dfXMax, dfYMax,
7087
0
                    iSrcXMin, iSrcYMin, iSrcXMax, iSrcYMax))
7088
0
            {
7089
0
                continue;
7090
0
            }
7091
7092
0
            const GPtrDiff_t iDstOffset =
7093
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
7094
7095
            // Loop processing each band.
7096
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
7097
0
            {
7098
0
                double dfBandDensity = 0.0;
7099
7100
0
                int nBins = 0;
7101
0
                int iModeIndex = -1;
7102
0
                T nVal{};
7103
7104
0
                for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
7105
0
                {
7106
0
                    const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
7107
0
                    iSrcOffset =
7108
0
                        iSrcXMin + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7109
0
                    for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
7110
0
                         iSrcX++, iSrcOffset++)
7111
0
                    {
7112
0
                        if (bWrapOverX)
7113
0
                            iSrcOffset =
7114
0
                                (iSrcX % nSrcXSize) +
7115
0
                                static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7116
7117
0
                        if (poWK->panUnifiedSrcValid != nullptr &&
7118
0
                            !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
7119
0
                            continue;
7120
7121
0
                        if (GWKGetPixelT(poWK, iBand, iSrcOffset,
7122
0
                                         &dfBandDensity, &nVal) &&
7123
0
                            dfBandDensity > BAND_DENSITY_THRESHOLD)
7124
0
                        {
7125
0
                            const double dfWeight =
7126
0
                                COMPUTE_WEIGHT(iSrcX, dfWeightY);
7127
7128
                            // Check array for existing entry.
7129
0
                            int i = 0;
7130
0
                            for (i = 0; i < nBins; ++i)
7131
0
                            {
7132
0
                                if (IsSame(pVals[i], nVal))
7133
0
                                {
7134
7135
0
                                    pafCounts[i] +=
7136
0
                                        static_cast<float>(dfWeight);
7137
0
                                    bool bValIsMaxCount =
7138
0
                                        (pafCounts[i] > pafCounts[iModeIndex]);
7139
7140
0
                                    if (!bValIsMaxCount &&
7141
0
                                        pafCounts[i] == pafCounts[iModeIndex])
7142
0
                                    {
7143
0
                                        switch (eTieStrategy)
7144
0
                                        {
7145
0
                                            case GWKTS_First:
7146
0
                                                break;
7147
0
                                            case GWKTS_Min:
7148
0
                                                bValIsMaxCount =
7149
0
                                                    nVal < pVals[iModeIndex];
7150
0
                                                break;
7151
0
                                            case GWKTS_Max:
7152
0
                                                bValIsMaxCount =
7153
0
                                                    nVal > pVals[iModeIndex];
7154
0
                                                break;
7155
0
                                        }
7156
0
                                    }
7157
7158
0
                                    if (bValIsMaxCount)
7159
0
                                    {
7160
0
                                        iModeIndex = i;
7161
0
                                    }
7162
7163
0
                                    break;
7164
0
                                }
7165
0
                            }
7166
7167
                            // Add to arr if entry not already there.
7168
0
                            if (i == nBins)
7169
0
                            {
7170
0
                                pVals[i] = nVal;
7171
0
                                pafCounts[i] = static_cast<float>(dfWeight);
7172
7173
0
                                if (iModeIndex < 0)
7174
0
                                    iModeIndex = i;
7175
7176
0
                                ++nBins;
7177
0
                            }
7178
0
                        }
7179
0
                    }
7180
0
                }
7181
7182
0
                if (iModeIndex != -1)
7183
0
                {
7184
0
                    nVal = pVals[iModeIndex];
7185
0
                    dfBandDensity = 1;
7186
0
                    bHasFoundDensity = true;
7187
0
                }
7188
7189
                // We have a computed value from the source.  Now apply it
7190
                // to the destination pixel
7191
0
                if (bHasFoundDensity)
7192
0
                {
7193
0
                    GWKSetPixelValueRealT(poWK, iBand, iDstOffset,
7194
0
                                          dfBandDensity, nVal,
7195
0
                                          bAvoidNoDataSingleBand);
7196
0
                }
7197
0
            }
7198
7199
0
            if (!bHasFoundDensity)
7200
0
                continue;
7201
7202
0
            if (!bAvoidNoDataSingleBand)
7203
0
            {
7204
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
7205
0
            }
7206
7207
            /* --------------------------------------------------------------------
7208
             */
7209
            /*      Update destination density/validity masks. */
7210
            /* --------------------------------------------------------------------
7211
             */
7212
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
7213
7214
0
            if (poWK->panDstValid != nullptr)
7215
0
            {
7216
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
7217
0
            }
7218
0
        } /* Next iDstX */
7219
7220
        /* --------------------------------------------------------------------
7221
         */
7222
        /*      Report progress to the user, and optionally cancel out. */
7223
        /* --------------------------------------------------------------------
7224
         */
7225
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
7226
0
            break;
7227
0
    }
7228
7229
    /* -------------------------------------------------------------------- */
7230
    /*      Cleanup and return.                                             */
7231
    /* -------------------------------------------------------------------- */
7232
0
    CPLFree(padfX);
7233
0
    CPLFree(padfY);
7234
0
    CPLFree(padfZ);
7235
0
    CPLFree(padfX2);
7236
0
    CPLFree(padfY2);
7237
0
    CPLFree(padfZ2);
7238
0
    CPLFree(pabSuccess);
7239
0
    CPLFree(pabSuccess2);
7240
0
    VSIFree(pVals);
7241
0
    VSIFree(pafCounts);
7242
0
}
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<int>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<unsigned int>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<long>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<unsigned long>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<cpl::Float16>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<float>(GWKJobStruct*)
Unexecuted instantiation: gdalwarpkernel.cpp:void GWKModeRealType<double>(GWKJobStruct*)
7243
7244
/************************************************************************/
7245
/*                         GWKModeComplexType()                         */
7246
/************************************************************************/
7247
7248
static void GWKModeComplexType(GWKJobStruct *psJob)
7249
0
{
7250
0
    const GDALWarpKernel *poWK = psJob->poWK;
7251
0
    const int iYMin = psJob->iYMin;
7252
0
    const int iYMax = psJob->iYMax;
7253
0
    const int nDstXSize = poWK->nDstXSize;
7254
0
    const int nSrcXSize = poWK->nSrcXSize;
7255
0
    const int nSrcYSize = poWK->nSrcYSize;
7256
0
    const GWKTieStrategy eTieStrategy = poWK->eTieStrategy;
7257
0
    const double dfMultFactorVerticalShiftPipeline =
7258
0
        poWK->bApplyVerticalShift
7259
0
            ? CPLAtof(CSLFetchNameValueDef(
7260
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
7261
0
                  "1.0"))
7262
0
            : 0.0;
7263
0
    const bool bAvoidNoDataSingleBand =
7264
0
        poWK->nBands == 1 ||
7265
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
7266
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
7267
7268
0
    double *padfRealVals = nullptr;
7269
0
    double *padfImagVals = nullptr;
7270
0
    float *pafCounts = nullptr;
7271
7272
0
    if (nSrcXSize > 0 && nSrcYSize > 0)
7273
0
    {
7274
0
        padfRealVals = static_cast<double *>(
7275
0
            VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(double)));
7276
0
        padfImagVals = static_cast<double *>(
7277
0
            VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(double)));
7278
0
        pafCounts = static_cast<float *>(
7279
0
            VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float)));
7280
0
        if (padfRealVals == nullptr || padfImagVals == nullptr ||
7281
0
            pafCounts == nullptr)
7282
0
        {
7283
0
            VSIFree(padfRealVals);
7284
0
            VSIFree(padfImagVals);
7285
0
            VSIFree(pafCounts);
7286
0
            return;
7287
0
        }
7288
0
    }
7289
7290
    /* -------------------------------------------------------------------- */
7291
    /*      Allocate x,y,z coordinate arrays for transformation ... two     */
7292
    /*      scanlines worth of positions.                                   */
7293
    /* -------------------------------------------------------------------- */
7294
7295
0
    double *padfX =
7296
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7297
0
    double *padfY =
7298
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7299
0
    double *padfZ =
7300
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7301
0
    double *padfX2 =
7302
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7303
0
    double *padfY2 =
7304
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7305
0
    double *padfZ2 =
7306
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7307
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7308
0
    int *pabSuccess2 = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7309
7310
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
7311
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
7312
0
    const double dfErrorThreshold = CPLAtof(
7313
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
7314
7315
0
    const int nXMargin =
7316
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfXScale)));
7317
0
    const int nYMargin =
7318
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfYScale)));
7319
7320
    /* ==================================================================== */
7321
    /*      Loop over output lines.                                         */
7322
    /* ==================================================================== */
7323
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
7324
0
    {
7325
0
        GWKAverageOrModeComputeLineCoords(
7326
0
            psJob, padfX, padfX2, padfY, padfY2, padfZ, padfZ2, pabSuccess,
7327
0
            pabSuccess2, iDstY, dfSrcCoordPrecision, dfErrorThreshold);
7328
7329
        // Loop over pixels in output scanline.
7330
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
7331
0
        {
7332
0
            GPtrDiff_t iSrcOffset = 0;
7333
0
            double dfDensity = 1.0;
7334
0
            bool bHasFoundDensity = false;
7335
7336
0
            bool bWrapOverX = false;
7337
0
            double dfXMin = 0;
7338
0
            double dfYMin = 0;
7339
0
            double dfXMax = 0;
7340
0
            double dfYMax = 0;
7341
0
            int iSrcXMin = 0;
7342
0
            int iSrcYMin = 0;
7343
0
            int iSrcXMax = 0;
7344
0
            int iSrcYMax = 0;
7345
0
            if (!GWKAverageOrModeComputeSourceCoords(
7346
0
                    psJob, padfX, padfX2, padfY, padfY2, iDstX, iDstY, nXMargin,
7347
0
                    nYMargin, bWrapOverX, dfXMin, dfYMin, dfXMax, dfYMax,
7348
0
                    iSrcXMin, iSrcYMin, iSrcXMax, iSrcYMax))
7349
0
            {
7350
0
                continue;
7351
0
            }
7352
7353
0
            const GPtrDiff_t iDstOffset =
7354
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
7355
7356
            // Loop processing each band.
7357
0
            for (int iBand = 0; iBand < poWK->nBands; iBand++)
7358
0
            {
7359
0
                double dfBandDensity = 0.0;
7360
7361
0
                int nBins = 0;
7362
0
                int iModeIndex = -1;
7363
0
                double dfValueReal = 0;
7364
0
                double dfValueImag = 0;
7365
7366
0
                for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
7367
0
                {
7368
0
                    const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
7369
0
                    iSrcOffset =
7370
0
                        iSrcXMin + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7371
0
                    for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
7372
0
                         iSrcX++, iSrcOffset++)
7373
0
                    {
7374
0
                        if (bWrapOverX)
7375
0
                            iSrcOffset =
7376
0
                                (iSrcX % nSrcXSize) +
7377
0
                                static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7378
7379
0
                        if (poWK->panUnifiedSrcValid != nullptr &&
7380
0
                            !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
7381
0
                            continue;
7382
7383
0
                        if (GWKGetPixelValue(poWK, iBand, iSrcOffset,
7384
0
                                             &dfBandDensity, &dfValueReal,
7385
0
                                             &dfValueImag) &&
7386
0
                            dfBandDensity > BAND_DENSITY_THRESHOLD)
7387
0
                        {
7388
0
                            const double dfWeight =
7389
0
                                COMPUTE_WEIGHT(iSrcX, dfWeightY);
7390
7391
                            // Check array for existing entry.
7392
0
                            int i = 0;
7393
0
                            for (i = 0; i < nBins; ++i)
7394
0
                            {
7395
0
                                if (IsSame(padfRealVals[i], dfValueReal) &&
7396
0
                                    IsSame(padfImagVals[i], dfValueImag))
7397
0
                                {
7398
7399
0
                                    pafCounts[i] +=
7400
0
                                        static_cast<float>(dfWeight);
7401
0
                                    bool bValIsMaxCount =
7402
0
                                        (pafCounts[i] > pafCounts[iModeIndex]);
7403
7404
0
                                    if (!bValIsMaxCount &&
7405
0
                                        pafCounts[i] == pafCounts[iModeIndex])
7406
0
                                    {
7407
0
                                        switch (eTieStrategy)
7408
0
                                        {
7409
0
                                            case GWKTS_First:
7410
0
                                                break;
7411
0
                                            case GWKTS_Min:
7412
0
                                                bValIsMaxCount =
7413
0
                                                    dfValueReal <
7414
0
                                                    padfRealVals[iModeIndex];
7415
0
                                                break;
7416
0
                                            case GWKTS_Max:
7417
0
                                                bValIsMaxCount =
7418
0
                                                    dfValueReal >
7419
0
                                                    padfRealVals[iModeIndex];
7420
0
                                                break;
7421
0
                                        }
7422
0
                                    }
7423
7424
0
                                    if (bValIsMaxCount)
7425
0
                                    {
7426
0
                                        iModeIndex = i;
7427
0
                                    }
7428
7429
0
                                    break;
7430
0
                                }
7431
0
                            }
7432
7433
                            // Add to arr if entry not already there.
7434
0
                            if (i == nBins)
7435
0
                            {
7436
0
                                padfRealVals[i] = dfValueReal;
7437
0
                                padfImagVals[i] = dfValueImag;
7438
0
                                pafCounts[i] = static_cast<float>(dfWeight);
7439
7440
0
                                if (iModeIndex < 0)
7441
0
                                    iModeIndex = i;
7442
7443
0
                                ++nBins;
7444
0
                            }
7445
0
                        }
7446
0
                    }
7447
0
                }
7448
7449
0
                if (iModeIndex != -1)
7450
0
                {
7451
0
                    dfValueReal = padfRealVals[iModeIndex];
7452
0
                    dfValueImag = padfImagVals[iModeIndex];
7453
0
                    dfBandDensity = 1;
7454
7455
0
                    if (poWK->bApplyVerticalShift)
7456
0
                    {
7457
0
                        if (!std::isfinite(padfZ[iDstX]))
7458
0
                            continue;
7459
                        // Subtract padfZ[] since the coordinate
7460
                        // transformation is from target to source
7461
0
                        dfValueReal =
7462
0
                            dfValueReal * poWK->dfMultFactorVerticalShift -
7463
0
                            padfZ[iDstX] * dfMultFactorVerticalShiftPipeline;
7464
0
                    }
7465
7466
0
                    bHasFoundDensity = true;
7467
0
                }
7468
7469
                // We have a computed value from the source.  Now apply it
7470
                // to the destination pixel
7471
0
                if (bHasFoundDensity)
7472
0
                {
7473
0
                    GWKSetPixelValue(poWK, iBand, iDstOffset, dfBandDensity,
7474
0
                                     dfValueReal, dfValueImag,
7475
0
                                     bAvoidNoDataSingleBand);
7476
0
                }
7477
0
            }
7478
7479
0
            if (!bHasFoundDensity)
7480
0
                continue;
7481
7482
0
            if (!bAvoidNoDataSingleBand)
7483
0
            {
7484
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
7485
0
            }
7486
7487
            /* --------------------------------------------------------------------
7488
             */
7489
            /*      Update destination density/validity masks. */
7490
            /* --------------------------------------------------------------------
7491
             */
7492
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
7493
7494
0
            if (poWK->panDstValid != nullptr)
7495
0
            {
7496
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
7497
0
            }
7498
0
        } /* Next iDstX */
7499
7500
        /* --------------------------------------------------------------------
7501
         */
7502
        /*      Report progress to the user, and optionally cancel out. */
7503
        /* --------------------------------------------------------------------
7504
         */
7505
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
7506
0
            break;
7507
0
    }
7508
7509
    /* -------------------------------------------------------------------- */
7510
    /*      Cleanup and return.                                             */
7511
    /* -------------------------------------------------------------------- */
7512
0
    CPLFree(padfX);
7513
0
    CPLFree(padfY);
7514
0
    CPLFree(padfZ);
7515
0
    CPLFree(padfX2);
7516
0
    CPLFree(padfY2);
7517
0
    CPLFree(padfZ2);
7518
0
    CPLFree(pabSuccess);
7519
0
    CPLFree(pabSuccess2);
7520
0
    VSIFree(padfRealVals);
7521
0
    VSIFree(padfImagVals);
7522
0
    VSIFree(pafCounts);
7523
0
}
7524
7525
/************************************************************************/
7526
/*                       GWKAverageOrModeThread()                       */
7527
/************************************************************************/
7528
7529
// Overall logic based on GWKGeneralCaseThread().
7530
static void GWKAverageOrModeThread(void *pData)
7531
0
{
7532
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
7533
0
    const GDALWarpKernel *poWK = psJob->poWK;
7534
0
    const int iYMin = psJob->iYMin;
7535
0
    const int iYMax = psJob->iYMax;
7536
0
    const double dfMultFactorVerticalShiftPipeline =
7537
0
        poWK->bApplyVerticalShift
7538
0
            ? CPLAtof(CSLFetchNameValueDef(
7539
0
                  poWK->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT_PIPELINE",
7540
0
                  "1.0"))
7541
0
            : 0.0;
7542
0
    const bool bAvoidNoDataSingleBand =
7543
0
        poWK->nBands == 1 ||
7544
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
7545
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
7546
7547
0
    const int nDstXSize = poWK->nDstXSize;
7548
0
    const int nSrcXSize = poWK->nSrcXSize;
7549
7550
    /* -------------------------------------------------------------------- */
7551
    /*      Find out which algorithm to use (small optim.)                  */
7552
    /* -------------------------------------------------------------------- */
7553
7554
    // Only used for GRA_Mode
7555
0
    float *pafCounts = nullptr;
7556
0
    int nBins = 0;
7557
0
    int nBinsOffset = 0;
7558
0
    const GWKTieStrategy eTieStrategy = poWK->eTieStrategy;
7559
7560
    // Only used with Q1, Med and Q3
7561
0
    float quant = 0.0f;
7562
7563
    // To control array allocation only when data type is complex
7564
0
    const bool bIsComplex = GDALDataTypeIsComplex(poWK->eWorkingDataType) != 0;
7565
7566
0
    if (poWK->eResample == GRA_Mode)
7567
0
    {
7568
0
        if (poWK->bApplyVerticalShift)
7569
0
        {
7570
0
            return GWKModeComplexType(psJob);
7571
0
        }
7572
7573
0
        switch (poWK->eWorkingDataType)
7574
0
        {
7575
0
            case GDT_UInt8:
7576
0
                nBins = 256;
7577
0
                break;
7578
7579
0
            case GDT_Int8:
7580
0
                nBins = 256;
7581
0
                nBinsOffset = nBins / 2;
7582
0
                break;
7583
7584
0
            case GDT_UInt16:
7585
0
                nBins = 65536;
7586
0
                break;
7587
7588
0
            case GDT_Int16:
7589
0
                nBins = 65536;
7590
0
                nBinsOffset = nBins / 2;
7591
0
                break;
7592
7593
0
            case GDT_Int32:
7594
0
                return GWKModeRealType<int32_t>(psJob);
7595
7596
0
            case GDT_UInt32:
7597
0
                return GWKModeRealType<uint32_t>(psJob);
7598
7599
0
            case GDT_Int64:
7600
0
                return GWKModeRealType<int64_t>(psJob);
7601
7602
0
            case GDT_UInt64:
7603
0
                return GWKModeRealType<uint64_t>(psJob);
7604
7605
0
            case GDT_Float16:
7606
0
                return GWKModeRealType<GFloat16>(psJob);
7607
7608
0
            case GDT_Float32:
7609
0
                return GWKModeRealType<float>(psJob);
7610
7611
0
            case GDT_Float64:
7612
0
                return GWKModeRealType<double>(psJob);
7613
7614
0
            case GDT_CInt16:
7615
0
            case GDT_CInt32:
7616
0
            case GDT_CFloat16:
7617
0
            case GDT_CFloat32:
7618
0
            case GDT_CFloat64:
7619
0
                return GWKModeComplexType(psJob);
7620
7621
0
            case GDT_Unknown:
7622
0
            case GDT_TypeCount:
7623
0
                CPLAssert(false);
7624
0
                return;
7625
0
        }
7626
7627
0
        if (nBins)
7628
0
        {
7629
0
            pafCounts =
7630
0
                static_cast<float *>(VSI_MALLOC_VERBOSE(nBins * sizeof(float)));
7631
0
            if (pafCounts == nullptr)
7632
0
                return;
7633
0
        }
7634
0
    }
7635
0
    else if (poWK->eResample == GRA_Med)
7636
0
    {
7637
0
        quant = 0.5f;
7638
0
    }
7639
0
    else if (poWK->eResample == GRA_Q1)
7640
0
    {
7641
0
        quant = 0.25f;
7642
0
    }
7643
0
    else if (poWK->eResample == GRA_Q3)
7644
0
    {
7645
0
        quant = 0.75f;
7646
0
    }
7647
0
    else if (poWK->eResample != GRA_Average && poWK->eResample != GRA_RMS &&
7648
0
             poWK->eResample != GRA_Min && poWK->eResample != GRA_Max)
7649
0
    {
7650
        // Other resample algorithms not permitted here.
7651
0
        CPLError(CE_Fatal, CPLE_AppDefined,
7652
0
                 "GDALWarpKernel():GWKAverageOrModeThread() ERROR, "
7653
0
                 "illegal resample");
7654
0
    }
7655
7656
0
    CPLDebug("GDAL", "GDALWarpKernel():GWKAverageOrModeThread()");
7657
7658
    /* -------------------------------------------------------------------- */
7659
    /*      Allocate x,y,z coordinate arrays for transformation ... two     */
7660
    /*      scanlines worth of positions.                                   */
7661
    /* -------------------------------------------------------------------- */
7662
7663
0
    double *padfX =
7664
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7665
0
    double *padfY =
7666
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7667
0
    double *padfZ =
7668
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7669
0
    double *padfX2 =
7670
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7671
0
    double *padfY2 =
7672
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7673
0
    double *padfZ2 =
7674
0
        static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
7675
0
    int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7676
0
    int *pabSuccess2 = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
7677
7678
0
    const double dfSrcCoordPrecision = CPLAtof(CSLFetchNameValueDef(
7679
0
        poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0"));
7680
0
    const double dfErrorThreshold = CPLAtof(
7681
0
        CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0"));
7682
7683
0
    const double dfExcludedValuesThreshold =
7684
0
        CPLAtof(CSLFetchNameValueDef(poWK->papszWarpOptions,
7685
0
                                     "EXCLUDED_VALUES_PCT_THRESHOLD", "50")) /
7686
0
        100.0;
7687
0
    const double dfNodataValuesThreshold =
7688
0
        CPLAtof(CSLFetchNameValueDef(poWK->papszWarpOptions,
7689
0
                                     "NODATA_VALUES_PCT_THRESHOLD", "100")) /
7690
0
        100.0;
7691
7692
0
    const int nXMargin =
7693
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfXScale)));
7694
0
    const int nYMargin =
7695
0
        2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfYScale)));
7696
7697
    /* ==================================================================== */
7698
    /*      Loop over output lines.                                         */
7699
    /* ==================================================================== */
7700
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
7701
0
    {
7702
0
        GWKAverageOrModeComputeLineCoords(
7703
0
            psJob, padfX, padfX2, padfY, padfY2, padfZ, padfZ2, pabSuccess,
7704
0
            pabSuccess2, iDstY, dfSrcCoordPrecision, dfErrorThreshold);
7705
7706
        /* ====================================================================
7707
         */
7708
        /*      Loop over pixels in output scanline. */
7709
        /* ====================================================================
7710
         */
7711
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
7712
0
        {
7713
0
            GPtrDiff_t iSrcOffset = 0;
7714
0
            double dfDensity = 1.0;
7715
0
            bool bHasFoundDensity = false;
7716
7717
0
            bool bWrapOverX = false;
7718
0
            double dfXMin = 0;
7719
0
            double dfYMin = 0;
7720
0
            double dfXMax = 0;
7721
0
            double dfYMax = 0;
7722
0
            int iSrcXMin = 0;
7723
0
            int iSrcYMin = 0;
7724
0
            int iSrcXMax = 0;
7725
0
            int iSrcYMax = 0;
7726
0
            if (!GWKAverageOrModeComputeSourceCoords(
7727
0
                    psJob, padfX, padfX2, padfY, padfY2, iDstX, iDstY, nXMargin,
7728
0
                    nYMargin, bWrapOverX, dfXMin, dfYMin, dfXMax, dfYMax,
7729
0
                    iSrcXMin, iSrcYMin, iSrcXMax, iSrcYMax))
7730
0
            {
7731
0
                continue;
7732
0
            }
7733
7734
0
            const GPtrDiff_t iDstOffset =
7735
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
7736
7737
0
            bool bDone = false;
7738
7739
            // Special Average mode where we process all bands together,
7740
            // to avoid averaging tuples that match an entry of m_aadfExcludedValues
7741
0
            constexpr double EPSILON = 1e-10;
7742
0
            if (poWK->eResample == GRA_Average &&
7743
0
                (!poWK->m_aadfExcludedValues.empty() ||
7744
0
                 dfNodataValuesThreshold < 1 - EPSILON) &&
7745
0
                !poWK->bApplyVerticalShift && !bIsComplex)
7746
0
            {
7747
0
                double dfTotalWeightInvalid = 0.0;
7748
0
                double dfTotalWeightExcluded = 0.0;
7749
0
                double dfTotalWeightRegular = 0.0;
7750
0
                std::vector<double> adfValueReal(poWK->nBands, 0);
7751
0
                std::vector<double> adfValueAveraged(poWK->nBands, 0);
7752
0
                std::vector<int> anCountExcludedValues(
7753
0
                    poWK->m_aadfExcludedValues.size(), 0);
7754
7755
0
                for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
7756
0
                {
7757
0
                    const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
7758
0
                    iSrcOffset =
7759
0
                        iSrcXMin + static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7760
0
                    for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
7761
0
                         iSrcX++, iSrcOffset++)
7762
0
                    {
7763
0
                        if (bWrapOverX)
7764
0
                            iSrcOffset =
7765
0
                                (iSrcX % nSrcXSize) +
7766
0
                                static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7767
7768
0
                        const double dfWeight =
7769
0
                            COMPUTE_WEIGHT(iSrcX, dfWeightY);
7770
0
                        if (dfWeight <= 0)
7771
0
                            continue;
7772
7773
0
                        if (poWK->panUnifiedSrcValid != nullptr &&
7774
0
                            !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
7775
0
                        {
7776
0
                            dfTotalWeightInvalid += dfWeight;
7777
0
                            continue;
7778
0
                        }
7779
7780
0
                        bool bAllValid = true;
7781
0
                        for (int iBand = 0; iBand < poWK->nBands; iBand++)
7782
0
                        {
7783
0
                            double dfBandDensity = 0;
7784
0
                            double dfValueImagTmp = 0;
7785
0
                            if (!(GWKGetPixelValue(
7786
0
                                      poWK, iBand, iSrcOffset, &dfBandDensity,
7787
0
                                      &adfValueReal[iBand], &dfValueImagTmp) &&
7788
0
                                  dfBandDensity > BAND_DENSITY_THRESHOLD))
7789
0
                            {
7790
0
                                bAllValid = false;
7791
0
                                break;
7792
0
                            }
7793
0
                        }
7794
7795
0
                        if (!bAllValid)
7796
0
                        {
7797
0
                            dfTotalWeightInvalid += dfWeight;
7798
0
                            continue;
7799
0
                        }
7800
7801
0
                        bool bExcludedValueFound = false;
7802
0
                        for (size_t i = 0;
7803
0
                             i < poWK->m_aadfExcludedValues.size(); ++i)
7804
0
                        {
7805
0
                            if (poWK->m_aadfExcludedValues[i] == adfValueReal)
7806
0
                            {
7807
0
                                bExcludedValueFound = true;
7808
0
                                ++anCountExcludedValues[i];
7809
0
                                dfTotalWeightExcluded += dfWeight;
7810
0
                                break;
7811
0
                            }
7812
0
                        }
7813
0
                        if (!bExcludedValueFound)
7814
0
                        {
7815
                            // Weighted incremental algorithm mean
7816
                            // Cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Weighted_incremental_algorithm
7817
0
                            dfTotalWeightRegular += dfWeight;
7818
0
                            for (int iBand = 0; iBand < poWK->nBands; iBand++)
7819
0
                            {
7820
0
                                adfValueAveraged[iBand] +=
7821
0
                                    (dfWeight / dfTotalWeightRegular) *
7822
0
                                    (adfValueReal[iBand] -
7823
0
                                     adfValueAveraged[iBand]);
7824
0
                            }
7825
0
                        }
7826
0
                    }
7827
0
                }
7828
7829
0
                const double dfTotalWeight = dfTotalWeightInvalid +
7830
0
                                             dfTotalWeightExcluded +
7831
0
                                             dfTotalWeightRegular;
7832
0
                if (dfTotalWeightInvalid > 0 &&
7833
0
                    dfTotalWeightInvalid >=
7834
0
                        dfNodataValuesThreshold * dfTotalWeight)
7835
0
                {
7836
                    // Do nothing. Let bHasFoundDensity to false.
7837
0
                }
7838
0
                else if (dfTotalWeightExcluded > 0 &&
7839
0
                         dfTotalWeightExcluded >=
7840
0
                             dfExcludedValuesThreshold * dfTotalWeight)
7841
0
                {
7842
                    // Find the most represented excluded value tuple
7843
0
                    size_t iExcludedValue = 0;
7844
0
                    int nExcludedValueCount = 0;
7845
0
                    for (size_t i = 0; i < poWK->m_aadfExcludedValues.size();
7846
0
                         ++i)
7847
0
                    {
7848
0
                        if (anCountExcludedValues[i] > nExcludedValueCount)
7849
0
                        {
7850
0
                            iExcludedValue = i;
7851
0
                            nExcludedValueCount = anCountExcludedValues[i];
7852
0
                        }
7853
0
                    }
7854
7855
0
                    bHasFoundDensity = true;
7856
7857
0
                    for (int iBand = 0; iBand < poWK->nBands; iBand++)
7858
0
                    {
7859
0
                        GWKSetPixelValue(
7860
0
                            poWK, iBand, iDstOffset, /* dfBandDensity = */ 1.0,
7861
0
                            poWK->m_aadfExcludedValues[iExcludedValue][iBand],
7862
0
                            0, bAvoidNoDataSingleBand);
7863
0
                    }
7864
7865
0
                    if (!bAvoidNoDataSingleBand)
7866
0
                    {
7867
0
                        GWKAvoidNoDataMultiBand(poWK, iDstOffset);
7868
0
                    }
7869
0
                }
7870
0
                else if (dfTotalWeightRegular > 0)
7871
0
                {
7872
0
                    bHasFoundDensity = true;
7873
7874
0
                    for (int iBand = 0; iBand < poWK->nBands; iBand++)
7875
0
                    {
7876
0
                        GWKSetPixelValue(poWK, iBand, iDstOffset,
7877
0
                                         /* dfBandDensity = */ 1.0,
7878
0
                                         adfValueAveraged[iBand], 0,
7879
0
                                         bAvoidNoDataSingleBand);
7880
0
                    }
7881
7882
0
                    if (!bAvoidNoDataSingleBand)
7883
0
                    {
7884
0
                        GWKAvoidNoDataMultiBand(poWK, iDstOffset);
7885
0
                    }
7886
0
                }
7887
7888
                // Skip below loop on bands
7889
0
                bDone = true;
7890
0
            }
7891
7892
            /* ====================================================================
7893
             */
7894
            /*      Loop processing each band. */
7895
            /* ====================================================================
7896
             */
7897
7898
0
            for (int iBand = 0; !bDone && iBand < poWK->nBands; iBand++)
7899
0
            {
7900
0
                double dfBandDensity = 0.0;
7901
0
                double dfValueReal = 0.0;
7902
0
                double dfValueImag = 0.0;
7903
0
                double dfValueRealTmp = 0.0;
7904
0
                double dfValueImagTmp = 0.0;
7905
7906
                /* --------------------------------------------------------------------
7907
                 */
7908
                /*      Collect the source value. */
7909
                /* --------------------------------------------------------------------
7910
                 */
7911
7912
                // Loop over source lines and pixels - 3 possible algorithms.
7913
7914
0
                if (poWK->eResample == GRA_Average)
7915
0
                {
7916
0
                    double dfTotalWeight = 0.0;
7917
7918
                    // This code adapted from GDALDownsampleChunk32R_AverageT()
7919
                    // in gcore/overview.cpp.
7920
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
7921
0
                    {
7922
0
                        const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
7923
0
                        iSrcOffset = iSrcXMin +
7924
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7925
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
7926
0
                             iSrcX++, iSrcOffset++)
7927
0
                        {
7928
0
                            if (bWrapOverX)
7929
0
                                iSrcOffset =
7930
0
                                    (iSrcX % nSrcXSize) +
7931
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7932
7933
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
7934
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
7935
0
                                            iSrcOffset))
7936
0
                            {
7937
0
                                continue;
7938
0
                            }
7939
7940
0
                            if (GWKGetPixelValue(
7941
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
7942
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
7943
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
7944
0
                            {
7945
0
                                const double dfWeight =
7946
0
                                    COMPUTE_WEIGHT(iSrcX, dfWeightY);
7947
0
                                if (dfWeight > 0)
7948
0
                                {
7949
                                    // Weighted incremental algorithm mean
7950
                                    // Cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Weighted_incremental_algorithm
7951
0
                                    dfTotalWeight += dfWeight;
7952
0
                                    dfValueReal +=
7953
0
                                        (dfWeight / dfTotalWeight) *
7954
0
                                        (dfValueRealTmp - dfValueReal);
7955
0
                                    if (bIsComplex)
7956
0
                                    {
7957
0
                                        dfValueImag +=
7958
0
                                            (dfWeight / dfTotalWeight) *
7959
0
                                            (dfValueImagTmp - dfValueImag);
7960
0
                                    }
7961
0
                                }
7962
0
                            }
7963
0
                        }
7964
0
                    }
7965
7966
0
                    if (dfTotalWeight > 0)
7967
0
                    {
7968
0
                        if (poWK->bApplyVerticalShift)
7969
0
                        {
7970
0
                            if (!std::isfinite(padfZ[iDstX]))
7971
0
                                continue;
7972
                            // Subtract padfZ[] since the coordinate
7973
                            // transformation is from target to source
7974
0
                            dfValueReal =
7975
0
                                dfValueReal * poWK->dfMultFactorVerticalShift -
7976
0
                                padfZ[iDstX] *
7977
0
                                    dfMultFactorVerticalShiftPipeline;
7978
0
                        }
7979
7980
0
                        dfBandDensity = 1;
7981
0
                        bHasFoundDensity = true;
7982
0
                    }
7983
0
                }  // GRA_Average.
7984
7985
0
                else if (poWK->eResample == GRA_RMS)
7986
0
                {
7987
0
                    double dfTotalReal = 0.0;
7988
0
                    double dfTotalImag = 0.0;
7989
0
                    double dfTotalWeight = 0.0;
7990
                    // This code adapted from GDALDownsampleChunk32R_AverageT()
7991
                    // in gcore/overview.cpp.
7992
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
7993
0
                    {
7994
0
                        const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
7995
0
                        iSrcOffset = iSrcXMin +
7996
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
7997
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
7998
0
                             iSrcX++, iSrcOffset++)
7999
0
                        {
8000
0
                            if (bWrapOverX)
8001
0
                                iSrcOffset =
8002
0
                                    (iSrcX % nSrcXSize) +
8003
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8004
8005
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
8006
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
8007
0
                                            iSrcOffset))
8008
0
                            {
8009
0
                                continue;
8010
0
                            }
8011
8012
0
                            if (GWKGetPixelValue(
8013
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
8014
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
8015
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
8016
0
                            {
8017
0
                                const double dfWeight =
8018
0
                                    COMPUTE_WEIGHT(iSrcX, dfWeightY);
8019
0
                                dfTotalWeight += dfWeight;
8020
0
                                dfTotalReal +=
8021
0
                                    dfValueRealTmp * dfValueRealTmp * dfWeight;
8022
0
                                if (bIsComplex)
8023
0
                                    dfTotalImag += dfValueImagTmp *
8024
0
                                                   dfValueImagTmp * dfWeight;
8025
0
                            }
8026
0
                        }
8027
0
                    }
8028
8029
0
                    if (dfTotalWeight > 0)
8030
0
                    {
8031
0
                        dfValueReal = sqrt(dfTotalReal / dfTotalWeight);
8032
8033
0
                        if (poWK->bApplyVerticalShift)
8034
0
                        {
8035
0
                            if (!std::isfinite(padfZ[iDstX]))
8036
0
                                continue;
8037
                            // Subtract padfZ[] since the coordinate
8038
                            // transformation is from target to source
8039
0
                            dfValueReal =
8040
0
                                dfValueReal * poWK->dfMultFactorVerticalShift -
8041
0
                                padfZ[iDstX] *
8042
0
                                    dfMultFactorVerticalShiftPipeline;
8043
0
                        }
8044
8045
0
                        if (bIsComplex)
8046
0
                            dfValueImag = sqrt(dfTotalImag / dfTotalWeight);
8047
8048
0
                        dfBandDensity = 1;
8049
0
                        bHasFoundDensity = true;
8050
0
                    }
8051
0
                }  // GRA_RMS.
8052
8053
0
                else if (poWK->eResample == GRA_Mode)
8054
0
                {
8055
0
                    float fMaxCount = 0.0f;
8056
0
                    int nMode = -1;
8057
0
                    bool bHasSourceValues = false;
8058
8059
0
                    memset(pafCounts, 0, nBins * sizeof(float));
8060
8061
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
8062
0
                    {
8063
0
                        const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
8064
0
                        iSrcOffset = iSrcXMin +
8065
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8066
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
8067
0
                             iSrcX++, iSrcOffset++)
8068
0
                        {
8069
0
                            if (bWrapOverX)
8070
0
                                iSrcOffset =
8071
0
                                    (iSrcX % nSrcXSize) +
8072
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8073
8074
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
8075
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
8076
0
                                            iSrcOffset))
8077
0
                                continue;
8078
8079
0
                            if (GWKGetPixelValue(
8080
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
8081
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
8082
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
8083
0
                            {
8084
0
                                bHasSourceValues = true;
8085
0
                                const int nVal =
8086
0
                                    static_cast<int>(dfValueRealTmp);
8087
0
                                const int iBin = nVal + nBinsOffset;
8088
0
                                const double dfWeight =
8089
0
                                    COMPUTE_WEIGHT(iSrcX, dfWeightY);
8090
8091
                                // Sum the density.
8092
0
                                pafCounts[iBin] += static_cast<float>(dfWeight);
8093
                                // Is it the most common value so far?
8094
0
                                bool bUpdateMode = pafCounts[iBin] > fMaxCount;
8095
0
                                if (!bUpdateMode &&
8096
0
                                    pafCounts[iBin] == fMaxCount)
8097
0
                                {
8098
0
                                    switch (eTieStrategy)
8099
0
                                    {
8100
0
                                        case GWKTS_First:
8101
0
                                            break;
8102
0
                                        case GWKTS_Min:
8103
0
                                            bUpdateMode = nVal < nMode;
8104
0
                                            break;
8105
0
                                        case GWKTS_Max:
8106
0
                                            bUpdateMode = nVal > nMode;
8107
0
                                            break;
8108
0
                                    }
8109
0
                                }
8110
0
                                if (bUpdateMode)
8111
0
                                {
8112
0
                                    nMode = nVal;
8113
0
                                    fMaxCount = pafCounts[iBin];
8114
0
                                }
8115
0
                            }
8116
0
                        }
8117
0
                    }
8118
8119
0
                    if (bHasSourceValues)
8120
0
                    {
8121
0
                        dfValueReal = nMode;
8122
0
                        dfBandDensity = 1;
8123
0
                        bHasFoundDensity = true;
8124
0
                    }
8125
0
                }  // GRA_Mode.
8126
8127
0
                else if (poWK->eResample == GRA_Max)
8128
0
                {
8129
0
                    bool bFoundValid = false;
8130
0
                    double dfTotalReal = cpl::NumericLimits<double>::lowest();
8131
                    // This code adapted from nAlgo 1 method, GRA_Average.
8132
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
8133
0
                    {
8134
0
                        iSrcOffset = iSrcXMin +
8135
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8136
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
8137
0
                             iSrcX++, iSrcOffset++)
8138
0
                        {
8139
0
                            if (bWrapOverX)
8140
0
                                iSrcOffset =
8141
0
                                    (iSrcX % nSrcXSize) +
8142
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8143
8144
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
8145
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
8146
0
                                            iSrcOffset))
8147
0
                            {
8148
0
                                continue;
8149
0
                            }
8150
8151
                            // Returns pixel value if it is not no data.
8152
0
                            if (GWKGetPixelValue(
8153
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
8154
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
8155
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
8156
0
                            {
8157
0
                                bFoundValid = true;
8158
0
                                if (dfTotalReal < dfValueRealTmp)
8159
0
                                {
8160
0
                                    dfTotalReal = dfValueRealTmp;
8161
0
                                }
8162
0
                            }
8163
0
                        }
8164
0
                    }
8165
8166
0
                    if (bFoundValid)
8167
0
                    {
8168
0
                        dfValueReal = dfTotalReal;
8169
8170
0
                        if (poWK->bApplyVerticalShift)
8171
0
                        {
8172
0
                            if (!std::isfinite(padfZ[iDstX]))
8173
0
                                continue;
8174
                            // Subtract padfZ[] since the coordinate
8175
                            // transformation is from target to source
8176
0
                            dfValueReal =
8177
0
                                dfValueReal * poWK->dfMultFactorVerticalShift -
8178
0
                                padfZ[iDstX] *
8179
0
                                    dfMultFactorVerticalShiftPipeline;
8180
0
                        }
8181
8182
0
                        dfBandDensity = 1;
8183
0
                        bHasFoundDensity = true;
8184
0
                    }
8185
0
                }
8186
8187
0
                else if (poWK->eResample == GRA_Min)
8188
0
                {
8189
0
                    bool bFoundValid = false;
8190
0
                    double dfTotalReal = cpl::NumericLimits<double>::max();
8191
                    // This code adapted from nAlgo 1 method, GRA_Average.
8192
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
8193
0
                    {
8194
0
                        iSrcOffset = iSrcXMin +
8195
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8196
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
8197
0
                             iSrcX++, iSrcOffset++)
8198
0
                        {
8199
0
                            if (bWrapOverX)
8200
0
                                iSrcOffset =
8201
0
                                    (iSrcX % nSrcXSize) +
8202
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8203
8204
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
8205
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
8206
0
                                            iSrcOffset))
8207
0
                            {
8208
0
                                continue;
8209
0
                            }
8210
8211
                            // Returns pixel value if it is not no data.
8212
0
                            if (GWKGetPixelValue(
8213
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
8214
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
8215
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
8216
0
                            {
8217
0
                                bFoundValid = true;
8218
0
                                if (dfTotalReal > dfValueRealTmp)
8219
0
                                {
8220
0
                                    dfTotalReal = dfValueRealTmp;
8221
0
                                }
8222
0
                            }
8223
0
                        }
8224
0
                    }
8225
8226
0
                    if (bFoundValid)
8227
0
                    {
8228
0
                        dfValueReal = dfTotalReal;
8229
8230
0
                        if (poWK->bApplyVerticalShift)
8231
0
                        {
8232
0
                            if (!std::isfinite(padfZ[iDstX]))
8233
0
                                continue;
8234
                            // Subtract padfZ[] since the coordinate
8235
                            // transformation is from target to source
8236
0
                            dfValueReal =
8237
0
                                dfValueReal * poWK->dfMultFactorVerticalShift -
8238
0
                                padfZ[iDstX] *
8239
0
                                    dfMultFactorVerticalShiftPipeline;
8240
0
                        }
8241
8242
0
                        dfBandDensity = 1;
8243
0
                        bHasFoundDensity = true;
8244
0
                    }
8245
0
                }  // GRA_Min.
8246
8247
0
                else
8248
                // poWK->eResample == GRA_Med | GRA_Q1 | GRA_Q3.
8249
0
                {
8250
0
                    CPLAssert(quant > 0.0f);
8251
8252
0
                    bool bFoundValid = false;
8253
0
                    std::vector<double> dfRealValuesTmp;
8254
8255
                    // This code adapted from nAlgo 1 method, GRA_Average.
8256
0
                    for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
8257
0
                    {
8258
0
                        iSrcOffset = iSrcXMin +
8259
0
                                     static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8260
0
                        for (int iSrcX = iSrcXMin; iSrcX < iSrcXMax;
8261
0
                             iSrcX++, iSrcOffset++)
8262
0
                        {
8263
0
                            if (bWrapOverX)
8264
0
                                iSrcOffset =
8265
0
                                    (iSrcX % nSrcXSize) +
8266
0
                                    static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
8267
8268
0
                            if (poWK->panUnifiedSrcValid != nullptr &&
8269
0
                                !CPLMaskGet(poWK->panUnifiedSrcValid,
8270
0
                                            iSrcOffset))
8271
0
                            {
8272
0
                                continue;
8273
0
                            }
8274
8275
                            // Returns pixel value if it is not no data.
8276
0
                            if (GWKGetPixelValue(
8277
0
                                    poWK, iBand, iSrcOffset, &dfBandDensity,
8278
0
                                    &dfValueRealTmp, &dfValueImagTmp) &&
8279
0
                                dfBandDensity > BAND_DENSITY_THRESHOLD)
8280
0
                            {
8281
0
                                bFoundValid = true;
8282
0
                                dfRealValuesTmp.push_back(dfValueRealTmp);
8283
0
                            }
8284
0
                        }
8285
0
                    }
8286
8287
0
                    if (bFoundValid)
8288
0
                    {
8289
0
                        std::sort(dfRealValuesTmp.begin(),
8290
0
                                  dfRealValuesTmp.end());
8291
0
                        int quantIdx = static_cast<int>(
8292
0
                            std::ceil(quant * dfRealValuesTmp.size() - 1));
8293
0
                        dfValueReal = dfRealValuesTmp[quantIdx];
8294
8295
0
                        if (poWK->bApplyVerticalShift)
8296
0
                        {
8297
0
                            if (!std::isfinite(padfZ[iDstX]))
8298
0
                                continue;
8299
                            // Subtract padfZ[] since the coordinate
8300
                            // transformation is from target to source
8301
0
                            dfValueReal =
8302
0
                                dfValueReal * poWK->dfMultFactorVerticalShift -
8303
0
                                padfZ[iDstX] *
8304
0
                                    dfMultFactorVerticalShiftPipeline;
8305
0
                        }
8306
8307
0
                        dfBandDensity = 1;
8308
0
                        bHasFoundDensity = true;
8309
0
                        dfRealValuesTmp.clear();
8310
0
                    }
8311
0
                }  // Quantile.
8312
8313
                /* --------------------------------------------------------------------
8314
                 */
8315
                /*      We have a computed value from the source.  Now apply it
8316
                 * to      */
8317
                /*      the destination pixel. */
8318
                /* --------------------------------------------------------------------
8319
                 */
8320
0
                if (bHasFoundDensity)
8321
0
                {
8322
                    // TODO: Should we compute dfBandDensity in fct of
8323
                    // nCount/nCount2, or use as a threshold to set the dest
8324
                    // value?
8325
                    // dfBandDensity = (float) nCount / nCount2;
8326
                    // if( (float) nCount / nCount2 > 0.1 )
8327
                    // or fix gdalwarp crop_to_cutline to crop partially
8328
                    // overlapping pixels.
8329
0
                    GWKSetPixelValue(poWK, iBand, iDstOffset, dfBandDensity,
8330
0
                                     dfValueReal, dfValueImag,
8331
0
                                     bAvoidNoDataSingleBand);
8332
0
                }
8333
0
            }
8334
8335
0
            if (!bHasFoundDensity)
8336
0
                continue;
8337
8338
0
            if (!bAvoidNoDataSingleBand)
8339
0
            {
8340
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
8341
0
            }
8342
8343
            /* --------------------------------------------------------------------
8344
             */
8345
            /*      Update destination density/validity masks. */
8346
            /* --------------------------------------------------------------------
8347
             */
8348
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity);
8349
8350
0
            if (poWK->panDstValid != nullptr)
8351
0
            {
8352
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
8353
0
            }
8354
0
        } /* Next iDstX */
8355
8356
        /* --------------------------------------------------------------------
8357
         */
8358
        /*      Report progress to the user, and optionally cancel out. */
8359
        /* --------------------------------------------------------------------
8360
         */
8361
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
8362
0
            break;
8363
0
    }
8364
8365
    /* -------------------------------------------------------------------- */
8366
    /*      Cleanup and return.                                             */
8367
    /* -------------------------------------------------------------------- */
8368
0
    CPLFree(padfX);
8369
0
    CPLFree(padfY);
8370
0
    CPLFree(padfZ);
8371
0
    CPLFree(padfX2);
8372
0
    CPLFree(padfY2);
8373
0
    CPLFree(padfZ2);
8374
0
    CPLFree(pabSuccess);
8375
0
    CPLFree(pabSuccess2);
8376
0
    VSIFree(pafCounts);
8377
0
}
8378
8379
/************************************************************************/
8380
/*                           getOrientation()                           */
8381
/************************************************************************/
8382
8383
// Returns 1 whether (p1,p2,p3) is clockwise oriented,
8384
// -1 if it is counter-clockwise oriented,
8385
// or 0 if it is colinear.
8386
static int getOrientation(const XYPair &p1, const XYPair &p2, const XYPair &p3)
8387
0
{
8388
0
    const double p1x = p1.first;
8389
0
    const double p1y = p1.second;
8390
0
    const double p2x = p2.first;
8391
0
    const double p2y = p2.second;
8392
0
    const double p3x = p3.first;
8393
0
    const double p3y = p3.second;
8394
0
    const double val = (p2y - p1y) * (p3x - p2x) - (p2x - p1x) * (p3y - p2y);
8395
0
    if (std::abs(val) < 1e-20)
8396
0
        return 0;
8397
0
    else if (val > 0)
8398
0
        return 1;
8399
0
    else
8400
0
        return -1;
8401
0
}
8402
8403
/************************************************************************/
8404
/*                              isConvex()                              */
8405
/************************************************************************/
8406
8407
// poly must be closed
8408
static bool isConvex(const XYPoly &poly)
8409
0
{
8410
0
    const size_t n = poly.size();
8411
0
    size_t i = 0;
8412
0
    int last_orientation = getOrientation(poly[i], poly[i + 1], poly[i + 2]);
8413
0
    ++i;
8414
0
    for (; i < n - 2; ++i)
8415
0
    {
8416
0
        const int orientation =
8417
0
            getOrientation(poly[i], poly[i + 1], poly[i + 2]);
8418
0
        if (orientation != 0)
8419
0
        {
8420
0
            if (last_orientation == 0)
8421
0
                last_orientation = orientation;
8422
0
            else if (orientation != last_orientation)
8423
0
                return false;
8424
0
        }
8425
0
    }
8426
0
    return true;
8427
0
}
8428
8429
/************************************************************************/
8430
/*                     pointIntersectsConvexPoly()                      */
8431
/************************************************************************/
8432
8433
// Returns whether xy intersects poly, that must be closed and convex.
8434
static bool pointIntersectsConvexPoly(const XYPair &xy, const XYPoly &poly)
8435
0
{
8436
0
    const size_t n = poly.size();
8437
0
    double dx1 = xy.first - poly[0].first;
8438
0
    double dy1 = xy.second - poly[0].second;
8439
0
    double dx2 = poly[1].first - poly[0].first;
8440
0
    double dy2 = poly[1].second - poly[0].second;
8441
0
    double prevCrossProduct = dx1 * dy2 - dx2 * dy1;
8442
8443
    // Check if the point remains on the same side (left/right) of all edges
8444
0
    for (size_t i = 2; i < n; i++)
8445
0
    {
8446
0
        dx1 = xy.first - poly[i - 1].first;
8447
0
        dy1 = xy.second - poly[i - 1].second;
8448
8449
0
        dx2 = poly[i].first - poly[i - 1].first;
8450
0
        dy2 = poly[i].second - poly[i - 1].second;
8451
8452
0
        double crossProduct = dx1 * dy2 - dx2 * dy1;
8453
0
        if (std::abs(prevCrossProduct) < 1e-20)
8454
0
            prevCrossProduct = crossProduct;
8455
0
        else if (prevCrossProduct * crossProduct < 0)
8456
0
            return false;
8457
0
    }
8458
8459
0
    return true;
8460
0
}
8461
8462
/************************************************************************/
8463
/*                          getIntersection()                           */
8464
/************************************************************************/
8465
8466
/* Returns intersection of [p1,p2] with [p3,p4], if
8467
 * it is a single point, and the 2 segments are not colinear.
8468
 */
8469
static bool getIntersection(const XYPair &p1, const XYPair &p2,
8470
                            const XYPair &p3, const XYPair &p4, XYPair &xy)
8471
0
{
8472
0
    const double x1 = p1.first;
8473
0
    const double y1 = p1.second;
8474
0
    const double x2 = p2.first;
8475
0
    const double y2 = p2.second;
8476
0
    const double x3 = p3.first;
8477
0
    const double y3 = p3.second;
8478
0
    const double x4 = p4.first;
8479
0
    const double y4 = p4.second;
8480
0
    const double t_num = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4);
8481
0
    const double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
8482
0
    if (t_num * denom < 0 || std::abs(t_num) > std::abs(denom) || denom == 0)
8483
0
        return false;
8484
8485
0
    const double u_num = (x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2);
8486
0
    if (u_num * denom < 0 || std::abs(u_num) > std::abs(denom))
8487
0
        return false;
8488
8489
0
    const double t = t_num / denom;
8490
0
    xy.first = x1 + t * (x2 - x1);
8491
0
    xy.second = y1 + t * (y2 - y1);
8492
0
    return true;
8493
0
}
8494
8495
/************************************************************************/
8496
/*                     getConvexPolyIntersection()                      */
8497
/************************************************************************/
8498
8499
// poly1 and poly2 must be closed and convex.
8500
// The returned intersection will not necessary be closed.
8501
static void getConvexPolyIntersection(const XYPoly &poly1, const XYPoly &poly2,
8502
                                      XYPoly &intersection)
8503
0
{
8504
0
    intersection.clear();
8505
8506
    // Add all points of poly1 inside poly2
8507
0
    for (size_t i = 0; i < poly1.size() - 1; ++i)
8508
0
    {
8509
0
        if (pointIntersectsConvexPoly(poly1[i], poly2))
8510
0
            intersection.push_back(poly1[i]);
8511
0
    }
8512
0
    if (intersection.size() == poly1.size() - 1)
8513
0
    {
8514
        // poly1 is inside poly2
8515
0
        return;
8516
0
    }
8517
8518
    // Add all points of poly2 inside poly1
8519
0
    for (size_t i = 0; i < poly2.size() - 1; ++i)
8520
0
    {
8521
0
        if (pointIntersectsConvexPoly(poly2[i], poly1))
8522
0
            intersection.push_back(poly2[i]);
8523
0
    }
8524
8525
    // Compute the intersection of all edges of both polygons
8526
0
    XYPair xy;
8527
0
    for (size_t i1 = 0; i1 < poly1.size() - 1; ++i1)
8528
0
    {
8529
0
        for (size_t i2 = 0; i2 < poly2.size() - 1; ++i2)
8530
0
        {
8531
0
            if (getIntersection(poly1[i1], poly1[i1 + 1], poly2[i2],
8532
0
                                poly2[i2 + 1], xy))
8533
0
            {
8534
0
                intersection.push_back(xy);
8535
0
            }
8536
0
        }
8537
0
    }
8538
8539
0
    if (intersection.empty())
8540
0
        return;
8541
8542
    // Find lowest-left point in intersection set
8543
0
    double lowest_x = cpl::NumericLimits<double>::max();
8544
0
    double lowest_y = cpl::NumericLimits<double>::max();
8545
0
    for (const auto &pair : intersection)
8546
0
    {
8547
0
        const double x = pair.first;
8548
0
        const double y = pair.second;
8549
0
        if (y < lowest_y || (y == lowest_y && x < lowest_x))
8550
0
        {
8551
0
            lowest_x = x;
8552
0
            lowest_y = y;
8553
0
        }
8554
0
    }
8555
8556
0
    const auto sortFunc = [&](const XYPair &p1, const XYPair &p2)
8557
0
    {
8558
0
        const double p1x_diff = p1.first - lowest_x;
8559
0
        const double p1y_diff = p1.second - lowest_y;
8560
0
        const double p2x_diff = p2.first - lowest_x;
8561
0
        const double p2y_diff = p2.second - lowest_y;
8562
0
        if (p2y_diff == 0.0 && p1y_diff == 0.0)
8563
0
        {
8564
0
            if (p1x_diff >= 0)
8565
0
            {
8566
0
                if (p2x_diff >= 0)
8567
0
                    return p1.first < p2.first;
8568
0
                return true;
8569
0
            }
8570
0
            else
8571
0
            {
8572
0
                if (p2x_diff >= 0)
8573
0
                    return false;
8574
0
                return p1.first < p2.first;
8575
0
            }
8576
0
        }
8577
8578
0
        if (p2x_diff == 0.0 && p1x_diff == 0.0)
8579
0
            return p1.second < p2.second;
8580
8581
0
        double tan_p1;
8582
0
        if (p1x_diff == 0.0)
8583
0
            tan_p1 = p1y_diff == 0.0 ? 0.0 : cpl::NumericLimits<double>::max();
8584
0
        else
8585
0
            tan_p1 = p1y_diff / p1x_diff;
8586
8587
0
        double tan_p2;
8588
0
        if (p2x_diff == 0.0)
8589
0
            tan_p2 = p2y_diff == 0.0 ? 0.0 : cpl::NumericLimits<double>::max();
8590
0
        else
8591
0
            tan_p2 = p2y_diff / p2x_diff;
8592
8593
0
        if (tan_p1 >= 0)
8594
0
        {
8595
0
            if (tan_p2 >= 0)
8596
0
                return tan_p1 < tan_p2;
8597
0
            else
8598
0
                return true;
8599
0
        }
8600
0
        else
8601
0
        {
8602
0
            if (tan_p2 >= 0)
8603
0
                return false;
8604
0
            else
8605
0
                return tan_p1 < tan_p2;
8606
0
        }
8607
0
    };
8608
8609
    // Sort points by increasing atan2(y-lowest_y, x-lowest_x) to form a convex
8610
    // hull
8611
0
    std::sort(intersection.begin(), intersection.end(), sortFunc);
8612
8613
    // Remove duplicated points
8614
0
    size_t j = 1;
8615
0
    for (size_t i = 1; i < intersection.size(); ++i)
8616
0
    {
8617
0
        if (intersection[i] != intersection[i - 1])
8618
0
        {
8619
0
            if (j < i)
8620
0
                intersection[j] = intersection[i];
8621
0
            ++j;
8622
0
        }
8623
0
    }
8624
0
    intersection.resize(j);
8625
0
}
8626
8627
/************************************************************************/
8628
/*                          GWKSumPreserving()                          */
8629
/************************************************************************/
8630
8631
static void GWKSumPreservingThread(void *pData);
8632
8633
static CPLErr GWKSumPreserving(GDALWarpKernel *poWK)
8634
0
{
8635
0
    return GWKRun(poWK, "GWKSumPreserving", GWKSumPreservingThread);
8636
0
}
8637
8638
static void GWKSumPreservingThread(void *pData)
8639
0
{
8640
0
    GWKJobStruct *psJob = static_cast<GWKJobStruct *>(pData);
8641
0
    GDALWarpKernel *poWK = psJob->poWK;
8642
0
    const int iYMin = psJob->iYMin;
8643
0
    const int iYMax = psJob->iYMax;
8644
0
    const bool bIsAffineNoRotation =
8645
0
        GDALTransformIsAffineNoRotation(poWK->pfnTransformer,
8646
0
                                        poWK->pTransformerArg) &&
8647
        // for debug/testing purposes
8648
0
        CPLTestBool(
8649
0
            CPLGetConfigOption("GDAL_WARP_USE_AFFINE_OPTIMIZATION", "YES"));
8650
0
    const bool bAvoidNoDataSingleBand =
8651
0
        poWK->nBands == 1 ||
8652
0
        !CPLTestBool(CSLFetchNameValueDef(poWK->papszWarpOptions,
8653
0
                                          "UNIFIED_SRC_NODATA", "FALSE"));
8654
8655
0
    const int nDstXSize = poWK->nDstXSize;
8656
0
    const int nSrcXSize = poWK->nSrcXSize;
8657
0
    const int nSrcYSize = poWK->nSrcYSize;
8658
8659
0
    std::vector<double> adfX0(nSrcXSize + 1);
8660
0
    std::vector<double> adfY0(nSrcXSize + 1);
8661
0
    std::vector<double> adfZ0(nSrcXSize + 1);
8662
0
    std::vector<double> adfX1(nSrcXSize + 1);
8663
0
    std::vector<double> adfY1(nSrcXSize + 1);
8664
0
    std::vector<double> adfZ1(nSrcXSize + 1);
8665
0
    std::vector<int> abSuccess0(nSrcXSize + 1);
8666
0
    std::vector<int> abSuccess1(nSrcXSize + 1);
8667
8668
0
    CPLRectObj sGlobalBounds;
8669
0
    sGlobalBounds.minx = -2 * poWK->dfXScale;
8670
0
    sGlobalBounds.miny = iYMin - 2 * poWK->dfYScale;
8671
0
    sGlobalBounds.maxx = nDstXSize + 2 * poWK->dfXScale;
8672
0
    sGlobalBounds.maxy = iYMax + 2 * poWK->dfYScale;
8673
0
    CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
8674
8675
0
    struct SourcePixel
8676
0
    {
8677
0
        int iSrcX;
8678
0
        int iSrcY;
8679
8680
        // Coordinates of source pixel in target pixel coordinates
8681
0
        double dfDstX0;
8682
0
        double dfDstY0;
8683
0
        double dfDstX1;
8684
0
        double dfDstY1;
8685
0
        double dfDstX2;
8686
0
        double dfDstY2;
8687
0
        double dfDstX3;
8688
0
        double dfDstY3;
8689
8690
        // Source pixel total area (might be larger than the one described
8691
        // by above coordinates, if the pixel was crossing the antimeridian
8692
        // and split)
8693
0
        double dfArea;
8694
0
    };
8695
8696
0
    std::vector<SourcePixel> sourcePixels;
8697
8698
0
    XYPoly discontinuityLeft(5);
8699
0
    XYPoly discontinuityRight(5);
8700
8701
    /* ==================================================================== */
8702
    /*      First pass: transform the 4 corners of each potential           */
8703
    /*      contributing source pixel to target pixel coordinates.          */
8704
    /* ==================================================================== */
8705
8706
    // Special case for top line
8707
0
    {
8708
0
        int iY = 0;
8709
0
        for (int iX = 0; iX <= nSrcXSize; ++iX)
8710
0
        {
8711
0
            adfX1[iX] = iX + poWK->nSrcXOff;
8712
0
            adfY1[iX] = iY + poWK->nSrcYOff;
8713
0
            adfZ1[iX] = 0;
8714
0
        }
8715
8716
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, nSrcXSize + 1,
8717
0
                             adfX1.data(), adfY1.data(), adfZ1.data(),
8718
0
                             abSuccess1.data());
8719
8720
0
        for (int iX = 0; iX <= nSrcXSize; ++iX)
8721
0
        {
8722
0
            if (abSuccess1[iX] && !std::isfinite(adfX1[iX]))
8723
0
                abSuccess1[iX] = FALSE;
8724
0
            else
8725
0
            {
8726
0
                adfX1[iX] -= poWK->nDstXOff;
8727
0
                adfY1[iX] -= poWK->nDstYOff;
8728
0
            }
8729
0
        }
8730
0
    }
8731
8732
0
    const auto getInsideXSign = [poWK, nDstXSize](double dfX)
8733
0
    {
8734
0
        return dfX - poWK->nDstXOff >= -2 * poWK->dfXScale &&
8735
0
                       dfX - poWK->nDstXOff <= nDstXSize + 2 * poWK->dfXScale
8736
0
                   ? 1
8737
0
                   : -1;
8738
0
    };
8739
8740
0
    const auto FindDiscontinuity =
8741
0
        [poWK, psJob, getInsideXSign](
8742
0
            double dfXLeft, double dfXRight, double dfY,
8743
0
            int XLeftReprojectedInsideSign, double &dfXMidReprojectedLeft,
8744
0
            double &dfXMidReprojectedRight, double &dfYMidReprojected)
8745
0
    {
8746
0
        for (int i = 0; i < 10 && dfXRight - dfXLeft > 1e-8; ++i)
8747
0
        {
8748
0
            double dfXMid = (dfXLeft + dfXRight) / 2;
8749
0
            double dfXMidReprojected = dfXMid;
8750
0
            dfYMidReprojected = dfY;
8751
0
            double dfZ = 0;
8752
0
            int nSuccess = 0;
8753
0
            poWK->pfnTransformer(psJob->pTransformerArg, FALSE, 1,
8754
0
                                 &dfXMidReprojected, &dfYMidReprojected, &dfZ,
8755
0
                                 &nSuccess);
8756
0
            if (XLeftReprojectedInsideSign != getInsideXSign(dfXMidReprojected))
8757
0
            {
8758
0
                dfXRight = dfXMid;
8759
0
                dfXMidReprojectedRight = dfXMidReprojected;
8760
0
            }
8761
0
            else
8762
0
            {
8763
0
                dfXLeft = dfXMid;
8764
0
                dfXMidReprojectedLeft = dfXMidReprojected;
8765
0
            }
8766
0
        }
8767
0
    };
8768
8769
0
    for (int iY = 0; iY < nSrcYSize; ++iY)
8770
0
    {
8771
0
        std::swap(adfX0, adfX1);
8772
0
        std::swap(adfY0, adfY1);
8773
0
        std::swap(adfZ0, adfZ1);
8774
0
        std::swap(abSuccess0, abSuccess1);
8775
8776
0
        for (int iX = 0; iX <= nSrcXSize; ++iX)
8777
0
        {
8778
0
            adfX1[iX] = iX + poWK->nSrcXOff;
8779
0
            adfY1[iX] = iY + 1 + poWK->nSrcYOff;
8780
0
            adfZ1[iX] = 0;
8781
0
        }
8782
8783
0
        poWK->pfnTransformer(psJob->pTransformerArg, FALSE, nSrcXSize + 1,
8784
0
                             adfX1.data(), adfY1.data(), adfZ1.data(),
8785
0
                             abSuccess1.data());
8786
8787
0
        for (int iX = 0; iX <= nSrcXSize; ++iX)
8788
0
        {
8789
0
            if (abSuccess1[iX] && !std::isfinite(adfX1[iX]))
8790
0
                abSuccess1[iX] = FALSE;
8791
0
            else
8792
0
            {
8793
0
                adfX1[iX] -= poWK->nDstXOff;
8794
0
                adfY1[iX] -= poWK->nDstYOff;
8795
0
            }
8796
0
        }
8797
8798
0
        for (int iX = 0; iX < nSrcXSize; ++iX)
8799
0
        {
8800
0
            if (abSuccess0[iX] && abSuccess0[iX + 1] && abSuccess1[iX] &&
8801
0
                abSuccess1[iX + 1])
8802
0
            {
8803
                /* --------------------------------------------------------------------
8804
                 */
8805
                /*      Do not try to apply transparent source pixels to the
8806
                 * destination.*/
8807
                /* --------------------------------------------------------------------
8808
                 */
8809
0
                const auto iSrcOffset =
8810
0
                    iX + static_cast<GPtrDiff_t>(iY) * nSrcXSize;
8811
0
                if (poWK->panUnifiedSrcValid != nullptr &&
8812
0
                    !CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
8813
0
                {
8814
0
                    continue;
8815
0
                }
8816
8817
0
                if (poWK->pafUnifiedSrcDensity != nullptr)
8818
0
                {
8819
0
                    if (poWK->pafUnifiedSrcDensity[iSrcOffset] <
8820
0
                        SRC_DENSITY_THRESHOLD_FLOAT)
8821
0
                        continue;
8822
0
                }
8823
8824
0
                SourcePixel sp;
8825
0
                sp.dfArea = 0;
8826
0
                sp.dfDstX0 = adfX0[iX];
8827
0
                sp.dfDstY0 = adfY0[iX];
8828
0
                sp.dfDstX1 = adfX0[iX + 1];
8829
0
                sp.dfDstY1 = adfY0[iX + 1];
8830
0
                sp.dfDstX2 = adfX1[iX + 1];
8831
0
                sp.dfDstY2 = adfY1[iX + 1];
8832
0
                sp.dfDstX3 = adfX1[iX];
8833
0
                sp.dfDstY3 = adfY1[iX];
8834
8835
                // Detect pixel that likely cross the anti-meridian and
8836
                // introduce a discontinuity when reprojected.
8837
8838
0
                if (std::fabs(adfX0[iX] - adfX0[iX + 1]) > 2 * poWK->dfXScale &&
8839
0
                    std::fabs(adfX1[iX] - adfX1[iX + 1]) > 2 * poWK->dfXScale &&
8840
0
                    getInsideXSign(adfX0[iX]) !=
8841
0
                        getInsideXSign(adfX0[iX + 1]) &&
8842
0
                    getInsideXSign(adfX0[iX]) == getInsideXSign(adfX1[iX]) &&
8843
0
                    getInsideXSign(adfX0[iX + 1]) ==
8844
0
                        getInsideXSign(adfX1[iX + 1]) &&
8845
0
                    (adfY1[iX] - adfY0[iX]) * (adfY1[iX + 1] - adfY0[iX + 1]) >
8846
0
                        0)
8847
0
                {
8848
#ifdef DEBUG_VERBOSE
8849
                    CPLDebug(
8850
                        "WARP",
8851
                        "Discontinuity for iSrcX=%d, iSrcY=%d, dest corners:"
8852
                        "X0[iX]=%f X0[iX+1]=%f X1[iX]=%f X1[iX+1]=%f,"
8853
                        "Y0[iX]=%f Y0[iX+1]=%f Y1[iX]=%f Y1[iX+1]=%f",
8854
                        iX + poWK->nSrcXOff, iY + poWK->nSrcYOff, adfX0[iX],
8855
                        adfX0[iX + 1], adfX1[iX], adfX1[iX + 1], adfY0[iX],
8856
                        adfY0[iX + 1], adfY1[iX], adfY1[iX + 1]);
8857
#endif
8858
0
                    double dfXMidReprojectedLeftTop = 0;
8859
0
                    double dfXMidReprojectedRightTop = 0;
8860
0
                    double dfYMidReprojectedTop = 0;
8861
0
                    FindDiscontinuity(
8862
0
                        iX + poWK->nSrcXOff, iX + poWK->nSrcXOff + 1,
8863
0
                        iY + poWK->nSrcYOff, getInsideXSign(adfX0[iX]),
8864
0
                        dfXMidReprojectedLeftTop, dfXMidReprojectedRightTop,
8865
0
                        dfYMidReprojectedTop);
8866
0
                    double dfXMidReprojectedLeftBottom = 0;
8867
0
                    double dfXMidReprojectedRightBottom = 0;
8868
0
                    double dfYMidReprojectedBottom = 0;
8869
0
                    FindDiscontinuity(
8870
0
                        iX + poWK->nSrcXOff, iX + poWK->nSrcXOff + 1,
8871
0
                        iY + poWK->nSrcYOff + 1, getInsideXSign(adfX1[iX]),
8872
0
                        dfXMidReprojectedLeftBottom,
8873
0
                        dfXMidReprojectedRightBottom, dfYMidReprojectedBottom);
8874
8875
0
                    discontinuityLeft[0] = XYPair(adfX0[iX], adfY0[iX]);
8876
0
                    discontinuityLeft[1] =
8877
0
                        XYPair(dfXMidReprojectedLeftTop, dfYMidReprojectedTop);
8878
0
                    discontinuityLeft[2] = XYPair(dfXMidReprojectedLeftBottom,
8879
0
                                                  dfYMidReprojectedBottom);
8880
0
                    discontinuityLeft[3] = XYPair(adfX1[iX], adfY1[iX]);
8881
0
                    discontinuityLeft[4] = XYPair(adfX0[iX], adfY0[iX]);
8882
8883
0
                    discontinuityRight[0] =
8884
0
                        XYPair(adfX0[iX + 1], adfY0[iX + 1]);
8885
0
                    discontinuityRight[1] =
8886
0
                        XYPair(dfXMidReprojectedRightTop, dfYMidReprojectedTop);
8887
0
                    discontinuityRight[2] = XYPair(dfXMidReprojectedRightBottom,
8888
0
                                                   dfYMidReprojectedBottom);
8889
0
                    discontinuityRight[3] =
8890
0
                        XYPair(adfX1[iX + 1], adfY1[iX + 1]);
8891
0
                    discontinuityRight[4] =
8892
0
                        XYPair(adfX0[iX + 1], adfY0[iX + 1]);
8893
8894
0
                    sp.dfArea = getArea(discontinuityLeft) +
8895
0
                                getArea(discontinuityRight);
8896
0
                    if (getInsideXSign(adfX0[iX]) >= 1)
8897
0
                    {
8898
0
                        sp.dfDstX1 = dfXMidReprojectedLeftTop;
8899
0
                        sp.dfDstY1 = dfYMidReprojectedTop;
8900
0
                        sp.dfDstX2 = dfXMidReprojectedLeftBottom;
8901
0
                        sp.dfDstY2 = dfYMidReprojectedBottom;
8902
0
                    }
8903
0
                    else
8904
0
                    {
8905
0
                        sp.dfDstX0 = dfXMidReprojectedRightTop;
8906
0
                        sp.dfDstY0 = dfYMidReprojectedTop;
8907
0
                        sp.dfDstX3 = dfXMidReprojectedRightBottom;
8908
0
                        sp.dfDstY3 = dfYMidReprojectedBottom;
8909
0
                    }
8910
0
                }
8911
8912
                // Bounding box of source pixel (expressed in target pixel
8913
                // coordinates)
8914
0
                CPLRectObj sRect;
8915
0
                sRect.minx = std::min(std::min(sp.dfDstX0, sp.dfDstX1),
8916
0
                                      std::min(sp.dfDstX2, sp.dfDstX3));
8917
0
                sRect.miny = std::min(std::min(sp.dfDstY0, sp.dfDstY1),
8918
0
                                      std::min(sp.dfDstY2, sp.dfDstY3));
8919
0
                sRect.maxx = std::max(std::max(sp.dfDstX0, sp.dfDstX1),
8920
0
                                      std::max(sp.dfDstX2, sp.dfDstX3));
8921
0
                sRect.maxy = std::max(std::max(sp.dfDstY0, sp.dfDstY1),
8922
0
                                      std::max(sp.dfDstY2, sp.dfDstY3));
8923
0
                if (!(sRect.minx < nDstXSize && sRect.maxx > 0 &&
8924
0
                      sRect.miny < iYMax && sRect.maxy > iYMin))
8925
0
                {
8926
0
                    continue;
8927
0
                }
8928
8929
0
                sp.iSrcX = iX;
8930
0
                sp.iSrcY = iY;
8931
8932
0
                if (!bIsAffineNoRotation)
8933
0
                {
8934
                    // Check polygon validity (no self-crossing)
8935
0
                    XYPair xy;
8936
0
                    if (getIntersection(XYPair(sp.dfDstX0, sp.dfDstY0),
8937
0
                                        XYPair(sp.dfDstX1, sp.dfDstY1),
8938
0
                                        XYPair(sp.dfDstX2, sp.dfDstY2),
8939
0
                                        XYPair(sp.dfDstX3, sp.dfDstY3), xy) ||
8940
0
                        getIntersection(XYPair(sp.dfDstX1, sp.dfDstY1),
8941
0
                                        XYPair(sp.dfDstX2, sp.dfDstY2),
8942
0
                                        XYPair(sp.dfDstX0, sp.dfDstY0),
8943
0
                                        XYPair(sp.dfDstX3, sp.dfDstY3), xy))
8944
0
                    {
8945
0
                        continue;
8946
0
                    }
8947
0
                }
8948
8949
0
                CPLQuadTreeInsertWithBounds(
8950
0
                    hQuadTree,
8951
0
                    reinterpret_cast<void *>(
8952
0
                        static_cast<uintptr_t>(sourcePixels.size())),
8953
0
                    &sRect);
8954
8955
0
                sourcePixels.push_back(sp);
8956
0
            }
8957
0
        }
8958
0
    }
8959
8960
0
    std::vector<double> adfRealValue(poWK->nBands);
8961
0
    std::vector<double> adfImagValue(poWK->nBands);
8962
0
    std::vector<double> adfBandDensity(poWK->nBands);
8963
0
    std::vector<double> adfWeight(poWK->nBands);
8964
8965
#ifdef CHECK_SUM_WITH_GEOS
8966
    auto hGEOSContext = OGRGeometry::createGEOSContext();
8967
    auto seq1 = GEOSCoordSeq_create_r(hGEOSContext, 5, 2);
8968
    GEOSCoordSeq_setXY_r(hGEOSContext, seq1, 0, 0.0, 0.0);
8969
    GEOSCoordSeq_setXY_r(hGEOSContext, seq1, 1, 1.0, 0.0);
8970
    GEOSCoordSeq_setXY_r(hGEOSContext, seq1, 2, 1.0, 1.0);
8971
    GEOSCoordSeq_setXY_r(hGEOSContext, seq1, 3, 0.0, 1.0);
8972
    GEOSCoordSeq_setXY_r(hGEOSContext, seq1, 4, 0.0, 0.0);
8973
    auto hLR1 = GEOSGeom_createLinearRing_r(hGEOSContext, seq1);
8974
    auto hP1 = GEOSGeom_createPolygon_r(hGEOSContext, hLR1, nullptr, 0);
8975
8976
    auto seq2 = GEOSCoordSeq_create_r(hGEOSContext, 5, 2);
8977
    auto hLR2 = GEOSGeom_createLinearRing_r(hGEOSContext, seq2);
8978
    auto hP2 = GEOSGeom_createPolygon_r(hGEOSContext, hLR2, nullptr, 0);
8979
#endif
8980
8981
0
    const XYPoly xy1{
8982
0
        {0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}};
8983
0
    XYPoly xy2(5);
8984
0
    XYPoly xy2_triangle(4);
8985
0
    XYPoly intersection;
8986
8987
    /* ==================================================================== */
8988
    /*      Loop over output lines.                                         */
8989
    /* ==================================================================== */
8990
0
    for (int iDstY = iYMin; iDstY < iYMax; iDstY++)
8991
0
    {
8992
0
        CPLRectObj sRect;
8993
0
        sRect.miny = iDstY;
8994
0
        sRect.maxy = iDstY + 1;
8995
8996
        /* ====================================================================
8997
         */
8998
        /*      Loop over pixels in output scanline. */
8999
        /* ====================================================================
9000
         */
9001
0
        for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
9002
0
        {
9003
0
            sRect.minx = iDstX;
9004
0
            sRect.maxx = iDstX + 1;
9005
0
            int nSourcePixels = 0;
9006
0
            void **pahSourcePixel =
9007
0
                CPLQuadTreeSearch(hQuadTree, &sRect, &nSourcePixels);
9008
0
            if (nSourcePixels == 0)
9009
0
            {
9010
0
                CPLFree(pahSourcePixel);
9011
0
                continue;
9012
0
            }
9013
9014
0
            std::fill(adfRealValue.begin(), adfRealValue.end(), 0);
9015
0
            std::fill(adfImagValue.begin(), adfImagValue.end(), 0);
9016
0
            std::fill(adfBandDensity.begin(), adfBandDensity.end(), 0);
9017
0
            std::fill(adfWeight.begin(), adfWeight.end(), 0);
9018
0
            double dfDensity = 0;
9019
            // Just above zero to please Coveriy Scan
9020
0
            double dfTotalWeight = std::numeric_limits<double>::min();
9021
9022
            /* ====================================================================
9023
             */
9024
            /*          Iterate over each contributing source pixel to add its
9025
             */
9026
            /*          value weighed by the ratio of the area of its
9027
             * intersection  */
9028
            /*          with the target pixel divided by the area of the source
9029
             */
9030
            /*          pixel. */
9031
            /* ====================================================================
9032
             */
9033
0
            for (int i = 0; i < nSourcePixels; ++i)
9034
0
            {
9035
0
                const int iSourcePixel = static_cast<int>(
9036
0
                    reinterpret_cast<uintptr_t>(pahSourcePixel[i]));
9037
0
                auto &sp = sourcePixels[iSourcePixel];
9038
9039
0
                double dfWeight = 0.0;
9040
0
                if (bIsAffineNoRotation)
9041
0
                {
9042
                    // Optimization since the source pixel is a rectangle in
9043
                    // target pixel coordinates
9044
0
                    double dfSrcMinX = std::min(sp.dfDstX0, sp.dfDstX2);
9045
0
                    double dfSrcMaxX = std::max(sp.dfDstX0, sp.dfDstX2);
9046
0
                    double dfSrcMinY = std::min(sp.dfDstY0, sp.dfDstY2);
9047
0
                    double dfSrcMaxY = std::max(sp.dfDstY0, sp.dfDstY2);
9048
0
                    double dfIntersMinX = std::max<double>(dfSrcMinX, iDstX);
9049
0
                    double dfIntersMaxX = std::min(dfSrcMaxX, iDstX + 1.0);
9050
0
                    double dfIntersMinY = std::max<double>(dfSrcMinY, iDstY);
9051
0
                    double dfIntersMaxY = std::min(dfSrcMaxY, iDstY + 1.0);
9052
0
                    dfWeight =
9053
0
                        ((dfIntersMaxX - dfIntersMinX) *
9054
0
                         (dfIntersMaxY - dfIntersMinY)) /
9055
0
                        ((dfSrcMaxX - dfSrcMinX) * (dfSrcMaxY - dfSrcMinY));
9056
0
                }
9057
0
                else
9058
0
                {
9059
                    // Compute the polygon of the source pixel in target pixel
9060
                    // coordinates, and shifted to the target pixel (unit square
9061
                    // coordinates)
9062
9063
0
                    xy2[0] = {sp.dfDstX0 - iDstX, sp.dfDstY0 - iDstY};
9064
0
                    xy2[1] = {sp.dfDstX1 - iDstX, sp.dfDstY1 - iDstY};
9065
0
                    xy2[2] = {sp.dfDstX2 - iDstX, sp.dfDstY2 - iDstY};
9066
0
                    xy2[3] = {sp.dfDstX3 - iDstX, sp.dfDstY3 - iDstY};
9067
0
                    xy2[4] = {sp.dfDstX0 - iDstX, sp.dfDstY0 - iDstY};
9068
9069
0
                    if (isConvex(xy2))
9070
0
                    {
9071
0
                        getConvexPolyIntersection(xy1, xy2, intersection);
9072
0
                        if (intersection.size() >= 3)
9073
0
                        {
9074
0
                            dfWeight = getArea(intersection);
9075
0
                        }
9076
0
                    }
9077
0
                    else
9078
0
                    {
9079
                        // Split xy2 into 2 triangles.
9080
0
                        xy2_triangle[0] = xy2[0];
9081
0
                        xy2_triangle[1] = xy2[1];
9082
0
                        xy2_triangle[2] = xy2[2];
9083
0
                        xy2_triangle[3] = xy2[0];
9084
0
                        getConvexPolyIntersection(xy1, xy2_triangle,
9085
0
                                                  intersection);
9086
0
                        if (intersection.size() >= 3)
9087
0
                        {
9088
0
                            dfWeight = getArea(intersection);
9089
0
                        }
9090
9091
0
                        xy2_triangle[1] = xy2[2];
9092
0
                        xy2_triangle[2] = xy2[3];
9093
0
                        getConvexPolyIntersection(xy1, xy2_triangle,
9094
0
                                                  intersection);
9095
0
                        if (intersection.size() >= 3)
9096
0
                        {
9097
0
                            dfWeight += getArea(intersection);
9098
0
                        }
9099
0
                    }
9100
0
                    if (dfWeight > 0.0)
9101
0
                    {
9102
0
                        if (sp.dfArea == 0)
9103
0
                            sp.dfArea = getArea(xy2);
9104
0
                        dfWeight /= sp.dfArea;
9105
0
                    }
9106
9107
#ifdef CHECK_SUM_WITH_GEOS
9108
                    GEOSCoordSeq_setXY_r(hGEOSContext, seq2, 0,
9109
                                         sp.dfDstX0 - iDstX,
9110
                                         sp.dfDstY0 - iDstY);
9111
                    GEOSCoordSeq_setXY_r(hGEOSContext, seq2, 1,
9112
                                         sp.dfDstX1 - iDstX,
9113
                                         sp.dfDstY1 - iDstY);
9114
                    GEOSCoordSeq_setXY_r(hGEOSContext, seq2, 2,
9115
                                         sp.dfDstX2 - iDstX,
9116
                                         sp.dfDstY2 - iDstY);
9117
                    GEOSCoordSeq_setXY_r(hGEOSContext, seq2, 3,
9118
                                         sp.dfDstX3 - iDstX,
9119
                                         sp.dfDstY3 - iDstY);
9120
                    GEOSCoordSeq_setXY_r(hGEOSContext, seq2, 4,
9121
                                         sp.dfDstX0 - iDstX,
9122
                                         sp.dfDstY0 - iDstY);
9123
9124
                    double dfWeightGEOS = 0.0;
9125
                    auto hIntersection =
9126
                        GEOSIntersection_r(hGEOSContext, hP1, hP2);
9127
                    if (hIntersection)
9128
                    {
9129
                        double dfIntersArea = 0.0;
9130
                        if (GEOSArea_r(hGEOSContext, hIntersection,
9131
                                       &dfIntersArea) &&
9132
                            dfIntersArea > 0)
9133
                        {
9134
                            double dfSourceArea = 0.0;
9135
                            if (GEOSArea_r(hGEOSContext, hP2, &dfSourceArea))
9136
                            {
9137
                                dfWeightGEOS = dfIntersArea / dfSourceArea;
9138
                            }
9139
                        }
9140
                        GEOSGeom_destroy_r(hGEOSContext, hIntersection);
9141
                    }
9142
                    if (fabs(dfWeight - dfWeightGEOS) > 1e-5 * dfWeightGEOS)
9143
                    {
9144
                        /* ok */ printf("dfWeight=%f dfWeightGEOS=%f\n",
9145
                                        dfWeight, dfWeightGEOS);
9146
                        printf("xy2: ");  // ok
9147
                        for (const auto &xy : xy2)
9148
                            printf("[%f, %f], ", xy.first, xy.second);  // ok
9149
                        printf("\n");                                   // ok
9150
                        printf("intersection: ");                       // ok
9151
                        for (const auto &xy : intersection)
9152
                            printf("[%f, %f], ", xy.first, xy.second);  // ok
9153
                        printf("\n");                                   // ok
9154
                    }
9155
#endif
9156
0
                }
9157
0
                if (dfWeight > 0.0)
9158
0
                {
9159
#ifdef DEBUG_VERBOSE
9160
#if defined(DST_X) && defined(DST_Y)
9161
                    if (iDstX + poWK->nDstXOff == DST_X &&
9162
                        iDstY + poWK->nDstYOff == DST_Y)
9163
                    {
9164
                        CPLDebug("WARP",
9165
                                 "iSrcX = %d, iSrcY = %d, weight =%.17g",
9166
                                 sp.iSrcX + poWK->nSrcXOff,
9167
                                 sp.iSrcY + poWK->nSrcYOff, dfWeight);
9168
                    }
9169
#endif
9170
#endif
9171
9172
0
                    const GPtrDiff_t iSrcOffset =
9173
0
                        sp.iSrcX +
9174
0
                        static_cast<GPtrDiff_t>(sp.iSrcY) * nSrcXSize;
9175
0
                    dfTotalWeight += dfWeight;
9176
9177
0
                    if (poWK->pafUnifiedSrcDensity != nullptr)
9178
0
                    {
9179
0
                        dfDensity +=
9180
0
                            dfWeight *
9181
0
                            double(poWK->pafUnifiedSrcDensity[iSrcOffset]);
9182
0
                    }
9183
0
                    else
9184
0
                    {
9185
0
                        dfDensity += dfWeight;
9186
0
                    }
9187
9188
0
                    for (int iBand = 0; iBand < poWK->nBands; ++iBand)
9189
0
                    {
9190
                        // Returns pixel value if it is not no data.
9191
0
                        double dfBandDensity;
9192
0
                        double dfRealValue;
9193
0
                        double dfImagValue;
9194
0
                        if (!(GWKGetPixelValue(poWK, iBand, iSrcOffset,
9195
0
                                               &dfBandDensity, &dfRealValue,
9196
0
                                               &dfImagValue) &&
9197
0
                              dfBandDensity > BAND_DENSITY_THRESHOLD))
9198
0
                        {
9199
0
                            continue;
9200
0
                        }
9201
#ifdef DEBUG_VERBOSE
9202
#if defined(DST_X) && defined(DST_Y)
9203
                        if (iDstX + poWK->nDstXOff == DST_X &&
9204
                            iDstY + poWK->nDstYOff == DST_Y)
9205
                        {
9206
                            CPLDebug("WARP", "value * weight = %.17g",
9207
                                     dfRealValue * dfWeight);
9208
                        }
9209
#endif
9210
#endif
9211
9212
0
                        adfRealValue[iBand] += dfRealValue * dfWeight;
9213
0
                        adfImagValue[iBand] += dfImagValue * dfWeight;
9214
0
                        adfBandDensity[iBand] += dfBandDensity * dfWeight;
9215
0
                        adfWeight[iBand] += dfWeight;
9216
0
                    }
9217
0
                }
9218
0
            }
9219
9220
0
            CPLFree(pahSourcePixel);
9221
9222
            /* --------------------------------------------------------------------
9223
             */
9224
            /*          Update destination pixel value. */
9225
            /* --------------------------------------------------------------------
9226
             */
9227
0
            bool bHasFoundDensity = false;
9228
0
            const GPtrDiff_t iDstOffset =
9229
0
                iDstX + static_cast<GPtrDiff_t>(iDstY) * nDstXSize;
9230
0
            for (int iBand = 0; iBand < poWK->nBands; ++iBand)
9231
0
            {
9232
0
                if (adfWeight[iBand] > 0)
9233
0
                {
9234
0
                    const double dfBandDensity =
9235
0
                        adfBandDensity[iBand] / adfWeight[iBand];
9236
0
                    if (dfBandDensity > BAND_DENSITY_THRESHOLD)
9237
0
                    {
9238
0
                        bHasFoundDensity = true;
9239
0
                        GWKSetPixelValue(poWK, iBand, iDstOffset, dfBandDensity,
9240
0
                                         adfRealValue[iBand],
9241
0
                                         adfImagValue[iBand],
9242
0
                                         bAvoidNoDataSingleBand);
9243
0
                    }
9244
0
                }
9245
0
            }
9246
9247
0
            if (!bHasFoundDensity)
9248
0
                continue;
9249
9250
0
            if (!bAvoidNoDataSingleBand)
9251
0
            {
9252
0
                GWKAvoidNoDataMultiBand(poWK, iDstOffset);
9253
0
            }
9254
9255
            /* --------------------------------------------------------------------
9256
             */
9257
            /*          Update destination density/validity masks. */
9258
            /* --------------------------------------------------------------------
9259
             */
9260
0
            GWKOverlayDensity(poWK, iDstOffset, dfDensity / dfTotalWeight);
9261
9262
0
            if (poWK->panDstValid != nullptr)
9263
0
            {
9264
0
                CPLMaskSet(poWK->panDstValid, iDstOffset);
9265
0
            }
9266
0
        }
9267
9268
        /* --------------------------------------------------------------------
9269
         */
9270
        /*      Report progress to the user, and optionally cancel out. */
9271
        /* --------------------------------------------------------------------
9272
         */
9273
0
        if (psJob->pfnProgress && psJob->pfnProgress(psJob))
9274
0
            break;
9275
0
    }
9276
9277
#ifdef CHECK_SUM_WITH_GEOS
9278
    GEOSGeom_destroy_r(hGEOSContext, hP1);
9279
    GEOSGeom_destroy_r(hGEOSContext, hP2);
9280
    OGRGeometry::freeGEOSContext(hGEOSContext);
9281
#endif
9282
0
    CPLQuadTreeDestroy(hQuadTree);
9283
0
}