Coverage Report

Created: 2026-04-01 06:20

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