Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/overview.cpp
Line
Count
Source
1
2
/******************************************************************************
3
 *
4
 * Project:  GDAL Core
5
 * Purpose:  Helper code to implement overview support in different drivers.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2000, Frank Warmerdam
10
 * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_port.h"
16
#include "gdal_priv.h"
17
18
#include <cmath>
19
#include <cstddef>
20
#include <cstdlib>
21
22
#include <algorithm>
23
#include <complex>
24
#include <condition_variable>
25
#include <limits>
26
#include <list>
27
#include <memory>
28
#include <mutex>
29
#include <vector>
30
31
#include "cpl_conv.h"
32
#include "cpl_error.h"
33
#include "cpl_float.h"
34
#include "cpl_progress.h"
35
#include "cpl_vsi.h"
36
#include "cpl_worker_thread_pool.h"
37
#include "gdal.h"
38
#include "gdal_thread_pool.h"
39
#include "gdalwarper.h"
40
#include "gdal_vrt.h"
41
#include "vrtdataset.h"
42
43
#ifdef USE_NEON_OPTIMIZATIONS
44
#include "include_sse2neon.h"
45
46
#if (!defined(__aarch64__) && !defined(_M_ARM64))
47
#define ARM_V7
48
#endif
49
50
#define USE_SSE2
51
52
#include "gdalsse_priv.h"
53
54
// Restrict to 64bit processors because they are guaranteed to have SSE2,
55
// or if __AVX2__ is defined.
56
#elif defined(__x86_64) || defined(_M_X64) || defined(__AVX2__)
57
#define USE_SSE2
58
59
#include "gdalsse_priv.h"
60
61
#ifdef __SSE3__
62
#include <pmmintrin.h>
63
#endif
64
#ifdef __SSSE3__
65
#include <tmmintrin.h>
66
#endif
67
#ifdef __SSE4_1__
68
#include <smmintrin.h>
69
#endif
70
#ifdef __AVX2__
71
#include <immintrin.h>
72
#endif
73
74
#endif
75
76
// To be included after above USE_SSE2 and include gdalsse_priv.h
77
// to avoid build issue on Windows x86
78
#include "gdal_priv_templates.hpp"
79
80
/************************************************************************/
81
/*                       GDALResampleChunk_Near()                       */
82
/************************************************************************/
83
84
template <class T>
85
static CPLErr GDALResampleChunk_NearT(const GDALOverviewResampleArgs &args,
86
                                      const T *pChunk, T **ppDstBuffer)
87
88
0
{
89
0
    const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
90
0
    const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
91
0
    const GDALDataType eWrkDataType = args.eWrkDataType;
92
0
    const int nChunkXOff = args.nChunkXOff;
93
0
    const int nChunkXSize = args.nChunkXSize;
94
0
    const int nChunkYOff = args.nChunkYOff;
95
0
    const int nDstXOff = args.nDstXOff;
96
0
    const int nDstXOff2 = args.nDstXOff2;
97
0
    const int nDstYOff = args.nDstYOff;
98
0
    const int nDstYOff2 = args.nDstYOff2;
99
0
    const int nDstXWidth = nDstXOff2 - nDstXOff;
100
101
    /* -------------------------------------------------------------------- */
102
    /*      Allocate buffers.                                               */
103
    /* -------------------------------------------------------------------- */
104
0
    *ppDstBuffer = static_cast<T *>(
105
0
        VSI_MALLOC3_VERBOSE(nDstXWidth, nDstYOff2 - nDstYOff,
106
0
                            GDALGetDataTypeSizeBytes(eWrkDataType)));
107
0
    if (*ppDstBuffer == nullptr)
108
0
    {
109
0
        return CE_Failure;
110
0
    }
111
0
    T *const pDstBuffer = *ppDstBuffer;
112
113
0
    int *panSrcXOff =
114
0
        static_cast<int *>(VSI_MALLOC2_VERBOSE(nDstXWidth, sizeof(int)));
115
116
0
    if (panSrcXOff == nullptr)
117
0
    {
118
0
        return CE_Failure;
119
0
    }
120
121
    /* ==================================================================== */
122
    /*      Precompute inner loop constants.                                */
123
    /* ==================================================================== */
124
0
    for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
125
0
    {
126
0
        int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
127
0
        if (nSrcXOff < nChunkXOff)
128
0
            nSrcXOff = nChunkXOff;
129
130
0
        panSrcXOff[iDstPixel - nDstXOff] = nSrcXOff;
131
0
    }
132
133
    /* ==================================================================== */
134
    /*      Loop over destination scanlines.                                */
135
    /* ==================================================================== */
136
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
137
0
    {
138
0
        int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
139
0
        if (nSrcYOff < nChunkYOff)
140
0
            nSrcYOff = nChunkYOff;
141
142
0
        const T *const pSrcScanline =
143
0
            pChunk +
144
0
            (static_cast<size_t>(nSrcYOff - nChunkYOff) * nChunkXSize) -
145
0
            nChunkXOff;
146
147
        /* --------------------------------------------------------------------
148
         */
149
        /*      Loop over destination pixels */
150
        /* --------------------------------------------------------------------
151
         */
152
0
        T *pDstScanline =
153
0
            pDstBuffer + static_cast<size_t>(iDstLine - nDstYOff) * nDstXWidth;
154
0
        for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
155
0
        {
156
0
            pDstScanline[iDstPixel] = pSrcScanline[panSrcXOff[iDstPixel]];
157
0
        }
158
0
    }
159
160
0
    CPLFree(panSrcXOff);
161
162
0
    return CE_None;
163
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_NearT<unsigned char>(GDALOverviewResampleArgs const&, unsigned char const*, unsigned char**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_NearT<unsigned short>(GDALOverviewResampleArgs const&, unsigned short const*, unsigned short**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_NearT<unsigned int>(GDALOverviewResampleArgs const&, unsigned int const*, unsigned int**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_NearT<unsigned long>(GDALOverviewResampleArgs const&, unsigned long const*, unsigned long**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_NearT<std::__1::complex<double> >(GDALOverviewResampleArgs const&, std::__1::complex<double> const*, std::__1::complex<double>**)
164
165
static CPLErr GDALResampleChunk_Near(const GDALOverviewResampleArgs &args,
166
                                     const void *pChunk, void **ppDstBuffer,
167
                                     GDALDataType *peDstBufferDataType)
168
0
{
169
0
    *peDstBufferDataType = args.eWrkDataType;
170
0
    switch (args.eWrkDataType)
171
0
    {
172
        // For nearest resampling, as no computation is done, only the
173
        // size of the data type matters.
174
0
        case GDT_UInt8:
175
0
        case GDT_Int8:
176
0
        {
177
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 1);
178
0
            return GDALResampleChunk_NearT(
179
0
                args, static_cast<const uint8_t *>(pChunk),
180
0
                reinterpret_cast<uint8_t **>(ppDstBuffer));
181
0
        }
182
183
0
        case GDT_Int16:
184
0
        case GDT_UInt16:
185
0
        case GDT_Float16:
186
0
        {
187
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 2);
188
0
            return GDALResampleChunk_NearT(
189
0
                args, static_cast<const uint16_t *>(pChunk),
190
0
                reinterpret_cast<uint16_t **>(ppDstBuffer));
191
0
        }
192
193
0
        case GDT_CInt16:
194
0
        case GDT_CFloat16:
195
0
        case GDT_Int32:
196
0
        case GDT_UInt32:
197
0
        case GDT_Float32:
198
0
        {
199
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 4);
200
0
            return GDALResampleChunk_NearT(
201
0
                args, static_cast<const uint32_t *>(pChunk),
202
0
                reinterpret_cast<uint32_t **>(ppDstBuffer));
203
0
        }
204
205
0
        case GDT_CInt32:
206
0
        case GDT_CFloat32:
207
0
        case GDT_Int64:
208
0
        case GDT_UInt64:
209
0
        case GDT_Float64:
210
0
        {
211
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 8);
212
0
            return GDALResampleChunk_NearT(
213
0
                args, static_cast<const uint64_t *>(pChunk),
214
0
                reinterpret_cast<uint64_t **>(ppDstBuffer));
215
0
        }
216
217
0
        case GDT_CFloat64:
218
0
        {
219
0
            return GDALResampleChunk_NearT(
220
0
                args, static_cast<const std::complex<double> *>(pChunk),
221
0
                reinterpret_cast<std::complex<double> **>(ppDstBuffer));
222
0
        }
223
224
0
        case GDT_Unknown:
225
0
        case GDT_TypeCount:
226
0
            break;
227
0
    }
228
0
    CPLAssert(false);
229
0
    return CE_Failure;
230
0
}
231
232
namespace
233
{
234
235
// Find in the color table the entry whose RGB value is the closest
236
// (using quadratic distance) to the test color, ignoring transparent entries.
237
int BestColorEntry(const std::vector<GDALColorEntry> &entries,
238
                   const GDALColorEntry &test)
239
0
{
240
0
    int nMinDist = std::numeric_limits<int>::max();
241
0
    size_t bestEntry = 0;
242
0
    for (size_t i = 0; i < entries.size(); ++i)
243
0
    {
244
0
        const GDALColorEntry &entry = entries[i];
245
        // Ignore transparent entries
246
0
        if (entry.c4 == 0)
247
0
            continue;
248
249
0
        int nDist = ((test.c1 - entry.c1) * (test.c1 - entry.c1)) +
250
0
                    ((test.c2 - entry.c2) * (test.c2 - entry.c2)) +
251
0
                    ((test.c3 - entry.c3) * (test.c3 - entry.c3));
252
0
        if (nDist < nMinDist)
253
0
        {
254
0
            nMinDist = nDist;
255
0
            bestEntry = i;
256
0
        }
257
0
    }
258
0
    return static_cast<int>(bestEntry);
259
0
}
260
261
std::vector<GDALColorEntry> ReadColorTable(const GDALColorTable &table,
262
                                           int &transparentIdx)
263
0
{
264
0
    std::vector<GDALColorEntry> entries(table.GetColorEntryCount());
265
266
0
    transparentIdx = -1;
267
0
    int i = 0;
268
0
    for (auto &entry : entries)
269
0
    {
270
0
        table.GetColorEntryAsRGB(i, &entry);
271
0
        if (transparentIdx < 0 && entry.c4 == 0)
272
0
            transparentIdx = i;
273
0
        ++i;
274
0
    }
275
0
    return entries;
276
0
}
277
278
}  // unnamed  namespace
279
280
/************************************************************************/
281
/*                               SQUARE()                               */
282
/************************************************************************/
283
284
template <class T, class Tsquare = T> inline Tsquare SQUARE(T val)
285
0
{
286
0
    return static_cast<Tsquare>(val) * val;
287
0
}
Unexecuted instantiation: int SQUARE<int, int>(int)
Unexecuted instantiation: double SQUARE<double, double>(double)
Unexecuted instantiation: float SQUARE<float, float>(float)
288
289
/************************************************************************/
290
/*                         ComputeIntegerRMS()                          */
291
/************************************************************************/
292
// Compute rms = sqrt(sumSquares / weight) in such a way that it is the
293
// integer that minimizes abs(rms**2 - sumSquares / weight)
294
template <class T, class Twork>
295
inline T ComputeIntegerRMS(double sumSquares, double weight)
296
0
{
297
0
    const double sumDivWeight = sumSquares / weight;
298
0
    T rms = static_cast<T>(sqrt(sumDivWeight));
299
300
    // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
301
    // Naive version:
302
    // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
303
0
    if (static_cast<double>(static_cast<Twork>(2) * rms * (rms + 1) + 1) <
304
0
        2 * sumDivWeight)
305
0
        rms += 1;
306
0
    return rms;
307
0
}
Unexecuted instantiation: unsigned char ComputeIntegerRMS<unsigned char, int>(double, double)
Unexecuted instantiation: unsigned short ComputeIntegerRMS<unsigned short, unsigned long>(double, double)
308
309
template <class T, class Tsum> inline T ComputeIntegerRMS_4values(Tsum)
310
{
311
    CPLAssert(false);
312
    return 0;
313
}
314
315
template <> inline GByte ComputeIntegerRMS_4values<GByte, int>(int sumSquares)
316
0
{
317
    // It has been verified that given the correction on rms below, using
318
    // sqrt((float)((sumSquares + 1)/ 4)) or sqrt((float)sumSquares * 0.25f)
319
    // is equivalent, so use the former as it is used twice.
320
0
    const int sumSquaresPlusOneDiv4 = (sumSquares + 1) / 4;
321
0
    const float sumDivWeight = static_cast<float>(sumSquaresPlusOneDiv4);
322
0
    GByte rms = static_cast<GByte>(std::sqrt(sumDivWeight));
323
324
    // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
325
    // Naive version:
326
    // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
327
    // Optimized version for integer case and weight == 4
328
0
    if (static_cast<int>(rms) * (rms + 1) < sumSquaresPlusOneDiv4)
329
0
        rms += 1;
330
0
    return rms;
331
0
}
332
333
template <>
334
inline GUInt16 ComputeIntegerRMS_4values<GUInt16, double>(double sumSquares)
335
0
{
336
0
    const double sumDivWeight = sumSquares * 0.25;
337
0
    GUInt16 rms = static_cast<GUInt16>(std::sqrt(sumDivWeight));
338
339
    // Is rms**2 or (rms+1)**2 closest to sumSquares / weight ?
340
    // Naive version:
341
    // if( weight * (rms+1)**2 - sumSquares < sumSquares - weight * rms**2 )
342
    // Optimized version for integer case and weight == 4
343
0
    if (static_cast<GUInt32>(rms) * (rms + 1) <
344
0
        static_cast<GUInt32>(sumDivWeight + 0.25))
345
0
        rms += 1;
346
0
    return rms;
347
0
}
348
349
#ifdef USE_SSE2
350
351
/************************************************************************/
352
/*                    QuadraticMeanByteSSE2OrAVX2()                     */
353
/************************************************************************/
354
355
#if defined(__SSSE3__) || defined(USE_NEON_OPTIMIZATIONS)
356
#define sse2_hadd_epi16 _mm_hadd_epi16
357
#else
358
inline __m128i sse2_hadd_epi16(__m128i a, __m128i b)
359
0
{
360
    // Horizontal addition of adjacent pairs
361
0
    const auto mask = _mm_set1_epi32(0xFFFF);
362
0
    const auto horizLo =
363
0
        _mm_add_epi32(_mm_and_si128(a, mask), _mm_srli_epi32(a, 16));
364
0
    const auto horizHi =
365
0
        _mm_add_epi32(_mm_and_si128(b, mask), _mm_srli_epi32(b, 16));
366
367
    // Recombine low and high parts
368
0
    return _mm_packs_epi32(horizLo, horizHi);
369
0
}
370
#endif
371
372
#ifdef __AVX2__
373
374
#define set1_epi16 _mm256_set1_epi16
375
#define set1_epi32 _mm256_set1_epi32
376
#define setzero _mm256_setzero_si256
377
#define set1_ps _mm256_set1_ps
378
#define loadu_int(x) _mm256_loadu_si256(reinterpret_cast<__m256i const *>(x))
379
#define unpacklo_epi8 _mm256_unpacklo_epi8
380
#define unpackhi_epi8 _mm256_unpackhi_epi8
381
#define madd_epi16 _mm256_madd_epi16
382
#define add_epi32 _mm256_add_epi32
383
#define mul_ps _mm256_mul_ps
384
#define cvtepi32_ps _mm256_cvtepi32_ps
385
#define sqrt_ps _mm256_sqrt_ps
386
#define cvttps_epi32 _mm256_cvttps_epi32
387
#define packs_epi32 _mm256_packs_epi32
388
#define packus_epi32 _mm256_packus_epi32
389
#define srli_epi32 _mm256_srli_epi32
390
#define mullo_epi16 _mm256_mullo_epi16
391
#define srli_epi16 _mm256_srli_epi16
392
#define cmpgt_epi16 _mm256_cmpgt_epi16
393
#define add_epi16 _mm256_add_epi16
394
#define sub_epi16 _mm256_sub_epi16
395
#define packus_epi16 _mm256_packus_epi16
396
397
/* AVX2 operates on 2 separate 128-bit lanes, so we have to do shuffling */
398
/* to get the lower 128-bit bits of what would be a true 256-bit vector register
399
 */
400
401
inline __m256i FIXUP_LANES(__m256i x)
402
{
403
    return _mm256_permute4x64_epi64(x, _MM_SHUFFLE(3, 1, 2, 0));
404
}
405
406
#define store_lo(x, y)                                                         \
407
    _mm_storeu_si128(reinterpret_cast<__m128i *>(x),                           \
408
                     _mm256_extracti128_si256(FIXUP_LANES(y), 0))
409
#define storeu_int(x, y)                                                       \
410
    _mm256_storeu_si256(reinterpret_cast<__m256i *>(x), FIXUP_LANES(y))
411
#define hadd_epi16 _mm256_hadd_epi16
412
#else
413
0
#define set1_epi16 _mm_set1_epi16
414
0
#define set1_epi32 _mm_set1_epi32
415
0
#define setzero _mm_setzero_si128
416
#define set1_ps _mm_set1_ps
417
0
#define loadu_int(x) _mm_loadu_si128(reinterpret_cast<__m128i const *>(x))
418
0
#define unpacklo_epi8 _mm_unpacklo_epi8
419
0
#define unpackhi_epi8 _mm_unpackhi_epi8
420
0
#define madd_epi16 _mm_madd_epi16
421
0
#define add_epi32 _mm_add_epi32
422
#define mul_ps _mm_mul_ps
423
0
#define cvtepi32_ps _mm_cvtepi32_ps
424
0
#define sqrt_ps _mm_sqrt_ps
425
0
#define cvttps_epi32 _mm_cvttps_epi32
426
0
#define packs_epi32 _mm_packs_epi32
427
0
#define packus_epi32 GDAL_mm_packus_epi32
428
0
#define srli_epi32 _mm_srli_epi32
429
0
#define mullo_epi16 _mm_mullo_epi16
430
0
#define srli_epi16 _mm_srli_epi16
431
0
#define cmpgt_epi16 _mm_cmpgt_epi16
432
0
#define add_epi16 _mm_add_epi16
433
0
#define sub_epi16 _mm_sub_epi16
434
0
#define packus_epi16 _mm_packus_epi16
435
0
#define store_lo(x, y) _mm_storel_epi64(reinterpret_cast<__m128i *>(x), (y))
436
0
#define storeu_int(x, y) _mm_storeu_si128(reinterpret_cast<__m128i *>(x), (y))
437
0
#define hadd_epi16 sse2_hadd_epi16
438
#endif
439
440
template <class T>
441
static int
442
#if defined(__GNUC__)
443
    __attribute__((noinline))
444
#endif
445
    QuadraticMeanByteSSE2OrAVX2(int nDstXWidth, int nChunkXSize,
446
                                const T *&CPL_RESTRICT pSrcScanlineShiftedInOut,
447
                                T *CPL_RESTRICT pDstScanline)
448
0
{
449
    // Optimized implementation for RMS on Byte by
450
    // processing by group of 8 output pixels, so as to use
451
    // a single _mm_sqrt_ps() call for 4 output pixels
452
0
    const T *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
453
454
0
    int iDstPixel = 0;
455
0
    const auto one16 = set1_epi16(1);
456
0
    const auto one32 = set1_epi32(1);
457
0
    const auto zero = setzero();
458
0
    const auto minus32768 = set1_epi16(-32768);
459
460
0
    constexpr int DEST_ELTS = static_cast<int>(sizeof(zero)) / 2;
461
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
462
0
    {
463
        // Load 2 * DEST_ELTS bytes from each line
464
0
        auto firstLine = loadu_int(pSrcScanlineShifted);
465
0
        auto secondLine = loadu_int(pSrcScanlineShifted + nChunkXSize);
466
        // Extend those Bytes as UInt16s
467
0
        auto firstLineLo = unpacklo_epi8(firstLine, zero);
468
0
        auto firstLineHi = unpackhi_epi8(firstLine, zero);
469
0
        auto secondLineLo = unpacklo_epi8(secondLine, zero);
470
0
        auto secondLineHi = unpackhi_epi8(secondLine, zero);
471
472
        // Multiplication of 16 bit values and horizontal
473
        // addition of 32 bit results
474
        // [ src[2*i+0]^2 + src[2*i+1]^2 for i in range(4) ]
475
0
        firstLineLo = madd_epi16(firstLineLo, firstLineLo);
476
0
        firstLineHi = madd_epi16(firstLineHi, firstLineHi);
477
0
        secondLineLo = madd_epi16(secondLineLo, secondLineLo);
478
0
        secondLineHi = madd_epi16(secondLineHi, secondLineHi);
479
480
        // Vertical addition
481
0
        const auto sumSquaresLo = add_epi32(firstLineLo, secondLineLo);
482
0
        const auto sumSquaresHi = add_epi32(firstLineHi, secondLineHi);
483
484
0
        const auto sumSquaresPlusOneDiv4Lo =
485
0
            srli_epi32(add_epi32(sumSquaresLo, one32), 2);
486
0
        const auto sumSquaresPlusOneDiv4Hi =
487
0
            srli_epi32(add_epi32(sumSquaresHi, one32), 2);
488
489
        // Take square root and truncate/floor to int32
490
0
        const auto rmsLo =
491
0
            cvttps_epi32(sqrt_ps(cvtepi32_ps(sumSquaresPlusOneDiv4Lo)));
492
0
        const auto rmsHi =
493
0
            cvttps_epi32(sqrt_ps(cvtepi32_ps(sumSquaresPlusOneDiv4Hi)));
494
495
        // Merge back low and high registers with each RMS value
496
        // as a 16 bit value.
497
0
        auto rms = packs_epi32(rmsLo, rmsHi);
498
499
        // Round to upper value if it minimizes the
500
        // error |rms^2 - sumSquares/4|
501
        // if( 2 * (2 * rms * (rms + 1) + 1) < sumSquares )
502
        //    rms += 1;
503
        // which is equivalent to:
504
        // if( rms * (rms + 1) < (sumSquares+1) / 4 )
505
        //    rms += 1;
506
        // And both left and right parts fit on 16 (unsigned) bits
507
0
        const auto sumSquaresPlusOneDiv4 =
508
0
            packus_epi32(sumSquaresPlusOneDiv4Lo, sumSquaresPlusOneDiv4Hi);
509
        // cmpgt_epi16 operates on signed int16, but here
510
        // we have unsigned values, so shift them by -32768 before
511
0
        const auto mask = cmpgt_epi16(
512
0
            add_epi16(sumSquaresPlusOneDiv4, minus32768),
513
0
            add_epi16(mullo_epi16(rms, add_epi16(rms, one16)), minus32768));
514
        // The value of the mask will be -1 when the correction needs to be
515
        // applied
516
0
        rms = sub_epi16(rms, mask);
517
518
        // Pack each 16 bit RMS value to 8 bits
519
0
        rms = packus_epi16(rms, rms /* could be anything */);
520
0
        store_lo(&pDstScanline[iDstPixel], rms);
521
0
        pSrcScanlineShifted += 2 * DEST_ELTS;
522
0
    }
523
524
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
525
0
    return iDstPixel;
526
0
}
527
528
/************************************************************************/
529
/*                       AverageByteSSE2OrAVX2()                        */
530
/************************************************************************/
531
532
static int
533
AverageByteSSE2OrAVX2(int nDstXWidth, int nChunkXSize,
534
                      const GByte *&CPL_RESTRICT pSrcScanlineShiftedInOut,
535
                      GByte *CPL_RESTRICT pDstScanline)
536
0
{
537
    // Optimized implementation for average on Byte by
538
    // processing by group of 16 output pixels for SSE2, or 32 for AVX2
539
540
0
    const auto zero = setzero();
541
0
    const auto two16 = set1_epi16(2);
542
0
    const GByte *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
543
544
0
    constexpr int DEST_ELTS = static_cast<int>(sizeof(zero)) / 2;
545
0
    int iDstPixel = 0;
546
0
    for (; iDstPixel < nDstXWidth - (2 * DEST_ELTS - 1);
547
0
         iDstPixel += 2 * DEST_ELTS)
548
0
    {
549
0
        decltype(setzero()) average0;
550
0
        {
551
            // Load 2 * DEST_ELTS bytes from each line
552
0
            const auto firstLine = loadu_int(pSrcScanlineShifted);
553
0
            const auto secondLine =
554
0
                loadu_int(pSrcScanlineShifted + nChunkXSize);
555
            // Extend those Bytes as UInt16s
556
0
            const auto firstLineLo = unpacklo_epi8(firstLine, zero);
557
0
            const auto firstLineHi = unpackhi_epi8(firstLine, zero);
558
0
            const auto secondLineLo = unpacklo_epi8(secondLine, zero);
559
0
            const auto secondLineHi = unpackhi_epi8(secondLine, zero);
560
561
            // Vertical addition
562
0
            const auto sumLo = add_epi16(firstLineLo, secondLineLo);
563
0
            const auto sumHi = add_epi16(firstLineHi, secondLineHi);
564
565
            // Horizontal addition of adjacent pairs, and recombine low and high
566
            // parts
567
0
            const auto sum = hadd_epi16(sumLo, sumHi);
568
569
            // average = (sum + 2) / 4
570
0
            average0 = srli_epi16(add_epi16(sum, two16), 2);
571
572
0
            pSrcScanlineShifted += 2 * DEST_ELTS;
573
0
        }
574
575
0
        decltype(setzero()) average1;
576
0
        {
577
            // Load 2 * DEST_ELTS bytes from each line
578
0
            const auto firstLine = loadu_int(pSrcScanlineShifted);
579
0
            const auto secondLine =
580
0
                loadu_int(pSrcScanlineShifted + nChunkXSize);
581
            // Extend those Bytes as UInt16s
582
0
            const auto firstLineLo = unpacklo_epi8(firstLine, zero);
583
0
            const auto firstLineHi = unpackhi_epi8(firstLine, zero);
584
0
            const auto secondLineLo = unpacklo_epi8(secondLine, zero);
585
0
            const auto secondLineHi = unpackhi_epi8(secondLine, zero);
586
587
            // Vertical addition
588
0
            const auto sumLo = add_epi16(firstLineLo, secondLineLo);
589
0
            const auto sumHi = add_epi16(firstLineHi, secondLineHi);
590
591
            // Horizontal addition of adjacent pairs, and recombine low and high
592
            // parts
593
0
            const auto sum = hadd_epi16(sumLo, sumHi);
594
595
            // average = (sum + 2) / 4
596
0
            average1 = srli_epi16(add_epi16(sum, two16), 2);
597
598
0
            pSrcScanlineShifted += 2 * DEST_ELTS;
599
0
        }
600
601
        // Pack each 16 bit average value to 8 bits
602
0
        const auto average = packus_epi16(average0, average1);
603
0
        storeu_int(&pDstScanline[iDstPixel], average);
604
0
    }
605
606
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
607
0
    return iDstPixel;
608
0
}
609
610
/************************************************************************/
611
/*                      QuadraticMeanUInt16SSE2()                       */
612
/************************************************************************/
613
614
#ifdef __SSE3__
615
#define sse2_hadd_pd _mm_hadd_pd
616
#else
617
inline __m128d sse2_hadd_pd(__m128d a, __m128d b)
618
0
{
619
0
    auto aLo_bLo =
620
0
        _mm_castps_pd(_mm_movelh_ps(_mm_castpd_ps(a), _mm_castpd_ps(b)));
621
0
    auto aHi_bHi =
622
0
        _mm_castps_pd(_mm_movehl_ps(_mm_castpd_ps(b), _mm_castpd_ps(a)));
623
0
    return _mm_add_pd(aLo_bLo, aHi_bHi);  // (aLo + aHi, bLo + bHi)
624
0
}
625
#endif
626
627
inline __m128d SQUARE_PD(__m128d x)
628
0
{
629
0
    return _mm_mul_pd(x, x);
630
0
}
631
632
#ifdef __AVX2__
633
634
inline __m256d SQUARE_PD(__m256d x)
635
{
636
    return _mm256_mul_pd(x, x);
637
}
638
639
inline __m256d FIXUP_LANES(__m256d x)
640
{
641
    return _mm256_permute4x64_pd(x, _MM_SHUFFLE(3, 1, 2, 0));
642
}
643
644
inline __m256 FIXUP_LANES(__m256 x)
645
{
646
    return _mm256_castpd_ps(FIXUP_LANES(_mm256_castps_pd(x)));
647
}
648
649
#endif
650
651
static int
652
QuadraticMeanUInt16SSE2(int nDstXWidth, int nChunkXSize,
653
                        const uint16_t *&CPL_RESTRICT pSrcScanlineShiftedInOut,
654
                        uint16_t *CPL_RESTRICT pDstScanline)
655
0
{
656
    // Optimized implementation for RMS on UInt16 by
657
    // processing by group of 4 output pixels.
658
0
    const uint16_t *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
659
660
0
    int iDstPixel = 0;
661
0
    const auto zero = _mm_setzero_si128();
662
663
#ifdef __AVX2__
664
    const auto zeroDot25 = _mm256_set1_pd(0.25);
665
    const auto zeroDot5 = _mm256_set1_pd(0.5);
666
667
    // The first four 0's could be anything, as we only take the bottom
668
    // 128 bits.
669
    const auto permutation = _mm256_set_epi32(0, 0, 0, 0, 6, 4, 2, 0);
670
#else
671
0
    const auto zeroDot25 = _mm_set1_pd(0.25);
672
0
    const auto zeroDot5 = _mm_set1_pd(0.5);
673
0
#endif
674
675
0
    constexpr int DEST_ELTS =
676
0
        static_cast<int>(sizeof(zero) / sizeof(uint16_t)) / 2;
677
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
678
0
    {
679
        // Load 8 UInt16 from each line
680
0
        const auto firstLine = _mm_loadu_si128(
681
0
            reinterpret_cast<__m128i const *>(pSrcScanlineShifted));
682
0
        const auto secondLine =
683
0
            _mm_loadu_si128(reinterpret_cast<__m128i const *>(
684
0
                pSrcScanlineShifted + nChunkXSize));
685
686
        // Detect if all of the source values fit in 14 bits.
687
        // because if x < 2^14, then 4 * x^2 < 2^30 which fits in a signed int32
688
        // and we can do a much faster implementation.
689
0
        const auto maskTmp =
690
0
            _mm_srli_epi16(_mm_or_si128(firstLine, secondLine), 14);
691
#if defined(__i386__) || defined(_M_IX86)
692
        uint64_t nMaskFitsIn14Bits = 0;
693
        _mm_storel_epi64(
694
            reinterpret_cast<__m128i *>(&nMaskFitsIn14Bits),
695
            _mm_packus_epi16(maskTmp, maskTmp /* could be anything */));
696
#else
697
0
        const auto nMaskFitsIn14Bits = _mm_cvtsi128_si64(
698
0
            _mm_packus_epi16(maskTmp, maskTmp /* could be anything */));
699
0
#endif
700
0
        if (nMaskFitsIn14Bits == 0)
701
0
        {
702
            // Multiplication of 16 bit values and horizontal
703
            // addition of 32 bit results
704
0
            const auto firstLineHSumSquare =
705
0
                _mm_madd_epi16(firstLine, firstLine);
706
0
            const auto secondLineHSumSquare =
707
0
                _mm_madd_epi16(secondLine, secondLine);
708
            // Vertical addition
709
0
            const auto sumSquares =
710
0
                _mm_add_epi32(firstLineHSumSquare, secondLineHSumSquare);
711
            // In theory we should take sqrt(sumSquares * 0.25f)
712
            // but given the rounding we do, this is equivalent to
713
            // sqrt((sumSquares + 1)/4). This has been verified exhaustively for
714
            // sumSquares <= 4 * 16383^2
715
0
            const auto one32 = _mm_set1_epi32(1);
716
0
            const auto sumSquaresPlusOneDiv4 =
717
0
                _mm_srli_epi32(_mm_add_epi32(sumSquares, one32), 2);
718
            // Take square root and truncate/floor to int32
719
0
            auto rms = _mm_cvttps_epi32(
720
0
                _mm_sqrt_ps(_mm_cvtepi32_ps(sumSquaresPlusOneDiv4)));
721
722
            // Round to upper value if it minimizes the
723
            // error |rms^2 - sumSquares/4|
724
            // if( 2 * (2 * rms * (rms + 1) + 1) < sumSquares )
725
            //    rms += 1;
726
            // which is equivalent to:
727
            // if( rms * rms + rms < (sumSquares+1) / 4 )
728
            //    rms += 1;
729
0
            auto mask =
730
0
                _mm_cmpgt_epi32(sumSquaresPlusOneDiv4,
731
0
                                _mm_add_epi32(_mm_madd_epi16(rms, rms), rms));
732
0
            rms = _mm_sub_epi32(rms, mask);
733
            // Pack each 32 bit RMS value to 16 bits
734
0
            rms = _mm_packs_epi32(rms, rms /* could be anything */);
735
0
            _mm_storel_epi64(
736
0
                reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]), rms);
737
0
            pSrcScanlineShifted += 2 * DEST_ELTS;
738
0
            continue;
739
0
        }
740
741
        // An approach using _mm_mullo_epi16, _mm_mulhi_epu16 before extending
742
        // to 32 bit would result in 4 multiplications instead of 8, but
743
        // mullo/mulhi have a worse throughput than mul_pd.
744
745
        // Extend those UInt16s as UInt32s
746
0
        const auto firstLineLo = _mm_unpacklo_epi16(firstLine, zero);
747
0
        const auto firstLineHi = _mm_unpackhi_epi16(firstLine, zero);
748
0
        const auto secondLineLo = _mm_unpacklo_epi16(secondLine, zero);
749
0
        const auto secondLineHi = _mm_unpackhi_epi16(secondLine, zero);
750
751
#ifdef __AVX2__
752
        // Multiplication of 32 bit values previously converted to 64 bit double
753
        const auto firstLineLoDbl = SQUARE_PD(_mm256_cvtepi32_pd(firstLineLo));
754
        const auto firstLineHiDbl = SQUARE_PD(_mm256_cvtepi32_pd(firstLineHi));
755
        const auto secondLineLoDbl =
756
            SQUARE_PD(_mm256_cvtepi32_pd(secondLineLo));
757
        const auto secondLineHiDbl =
758
            SQUARE_PD(_mm256_cvtepi32_pd(secondLineHi));
759
760
        // Vertical addition of squares
761
        const auto sumSquaresLo =
762
            _mm256_add_pd(firstLineLoDbl, secondLineLoDbl);
763
        const auto sumSquaresHi =
764
            _mm256_add_pd(firstLineHiDbl, secondLineHiDbl);
765
766
        // Horizontal addition of squares
767
        const auto sumSquares =
768
            FIXUP_LANES(_mm256_hadd_pd(sumSquaresLo, sumSquaresHi));
769
770
        const auto sumDivWeight = _mm256_mul_pd(sumSquares, zeroDot25);
771
772
        // Take square root and truncate/floor to int32
773
        auto rms = _mm256_cvttpd_epi32(_mm256_sqrt_pd(sumDivWeight));
774
        const auto rmsDouble = _mm256_cvtepi32_pd(rms);
775
        const auto right = _mm256_sub_pd(
776
            sumDivWeight, _mm256_add_pd(SQUARE_PD(rmsDouble), rmsDouble));
777
778
        auto mask =
779
            _mm256_castpd_ps(_mm256_cmp_pd(zeroDot5, right, _CMP_LT_OS));
780
        // Extract 32-bit from each of the 4 64-bit masks
781
        // mask = FIXUP_LANES(_mm256_shuffle_ps(mask, mask,
782
        // _MM_SHUFFLE(2,0,2,0)));
783
        mask = _mm256_permutevar8x32_ps(mask, permutation);
784
        const auto maskI = _mm_castps_si128(_mm256_extractf128_ps(mask, 0));
785
786
        // Apply the correction
787
        rms = _mm_sub_epi32(rms, maskI);
788
789
        // Pack each 32 bit RMS value to 16 bits
790
        rms = _mm_packus_epi32(rms, rms /* could be anything */);
791
#else
792
        // Multiplication of 32 bit values previously converted to 64 bit double
793
0
        const auto firstLineLoLo = SQUARE_PD(_mm_cvtepi32_pd(firstLineLo));
794
0
        const auto firstLineLoHi =
795
0
            SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(firstLineLo, 8)));
796
0
        const auto firstLineHiLo = SQUARE_PD(_mm_cvtepi32_pd(firstLineHi));
797
0
        const auto firstLineHiHi =
798
0
            SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(firstLineHi, 8)));
799
800
0
        const auto secondLineLoLo = SQUARE_PD(_mm_cvtepi32_pd(secondLineLo));
801
0
        const auto secondLineLoHi =
802
0
            SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(secondLineLo, 8)));
803
0
        const auto secondLineHiLo = SQUARE_PD(_mm_cvtepi32_pd(secondLineHi));
804
0
        const auto secondLineHiHi =
805
0
            SQUARE_PD(_mm_cvtepi32_pd(_mm_srli_si128(secondLineHi, 8)));
806
807
        // Vertical addition of squares
808
0
        const auto sumSquaresLoLo = _mm_add_pd(firstLineLoLo, secondLineLoLo);
809
0
        const auto sumSquaresLoHi = _mm_add_pd(firstLineLoHi, secondLineLoHi);
810
0
        const auto sumSquaresHiLo = _mm_add_pd(firstLineHiLo, secondLineHiLo);
811
0
        const auto sumSquaresHiHi = _mm_add_pd(firstLineHiHi, secondLineHiHi);
812
813
        // Horizontal addition of squares
814
0
        const auto sumSquaresLo = sse2_hadd_pd(sumSquaresLoLo, sumSquaresLoHi);
815
0
        const auto sumSquaresHi = sse2_hadd_pd(sumSquaresHiLo, sumSquaresHiHi);
816
817
0
        const auto sumDivWeightLo = _mm_mul_pd(sumSquaresLo, zeroDot25);
818
0
        const auto sumDivWeightHi = _mm_mul_pd(sumSquaresHi, zeroDot25);
819
        // Take square root and truncate/floor to int32
820
0
        const auto rmsLo = _mm_cvttpd_epi32(_mm_sqrt_pd(sumDivWeightLo));
821
0
        const auto rmsHi = _mm_cvttpd_epi32(_mm_sqrt_pd(sumDivWeightHi));
822
823
        // Correctly round rms to minimize | rms^2 - sumSquares / 4 |
824
        // if( 0.5 < sumDivWeight - (rms * rms + rms) )
825
        //     rms += 1;
826
0
        const auto rmsLoDouble = _mm_cvtepi32_pd(rmsLo);
827
0
        const auto rmsHiDouble = _mm_cvtepi32_pd(rmsHi);
828
0
        const auto rightLo = _mm_sub_pd(
829
0
            sumDivWeightLo, _mm_add_pd(SQUARE_PD(rmsLoDouble), rmsLoDouble));
830
0
        const auto rightHi = _mm_sub_pd(
831
0
            sumDivWeightHi, _mm_add_pd(SQUARE_PD(rmsHiDouble), rmsHiDouble));
832
833
0
        const auto maskLo = _mm_castpd_ps(_mm_cmplt_pd(zeroDot5, rightLo));
834
0
        const auto maskHi = _mm_castpd_ps(_mm_cmplt_pd(zeroDot5, rightHi));
835
        // The value of the mask will be -1 when the correction needs to be
836
        // applied
837
0
        const auto mask = _mm_castps_si128(_mm_shuffle_ps(
838
0
            maskLo, maskHi, (0 << 0) | (2 << 2) | (0 << 4) | (2 << 6)));
839
840
0
        auto rms = _mm_castps_si128(
841
0
            _mm_movelh_ps(_mm_castsi128_ps(rmsLo), _mm_castsi128_ps(rmsHi)));
842
        // Apply the correction
843
0
        rms = _mm_sub_epi32(rms, mask);
844
845
        // Pack each 32 bit RMS value to 16 bits
846
0
        rms = GDAL_mm_int32_to_uint16(rms);
847
0
#endif
848
849
0
        _mm_storel_epi64(reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]),
850
0
                         rms);
851
0
        pSrcScanlineShifted += 2 * DEST_ELTS;
852
0
    }
853
854
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
855
0
    return iDstPixel;
856
0
}
857
858
/************************************************************************/
859
/*                         AverageUInt16SSE2()                          */
860
/************************************************************************/
861
862
static int
863
AverageUInt16SSE2(int nDstXWidth, int nChunkXSize,
864
                  const uint16_t *&CPL_RESTRICT pSrcScanlineShiftedInOut,
865
                  uint16_t *CPL_RESTRICT pDstScanline)
866
0
{
867
    // Optimized implementation for average on UInt16 by
868
    // processing by group of 8 output pixels.
869
870
0
    const auto mask = _mm_set1_epi32(0xFFFF);
871
0
    const auto two = _mm_set1_epi32(2);
872
0
    const uint16_t *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
873
874
0
    int iDstPixel = 0;
875
0
    constexpr int DEST_ELTS = static_cast<int>(sizeof(mask) / sizeof(uint16_t));
876
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
877
0
    {
878
0
        __m128i averageLow;
879
        // Load 8 UInt16 from each line
880
0
        {
881
0
            const auto firstLine = _mm_loadu_si128(
882
0
                reinterpret_cast<__m128i const *>(pSrcScanlineShifted));
883
0
            const auto secondLine =
884
0
                _mm_loadu_si128(reinterpret_cast<__m128i const *>(
885
0
                    pSrcScanlineShifted + nChunkXSize));
886
887
            // Horizontal addition and extension to 32 bit
888
0
            const auto horizAddFirstLine = _mm_add_epi32(
889
0
                _mm_and_si128(firstLine, mask), _mm_srli_epi32(firstLine, 16));
890
0
            const auto horizAddSecondLine =
891
0
                _mm_add_epi32(_mm_and_si128(secondLine, mask),
892
0
                              _mm_srli_epi32(secondLine, 16));
893
894
            // Vertical addition and average computation
895
            // average = (sum + 2) >> 2
896
0
            const auto sum = _mm_add_epi32(
897
0
                _mm_add_epi32(horizAddFirstLine, horizAddSecondLine), two);
898
0
            averageLow = _mm_srli_epi32(sum, 2);
899
0
        }
900
        // Load 8 UInt16 from each line
901
0
        __m128i averageHigh;
902
0
        {
903
0
            const auto firstLine =
904
0
                _mm_loadu_si128(reinterpret_cast<__m128i const *>(
905
0
                    pSrcScanlineShifted + DEST_ELTS));
906
0
            const auto secondLine =
907
0
                _mm_loadu_si128(reinterpret_cast<__m128i const *>(
908
0
                    pSrcScanlineShifted + DEST_ELTS + nChunkXSize));
909
910
            // Horizontal addition and extension to 32 bit
911
0
            const auto horizAddFirstLine = _mm_add_epi32(
912
0
                _mm_and_si128(firstLine, mask), _mm_srli_epi32(firstLine, 16));
913
0
            const auto horizAddSecondLine =
914
0
                _mm_add_epi32(_mm_and_si128(secondLine, mask),
915
0
                              _mm_srli_epi32(secondLine, 16));
916
917
            // Vertical addition and average computation
918
            // average = (sum + 2) >> 2
919
0
            const auto sum = _mm_add_epi32(
920
0
                _mm_add_epi32(horizAddFirstLine, horizAddSecondLine), two);
921
0
            averageHigh = _mm_srli_epi32(sum, 2);
922
0
        }
923
924
        // Pack each 32 bit average value to 16 bits
925
0
        auto average = GDAL_mm_packus_epi32(averageLow, averageHigh);
926
0
        _mm_storeu_si128(reinterpret_cast<__m128i *>(&pDstScanline[iDstPixel]),
927
0
                         average);
928
0
        pSrcScanlineShifted += 2 * DEST_ELTS;
929
0
    }
930
931
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
932
0
    return iDstPixel;
933
0
}
934
935
/************************************************************************/
936
/*                       QuadraticMeanFloatSSE2()                       */
937
/************************************************************************/
938
939
#if !defined(ARM_V7)
940
941
#ifdef __SSE3__
942
#define sse2_hadd_ps _mm_hadd_ps
943
#else
944
inline __m128 sse2_hadd_ps(__m128 a, __m128 b)
945
0
{
946
0
    auto aEven_bEven = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2, 0, 2, 0));
947
0
    auto aOdd_bOdd = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3, 1, 3, 1));
948
0
    return _mm_add_ps(aEven_bEven, aOdd_bOdd);  // (aEven + aOdd, bEven + bOdd)
949
0
}
950
#endif
951
952
#ifdef __AVX2__
953
#define set1_ps _mm256_set1_ps
954
#define loadu_ps _mm256_loadu_ps
955
#define andnot_ps _mm256_andnot_ps
956
#define and_ps _mm256_and_ps
957
#define max_ps _mm256_max_ps
958
#define shuffle_ps _mm256_shuffle_ps
959
#define div_ps _mm256_div_ps
960
#define cmpeq_ps(x, y) _mm256_cmp_ps((x), (y), _CMP_EQ_OQ)
961
#define mul_ps _mm256_mul_ps
962
#define add_ps _mm256_add_ps
963
#define hadd_ps _mm256_hadd_ps
964
#define sqrt_ps _mm256_sqrt_ps
965
#define or_ps _mm256_or_ps
966
#define unpacklo_ps _mm256_unpacklo_ps
967
#define unpackhi_ps _mm256_unpackhi_ps
968
#define storeu_ps _mm256_storeu_ps
969
#define blendv_ps _mm256_blendv_ps
970
971
inline __m256 SQUARE_PS(__m256 x)
972
{
973
    return _mm256_mul_ps(x, x);
974
}
975
976
#else
977
978
0
#define set1_ps _mm_set1_ps
979
0
#define loadu_ps _mm_loadu_ps
980
0
#define andnot_ps _mm_andnot_ps
981
#define and_ps _mm_and_ps
982
0
#define max_ps _mm_max_ps
983
0
#define shuffle_ps _mm_shuffle_ps
984
0
#define div_ps _mm_div_ps
985
0
#define cmpeq_ps _mm_cmpeq_ps
986
0
#define mul_ps _mm_mul_ps
987
0
#define add_ps _mm_add_ps
988
#define hadd_ps sse2_hadd_ps
989
0
#define sqrt_ps _mm_sqrt_ps
990
#define or_ps _mm_or_ps
991
#define unpacklo_ps _mm_unpacklo_ps
992
#define unpackhi_ps _mm_unpackhi_ps
993
0
#define storeu_ps _mm_storeu_ps
994
995
inline __m128 blendv_ps(__m128 a, __m128 b, __m128 mask)
996
0
{
997
#if defined(__SSE4_1__) || defined(__AVX__) || defined(USE_NEON_OPTIMIZATIONS)
998
    return _mm_blendv_ps(a, b, mask);
999
#else
1000
0
    return _mm_or_ps(_mm_andnot_ps(mask, a), _mm_and_ps(mask, b));
1001
0
#endif
1002
0
}
1003
1004
inline __m128 SQUARE_PS(__m128 x)
1005
0
{
1006
0
    return _mm_mul_ps(x, x);
1007
0
}
1008
1009
inline __m128 FIXUP_LANES(__m128 x)
1010
0
{
1011
0
    return x;
1012
0
}
1013
1014
#endif
1015
1016
static int
1017
#if defined(__GNUC__)
1018
    __attribute__((noinline))
1019
#endif
1020
    QuadraticMeanFloatSSE2(int nDstXWidth, int nChunkXSize,
1021
                           const float *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1022
                           float *CPL_RESTRICT pDstScanline)
1023
0
{
1024
    // Optimized implementation for RMS on Float32 by
1025
    // processing by group of output pixels.
1026
0
    const float *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1027
1028
0
    int iDstPixel = 0;
1029
0
    const auto minus_zero = set1_ps(-0.0f);
1030
0
    const auto zeroDot25 = set1_ps(0.25f);
1031
0
    const auto one = set1_ps(1.0f);
1032
0
    const auto infv = set1_ps(std::numeric_limits<float>::infinity());
1033
0
    constexpr int DEST_ELTS = static_cast<int>(sizeof(one) / sizeof(float));
1034
1035
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1036
0
    {
1037
        // Load 2*DEST_ELTS Float32 from each line
1038
0
        auto firstLineLo = loadu_ps(pSrcScanlineShifted);
1039
0
        auto firstLineHi = loadu_ps(pSrcScanlineShifted + DEST_ELTS);
1040
0
        auto secondLineLo = loadu_ps(pSrcScanlineShifted + nChunkXSize);
1041
0
        auto secondLineHi =
1042
0
            loadu_ps(pSrcScanlineShifted + DEST_ELTS + nChunkXSize);
1043
1044
        // Take the absolute value
1045
0
        firstLineLo = andnot_ps(minus_zero, firstLineLo);
1046
0
        firstLineHi = andnot_ps(minus_zero, firstLineHi);
1047
0
        secondLineLo = andnot_ps(minus_zero, secondLineLo);
1048
0
        secondLineHi = andnot_ps(minus_zero, secondLineHi);
1049
1050
0
        auto firstLineEven =
1051
0
            shuffle_ps(firstLineLo, firstLineHi, _MM_SHUFFLE(2, 0, 2, 0));
1052
0
        auto firstLineOdd =
1053
0
            shuffle_ps(firstLineLo, firstLineHi, _MM_SHUFFLE(3, 1, 3, 1));
1054
0
        auto secondLineEven =
1055
0
            shuffle_ps(secondLineLo, secondLineHi, _MM_SHUFFLE(2, 0, 2, 0));
1056
0
        auto secondLineOdd =
1057
0
            shuffle_ps(secondLineLo, secondLineHi, _MM_SHUFFLE(3, 1, 3, 1));
1058
1059
        // Compute the maximum of each DEST_ELTS value to RMS-average
1060
0
        const auto maxV = max_ps(max_ps(firstLineEven, firstLineOdd),
1061
0
                                 max_ps(secondLineEven, secondLineOdd));
1062
1063
        // Normalize each value by the maximum of the DEST_ELTS ones.
1064
        // This step is important to avoid that the square evaluates to infinity
1065
        // for sufficiently big input.
1066
0
        auto invMax = div_ps(one, maxV);
1067
        // Deal with 0 being the maximum to correct division by zero
1068
        // note: comparing to -0 leads to identical results as to comparing with
1069
        // 0
1070
0
        invMax = andnot_ps(cmpeq_ps(maxV, minus_zero), invMax);
1071
1072
0
        firstLineEven = mul_ps(firstLineEven, invMax);
1073
0
        firstLineOdd = mul_ps(firstLineOdd, invMax);
1074
0
        secondLineEven = mul_ps(secondLineEven, invMax);
1075
0
        secondLineOdd = mul_ps(secondLineOdd, invMax);
1076
1077
        // Compute squares
1078
0
        firstLineEven = SQUARE_PS(firstLineEven);
1079
0
        firstLineOdd = SQUARE_PS(firstLineOdd);
1080
0
        secondLineEven = SQUARE_PS(secondLineEven);
1081
0
        secondLineOdd = SQUARE_PS(secondLineOdd);
1082
1083
0
        const auto sumSquares = add_ps(add_ps(firstLineEven, firstLineOdd),
1084
0
                                       add_ps(secondLineEven, secondLineOdd));
1085
1086
0
        auto rms = mul_ps(maxV, sqrt_ps(mul_ps(sumSquares, zeroDot25)));
1087
1088
        // Deal with infinity being the maximum
1089
0
        const auto maskIsInf = cmpeq_ps(maxV, infv);
1090
0
        rms = blendv_ps(rms, infv, maskIsInf);
1091
1092
0
        rms = FIXUP_LANES(rms);
1093
1094
0
        storeu_ps(&pDstScanline[iDstPixel], rms);
1095
0
        pSrcScanlineShifted += DEST_ELTS * 2;
1096
0
    }
1097
1098
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1099
0
    return iDstPixel;
1100
0
}
1101
1102
/************************************************************************/
1103
/*                          AverageFloatSSE2()                          */
1104
/************************************************************************/
1105
1106
static int AverageFloatSSE2(int nDstXWidth, int nChunkXSize,
1107
                            const float *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1108
                            float *CPL_RESTRICT pDstScanline)
1109
0
{
1110
    // Optimized implementation for average on Float32 by
1111
    // processing by group of output pixels.
1112
0
    const float *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1113
1114
0
    int iDstPixel = 0;
1115
0
    const auto zeroDot25 = _mm_set1_ps(0.25f);
1116
0
    constexpr int DEST_ELTS =
1117
0
        static_cast<int>(sizeof(zeroDot25) / sizeof(float));
1118
1119
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1120
0
    {
1121
        // Load 2 * DEST_ELTS Float32 from each line
1122
0
        const auto firstLineLo =
1123
0
            _mm_mul_ps(_mm_loadu_ps(pSrcScanlineShifted), zeroDot25);
1124
0
        const auto firstLineHi = _mm_mul_ps(
1125
0
            _mm_loadu_ps(pSrcScanlineShifted + DEST_ELTS), zeroDot25);
1126
0
        const auto secondLineLo = _mm_mul_ps(
1127
0
            _mm_loadu_ps(pSrcScanlineShifted + nChunkXSize), zeroDot25);
1128
0
        const auto secondLineHi = _mm_mul_ps(
1129
0
            _mm_loadu_ps(pSrcScanlineShifted + DEST_ELTS + nChunkXSize),
1130
0
            zeroDot25);
1131
1132
        // Vertical addition
1133
0
        const auto tmpLo = _mm_add_ps(firstLineLo, secondLineLo);
1134
0
        const auto tmpHi = _mm_add_ps(firstLineHi, secondLineHi);
1135
1136
        // Horizontal addition
1137
0
        const auto average = sse2_hadd_ps(tmpLo, tmpHi);
1138
1139
0
        _mm_storeu_ps(&pDstScanline[iDstPixel], average);
1140
0
        pSrcScanlineShifted += DEST_ELTS * 2;
1141
0
    }
1142
1143
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1144
0
    return iDstPixel;
1145
0
}
1146
1147
/************************************************************************/
1148
/*                         AverageDoubleSSE2()                          */
1149
/************************************************************************/
1150
1151
static int
1152
AverageDoubleSSE2(int nDstXWidth, int nChunkXSize,
1153
                  const double *&CPL_RESTRICT pSrcScanlineShiftedInOut,
1154
                  double *CPL_RESTRICT pDstScanline)
1155
0
{
1156
    // Optimized implementation for average on Float64 by
1157
    // processing by group of output pixels.
1158
0
    const double *CPL_RESTRICT pSrcScanlineShifted = pSrcScanlineShiftedInOut;
1159
1160
0
    int iDstPixel = 0;
1161
0
    const auto zeroDot25 = _mm_set1_pd(0.25);
1162
0
    constexpr int DEST_ELTS =
1163
0
        static_cast<int>(sizeof(zeroDot25) / sizeof(double));
1164
1165
0
    for (; iDstPixel < nDstXWidth - (DEST_ELTS - 1); iDstPixel += DEST_ELTS)
1166
0
    {
1167
        // Load 4 * DEST_ELTS Float64 from each line
1168
0
        const auto firstLine0 = _mm_mul_pd(
1169
0
            _mm_loadu_pd(pSrcScanlineShifted + 0 * DEST_ELTS), zeroDot25);
1170
0
        const auto firstLine1 = _mm_mul_pd(
1171
0
            _mm_loadu_pd(pSrcScanlineShifted + 1 * DEST_ELTS), zeroDot25);
1172
0
        const auto secondLine0 = _mm_mul_pd(
1173
0
            _mm_loadu_pd(pSrcScanlineShifted + 0 * DEST_ELTS + nChunkXSize),
1174
0
            zeroDot25);
1175
0
        const auto secondLine1 = _mm_mul_pd(
1176
0
            _mm_loadu_pd(pSrcScanlineShifted + 1 * DEST_ELTS + nChunkXSize),
1177
0
            zeroDot25);
1178
1179
        // Vertical addition
1180
0
        const auto tmp0 = _mm_add_pd(firstLine0, secondLine0);
1181
0
        const auto tmp1 = _mm_add_pd(firstLine1, secondLine1);
1182
1183
        // Horizontal addition
1184
0
        const auto average0 = sse2_hadd_pd(tmp0, tmp1);
1185
1186
0
        _mm_storeu_pd(&pDstScanline[iDstPixel + 0], average0);
1187
0
        pSrcScanlineShifted += DEST_ELTS * 2;
1188
0
    }
1189
1190
0
    pSrcScanlineShiftedInOut = pSrcScanlineShifted;
1191
0
    return iDstPixel;
1192
0
}
1193
1194
#endif
1195
1196
#endif
1197
1198
/************************************************************************/
1199
/*                   GDALResampleChunk_AverageOrRMS()                   */
1200
/************************************************************************/
1201
1202
template <class T, class Tsum, GDALDataType eWrkDataType, bool bQuadraticMean>
1203
static CPLErr
1204
GDALResampleChunk_AverageOrRMS_T(const GDALOverviewResampleArgs &args,
1205
                                 const T *pChunk, void **ppDstBuffer)
1206
0
{
1207
0
    const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
1208
0
    const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
1209
0
    const double dfSrcXDelta = args.dfSrcXDelta;
1210
0
    const double dfSrcYDelta = args.dfSrcYDelta;
1211
0
    const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
1212
0
    const int nChunkXOff = args.nChunkXOff;
1213
0
    const int nChunkYOff = args.nChunkYOff;
1214
0
    const int nChunkXSize = args.nChunkXSize;
1215
0
    const int nChunkYSize = args.nChunkYSize;
1216
0
    const int nDstXOff = args.nDstXOff;
1217
0
    const int nDstXOff2 = args.nDstXOff2;
1218
0
    const int nDstYOff = args.nDstYOff;
1219
0
    const int nDstYOff2 = args.nDstYOff2;
1220
0
    const char *pszResampling = args.pszResampling;
1221
0
    bool bHasNoData = args.bHasNoData;
1222
0
    const double dfNoDataValue = args.dfNoDataValue;
1223
0
    const GDALColorTable *const poColorTable =
1224
0
        !bQuadraticMean &&
1225
                // AVERAGE_BIT2GRAYSCALE
1226
0
                STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2G")
1227
0
            ? nullptr
1228
0
            : args.poColorTable;
1229
0
    const bool bPropagateNoData = args.bPropagateNoData;
1230
1231
0
    T tNoDataValue = (!bHasNoData) ? 0 : static_cast<T>(dfNoDataValue);
1232
0
    const T tReplacementVal =
1233
0
        bHasNoData ? static_cast<T>(GDALGetNoDataReplacementValue(
1234
0
                         args.eOvrDataType, dfNoDataValue))
1235
0
                   : 0;
1236
1237
0
    const int nChunkRightXOff = nChunkXOff + nChunkXSize;
1238
0
    const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
1239
0
    const int nDstXWidth = nDstXOff2 - nDstXOff;
1240
1241
    /* -------------------------------------------------------------------- */
1242
    /*      Allocate buffers.                                               */
1243
    /* -------------------------------------------------------------------- */
1244
0
    *ppDstBuffer = static_cast<T *>(
1245
0
        VSI_MALLOC3_VERBOSE(nDstXWidth, nDstYOff2 - nDstYOff,
1246
0
                            GDALGetDataTypeSizeBytes(eWrkDataType)));
1247
0
    if (*ppDstBuffer == nullptr)
1248
0
    {
1249
0
        return CE_Failure;
1250
0
    }
1251
0
    T *const pDstBuffer = static_cast<T *>(*ppDstBuffer);
1252
1253
0
    struct PrecomputedXValue
1254
0
    {
1255
0
        int nLeftXOffShifted;
1256
0
        int nRightXOffShifted;
1257
0
        double dfLeftWeight;
1258
0
        double dfRightWeight;
1259
0
        double dfTotalWeightFullLine;
1260
0
    };
1261
1262
0
    PrecomputedXValue *pasSrcX = static_cast<PrecomputedXValue *>(
1263
0
        VSI_MALLOC2_VERBOSE(nDstXWidth, sizeof(PrecomputedXValue)));
1264
1265
0
    if (pasSrcX == nullptr)
1266
0
    {
1267
0
        return CE_Failure;
1268
0
    }
1269
1270
0
    std::vector<GDALColorEntry> colorEntries;
1271
1272
0
    if (poColorTable)
1273
0
    {
1274
0
        int nTransparentIdx = -1;
1275
0
        colorEntries = ReadColorTable(*poColorTable, nTransparentIdx);
1276
1277
        // Force c4 of nodata entry to 0 so that GDALFindBestEntry() identifies
1278
        // it as nodata value
1279
0
        if (bHasNoData && dfNoDataValue >= 0.0 &&
1280
0
            tNoDataValue < colorEntries.size())
1281
0
            colorEntries[static_cast<int>(tNoDataValue)].c4 = 0;
1282
1283
        // Or if we have no explicit nodata, but a color table entry that is
1284
        // transparent, consider it as the nodata value
1285
0
        else if (!bHasNoData && nTransparentIdx >= 0)
1286
0
        {
1287
0
            bHasNoData = true;
1288
0
            tNoDataValue = static_cast<T>(nTransparentIdx);
1289
0
        }
1290
0
    }
1291
1292
    /* ==================================================================== */
1293
    /*      Precompute inner loop constants.                                */
1294
    /* ==================================================================== */
1295
0
    bool bSrcXSpacingIsTwo = true;
1296
0
    int nLastSrcXOff2 = -1;
1297
0
    for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
1298
0
    {
1299
0
        const double dfSrcXOff = dfSrcXDelta + iDstPixel * dfXRatioDstToSrc;
1300
        // Apply some epsilon to avoid numerical precision issues
1301
0
        const int nSrcXOff =
1302
0
            std::max(static_cast<int>(dfSrcXOff + 1e-8), nChunkXOff);
1303
0
        const double dfSrcXOff2 =
1304
0
            dfSrcXDelta + (iDstPixel + 1) * dfXRatioDstToSrc;
1305
0
        int nSrcXOff2 = static_cast<int>(ceil(dfSrcXOff2 - 1e-8));
1306
0
        if (nSrcXOff2 == nSrcXOff)
1307
0
            nSrcXOff2++;
1308
0
        if (nSrcXOff2 > nChunkRightXOff)
1309
0
            nSrcXOff2 = nChunkRightXOff;
1310
1311
0
        pasSrcX[iDstPixel - nDstXOff].nLeftXOffShifted = nSrcXOff - nChunkXOff;
1312
0
        pasSrcX[iDstPixel - nDstXOff].nRightXOffShifted =
1313
0
            nSrcXOff2 - nChunkXOff;
1314
0
        pasSrcX[iDstPixel - nDstXOff].dfLeftWeight =
1315
0
            (nSrcXOff2 == nSrcXOff + 1) ? 1.0 : 1 - (dfSrcXOff - nSrcXOff);
1316
0
        pasSrcX[iDstPixel - nDstXOff].dfRightWeight =
1317
0
            1 - (nSrcXOff2 - dfSrcXOff2);
1318
0
        pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine =
1319
0
            pasSrcX[iDstPixel - nDstXOff].dfLeftWeight;
1320
0
        if (nSrcXOff + 1 < nSrcXOff2)
1321
0
        {
1322
0
            pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine +=
1323
0
                nSrcXOff2 - nSrcXOff - 2;
1324
0
            pasSrcX[iDstPixel - nDstXOff].dfTotalWeightFullLine +=
1325
0
                pasSrcX[iDstPixel - nDstXOff].dfRightWeight;
1326
0
        }
1327
1328
0
        if (nSrcXOff2 - nSrcXOff != 2 ||
1329
0
            (nLastSrcXOff2 >= 0 && nLastSrcXOff2 != nSrcXOff))
1330
0
        {
1331
0
            bSrcXSpacingIsTwo = false;
1332
0
        }
1333
0
        nLastSrcXOff2 = nSrcXOff2;
1334
0
    }
1335
1336
    /* ==================================================================== */
1337
    /*      Loop over destination scanlines.                                */
1338
    /* ==================================================================== */
1339
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
1340
0
    {
1341
0
        const double dfSrcYOff = dfSrcYDelta + iDstLine * dfYRatioDstToSrc;
1342
0
        int nSrcYOff = std::max(static_cast<int>(dfSrcYOff + 1e-8), nChunkYOff);
1343
1344
0
        const double dfSrcYOff2 =
1345
0
            dfSrcYDelta + (iDstLine + 1) * dfYRatioDstToSrc;
1346
0
        int nSrcYOff2 = static_cast<int>(ceil(dfSrcYOff2 - 1e-8));
1347
0
        if (nSrcYOff2 == nSrcYOff)
1348
0
            ++nSrcYOff2;
1349
0
        if (nSrcYOff2 > nChunkBottomYOff)
1350
0
            nSrcYOff2 = nChunkBottomYOff;
1351
1352
0
        T *const pDstScanline =
1353
0
            pDstBuffer + static_cast<size_t>(iDstLine - nDstYOff) * nDstXWidth;
1354
1355
        /* --------------------------------------------------------------------
1356
         */
1357
        /*      Loop over destination pixels */
1358
        /* --------------------------------------------------------------------
1359
         */
1360
0
        if (poColorTable == nullptr)
1361
0
        {
1362
0
            if (bSrcXSpacingIsTwo && nSrcYOff2 == nSrcYOff + 2 &&
1363
0
                pabyChunkNodataMask == nullptr)
1364
0
            {
1365
                if constexpr (eWrkDataType == GDT_UInt8 ||
1366
                              eWrkDataType == GDT_UInt16)
1367
0
                {
1368
                    // Optimized case : no nodata, overview by a factor of 2 and
1369
                    // regular x and y src spacing.
1370
0
                    const T *pSrcScanlineShifted =
1371
0
                        pChunk + pasSrcX[0].nLeftXOffShifted +
1372
0
                        static_cast<size_t>(nSrcYOff - nChunkYOff) *
1373
0
                            nChunkXSize;
1374
0
                    int iDstPixel = 0;
1375
0
#ifdef USE_SSE2
1376
                    if constexpr (eWrkDataType == GDT_UInt8)
1377
0
                    {
1378
                        if constexpr (bQuadraticMean)
1379
0
                        {
1380
0
                            iDstPixel = QuadraticMeanByteSSE2OrAVX2(
1381
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1382
0
                                pDstScanline);
1383
                        }
1384
                        else
1385
0
                        {
1386
0
                            iDstPixel = AverageByteSSE2OrAVX2(
1387
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1388
0
                                pDstScanline);
1389
0
                        }
1390
                    }
1391
                    else
1392
0
                    {
1393
0
                        static_assert(eWrkDataType == GDT_UInt16);
1394
                        if constexpr (bQuadraticMean)
1395
0
                        {
1396
0
                            iDstPixel = QuadraticMeanUInt16SSE2(
1397
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1398
0
                                pDstScanline);
1399
                        }
1400
                        else
1401
0
                        {
1402
0
                            iDstPixel = AverageUInt16SSE2(
1403
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1404
0
                                pDstScanline);
1405
0
                        }
1406
0
                    }
1407
0
#endif
1408
0
                    for (; iDstPixel < nDstXWidth; ++iDstPixel)
1409
0
                    {
1410
0
                        Tsum nTotal = 0;
1411
0
                        T nVal;
1412
                        if constexpr (bQuadraticMean)
1413
0
                            nTotal =
1414
0
                                SQUARE<Tsum>(pSrcScanlineShifted[0]) +
1415
0
                                SQUARE<Tsum>(pSrcScanlineShifted[1]) +
1416
0
                                SQUARE<Tsum>(pSrcScanlineShifted[nChunkXSize]) +
1417
0
                                SQUARE<Tsum>(
1418
                                    pSrcScanlineShifted[1 + nChunkXSize]);
1419
                        else
1420
0
                            nTotal = pSrcScanlineShifted[0] +
1421
0
                                     pSrcScanlineShifted[1] +
1422
0
                                     pSrcScanlineShifted[nChunkXSize] +
1423
0
                                     pSrcScanlineShifted[1 + nChunkXSize];
1424
1425
0
                        constexpr int nTotalWeight = 4;
1426
                        if constexpr (bQuadraticMean)
1427
0
                            nVal = ComputeIntegerRMS_4values<T>(nTotal);
1428
                        else
1429
0
                            nVal = static_cast<T>((nTotal + nTotalWeight / 2) /
1430
0
                                                  nTotalWeight);
1431
1432
                        // No need to compare nVal against tNoDataValue as we
1433
                        // are in a case where pabyChunkNodataMask == nullptr
1434
                        // implies the absence of nodata value.
1435
0
                        pDstScanline[iDstPixel] = nVal;
1436
0
                        pSrcScanlineShifted += 2;
1437
0
                    }
1438
                }
1439
                else
1440
0
                {
1441
0
                    static_assert(eWrkDataType == GDT_Float32 ||
1442
0
                                  eWrkDataType == GDT_Float64);
1443
0
                    const T *pSrcScanlineShifted =
1444
0
                        pChunk + pasSrcX[0].nLeftXOffShifted +
1445
0
                        static_cast<size_t>(nSrcYOff - nChunkYOff) *
1446
0
                            nChunkXSize;
1447
0
                    int iDstPixel = 0;
1448
0
#if defined(USE_SSE2) && !defined(ARM_V7)
1449
                    if constexpr (eWrkDataType == GDT_Float32)
1450
0
                    {
1451
0
                        static_assert(std::is_same_v<T, float>);
1452
                        if constexpr (bQuadraticMean)
1453
0
                        {
1454
0
                            iDstPixel = QuadraticMeanFloatSSE2(
1455
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1456
0
                                pDstScanline);
1457
                        }
1458
                        else
1459
0
                        {
1460
0
                            iDstPixel = AverageFloatSSE2(
1461
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1462
0
                                pDstScanline);
1463
0
                        }
1464
                    }
1465
                    else
1466
0
                    {
1467
                        if constexpr (!bQuadraticMean)
1468
0
                        {
1469
0
                            iDstPixel = AverageDoubleSSE2(
1470
0
                                nDstXWidth, nChunkXSize, pSrcScanlineShifted,
1471
0
                                pDstScanline);
1472
0
                        }
1473
0
                    }
1474
0
#endif
1475
1476
0
                    for (; iDstPixel < nDstXWidth; ++iDstPixel)
1477
0
                    {
1478
0
                        T nVal;
1479
1480
                        if constexpr (bQuadraticMean)
1481
0
                        {
1482
                            // Avoid issues with large values by renormalizing
1483
0
                            const auto max = std::max(
1484
0
                                {std::fabs(pSrcScanlineShifted[0]),
1485
0
                                 std::fabs(pSrcScanlineShifted[1]),
1486
0
                                 std::fabs(pSrcScanlineShifted[nChunkXSize]),
1487
0
                                 std::fabs(
1488
0
                                     pSrcScanlineShifted[1 + nChunkXSize])});
1489
0
                            if (max == 0)
1490
0
                            {
1491
0
                                nVal = 0;
1492
0
                            }
1493
0
                            else if (std::isinf(max))
1494
0
                            {
1495
                                // If there is at least one infinity value,
1496
                                // then just summing, and taking the abs
1497
                                // value will give the expected result:
1498
                                // * +inf if all values are +inf
1499
                                // * +inf if all values are -inf
1500
                                // * NaN otherwise
1501
0
                                nVal = std::fabs(
1502
0
                                    pSrcScanlineShifted[0] +
1503
0
                                    pSrcScanlineShifted[1] +
1504
0
                                    pSrcScanlineShifted[nChunkXSize] +
1505
0
                                    pSrcScanlineShifted[1 + nChunkXSize]);
1506
0
                            }
1507
0
                            else
1508
0
                            {
1509
0
                                const auto inv_max = static_cast<T>(1.0) / max;
1510
0
                                nVal =
1511
0
                                    max *
1512
0
                                    std::sqrt(
1513
0
                                        static_cast<T>(0.25) *
1514
0
                                        (SQUARE(pSrcScanlineShifted[0] *
1515
0
                                                inv_max) +
1516
0
                                         SQUARE(pSrcScanlineShifted[1] *
1517
0
                                                inv_max) +
1518
0
                                         SQUARE(
1519
0
                                             pSrcScanlineShifted[nChunkXSize] *
1520
0
                                             inv_max) +
1521
0
                                         SQUARE(
1522
0
                                             pSrcScanlineShifted[1 +
1523
0
                                                                 nChunkXSize] *
1524
0
                                             inv_max)));
1525
0
                            }
1526
                        }
1527
                        else
1528
0
                        {
1529
0
                            constexpr auto weight = static_cast<T>(0.25);
1530
                            // Multiply each value by weight to avoid
1531
                            // potential overflow
1532
0
                            nVal =
1533
0
                                (weight * pSrcScanlineShifted[0] +
1534
0
                                 weight * pSrcScanlineShifted[1] +
1535
0
                                 weight * pSrcScanlineShifted[nChunkXSize] +
1536
0
                                 weight * pSrcScanlineShifted[1 + nChunkXSize]);
1537
0
                        }
1538
1539
                        // No need to compare nVal against tNoDataValue as we
1540
                        // are in a case where pabyChunkNodataMask == nullptr
1541
                        // implies the absence of nodata value.
1542
0
                        pDstScanline[iDstPixel] = nVal;
1543
0
                        pSrcScanlineShifted += 2;
1544
0
                    }
1545
0
                }
1546
0
            }
1547
0
            else
1548
0
            {
1549
0
                const double dfBottomWeight =
1550
0
                    (nSrcYOff + 1 == nSrcYOff2) ? 1.0
1551
0
                                                : 1.0 - (dfSrcYOff - nSrcYOff);
1552
0
                const double dfTopWeight = 1.0 - (nSrcYOff2 - dfSrcYOff2);
1553
0
                nSrcYOff -= nChunkYOff;
1554
0
                nSrcYOff2 -= nChunkYOff;
1555
1556
0
                double dfTotalWeightFullColumn = dfBottomWeight;
1557
0
                if (nSrcYOff + 1 < nSrcYOff2)
1558
0
                {
1559
0
                    dfTotalWeightFullColumn += nSrcYOff2 - nSrcYOff - 2;
1560
0
                    dfTotalWeightFullColumn += dfTopWeight;
1561
0
                }
1562
1563
0
                for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
1564
0
                {
1565
0
                    const int nSrcXOff = pasSrcX[iDstPixel].nLeftXOffShifted;
1566
0
                    const int nSrcXOff2 = pasSrcX[iDstPixel].nRightXOffShifted;
1567
1568
0
                    double dfTotal = 0;
1569
0
                    double dfTotalWeight = 0;
1570
0
                    [[maybe_unused]] double dfMulFactor = 1.0;
1571
0
                    [[maybe_unused]] double dfInvMulFactor = 1.0;
1572
0
                    constexpr bool bUseMulFactor =
1573
0
                        (eWrkDataType == GDT_Float32 ||
1574
0
                         eWrkDataType == GDT_Float64);
1575
0
                    if (pabyChunkNodataMask == nullptr)
1576
0
                    {
1577
                        if constexpr (bUseMulFactor)
1578
0
                        {
1579
                            if constexpr (bQuadraticMean)
1580
0
                            {
1581
0
                                T mulFactor = 0;
1582
0
                                auto pChunkShifted =
1583
0
                                    pChunk +
1584
0
                                    static_cast<size_t>(nSrcYOff) * nChunkXSize;
1585
1586
0
                                for (int iY = nSrcYOff; iY < nSrcYOff2;
1587
0
                                     ++iY, pChunkShifted += nChunkXSize)
1588
0
                                {
1589
0
                                    for (int iX = nSrcXOff; iX < nSrcXOff2;
1590
0
                                         ++iX)
1591
0
                                        mulFactor = std::max(
1592
0
                                            mulFactor,
1593
0
                                            std::fabs(pChunkShifted[iX]));
1594
0
                                }
1595
0
                                dfMulFactor = double(mulFactor);
1596
0
                                dfInvMulFactor =
1597
0
                                    dfMulFactor > 0 &&
1598
0
                                            std::isfinite(dfMulFactor)
1599
0
                                        ? 1.0 / dfMulFactor
1600
0
                                        : 1.0;
1601
                            }
1602
                            else
1603
0
                            {
1604
0
                                dfMulFactor = (nSrcYOff2 - nSrcYOff) *
1605
0
                                              (nSrcXOff2 - nSrcXOff);
1606
0
                                dfInvMulFactor = 1.0 / dfMulFactor;
1607
0
                            }
1608
0
                        }
1609
1610
0
                        auto pChunkShifted =
1611
0
                            pChunk +
1612
0
                            static_cast<size_t>(nSrcYOff) * nChunkXSize;
1613
0
                        int nCounterY = nSrcYOff2 - nSrcYOff - 1;
1614
0
                        double dfWeightY = dfBottomWeight;
1615
0
                        while (true)
1616
0
                        {
1617
0
                            double dfTotalLine;
1618
                            if constexpr (bQuadraticMean)
1619
0
                            {
1620
                                // Left pixel
1621
0
                                {
1622
0
                                    const T val = pChunkShifted[nSrcXOff];
1623
0
                                    dfTotalLine =
1624
0
                                        SQUARE(double(val) * dfInvMulFactor) *
1625
0
                                        pasSrcX[iDstPixel].dfLeftWeight;
1626
0
                                }
1627
1628
0
                                if (nSrcXOff + 1 < nSrcXOff2)
1629
0
                                {
1630
                                    // Middle pixels
1631
0
                                    for (int iX = nSrcXOff + 1;
1632
0
                                         iX < nSrcXOff2 - 1; ++iX)
1633
0
                                    {
1634
0
                                        const T val = pChunkShifted[iX];
1635
0
                                        dfTotalLine += SQUARE(double(val) *
1636
0
                                                              dfInvMulFactor);
1637
0
                                    }
1638
1639
                                    // Right pixel
1640
0
                                    {
1641
0
                                        const T val =
1642
0
                                            pChunkShifted[nSrcXOff2 - 1];
1643
0
                                        dfTotalLine +=
1644
0
                                            SQUARE(double(val) *
1645
0
                                                   dfInvMulFactor) *
1646
0
                                            pasSrcX[iDstPixel].dfRightWeight;
1647
0
                                    }
1648
0
                                }
1649
                            }
1650
                            else
1651
0
                            {
1652
                                // Left pixel
1653
0
                                {
1654
0
                                    const T val = pChunkShifted[nSrcXOff];
1655
0
                                    dfTotalLine =
1656
0
                                        double(val) * dfInvMulFactor *
1657
0
                                        pasSrcX[iDstPixel].dfLeftWeight;
1658
0
                                }
1659
1660
0
                                if (nSrcXOff + 1 < nSrcXOff2)
1661
0
                                {
1662
                                    // Middle pixels
1663
0
                                    for (int iX = nSrcXOff + 1;
1664
0
                                         iX < nSrcXOff2 - 1; ++iX)
1665
0
                                    {
1666
0
                                        const T val = pChunkShifted[iX];
1667
0
                                        dfTotalLine +=
1668
0
                                            double(val) * dfInvMulFactor;
1669
0
                                    }
1670
1671
                                    // Right pixel
1672
0
                                    {
1673
0
                                        const T val =
1674
0
                                            pChunkShifted[nSrcXOff2 - 1];
1675
0
                                        dfTotalLine +=
1676
0
                                            double(val) * dfInvMulFactor *
1677
0
                                            pasSrcX[iDstPixel].dfRightWeight;
1678
0
                                    }
1679
0
                                }
1680
0
                            }
1681
1682
0
                            dfTotal += dfTotalLine * dfWeightY;
1683
0
                            --nCounterY;
1684
0
                            if (nCounterY < 0)
1685
0
                                break;
1686
0
                            pChunkShifted += nChunkXSize;
1687
0
                            dfWeightY = (nCounterY == 0) ? dfTopWeight : 1.0;
1688
0
                        }
1689
1690
0
                        dfTotalWeight =
1691
0
                            pasSrcX[iDstPixel].dfTotalWeightFullLine *
1692
0
                            dfTotalWeightFullColumn;
1693
0
                    }
1694
0
                    else
1695
0
                    {
1696
0
                        size_t nCount = 0;
1697
0
                        for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
1698
0
                        {
1699
0
                            const auto pChunkShifted =
1700
0
                                pChunk + static_cast<size_t>(iY) * nChunkXSize;
1701
1702
0
                            double dfTotalLine = 0;
1703
0
                            double dfTotalWeightLine = 0;
1704
                            // Left pixel
1705
0
                            {
1706
0
                                const int iX = nSrcXOff;
1707
0
                                const T val = pChunkShifted[iX];
1708
0
                                if (pabyChunkNodataMask
1709
0
                                        [iX +
1710
0
                                         static_cast<size_t>(iY) * nChunkXSize])
1711
0
                                {
1712
0
                                    nCount++;
1713
0
                                    const double dfWeightX =
1714
0
                                        pasSrcX[iDstPixel].dfLeftWeight;
1715
0
                                    dfTotalWeightLine = dfWeightX;
1716
                                    if constexpr (bQuadraticMean)
1717
0
                                        dfTotalLine =
1718
                                            SQUARE(double(val)) * dfWeightX;
1719
                                    else
1720
0
                                        dfTotalLine = double(val) * dfWeightX;
1721
0
                                }
1722
0
                            }
1723
1724
0
                            if (nSrcXOff < nSrcXOff2 - 1)
1725
0
                            {
1726
                                // Middle pixels
1727
0
                                for (int iX = nSrcXOff + 1; iX < nSrcXOff2 - 1;
1728
0
                                     ++iX)
1729
0
                                {
1730
0
                                    const T val = pChunkShifted[iX];
1731
0
                                    if (pabyChunkNodataMask
1732
0
                                            [iX + static_cast<size_t>(iY) *
1733
0
                                                      nChunkXSize])
1734
0
                                    {
1735
0
                                        nCount++;
1736
0
                                        dfTotalWeightLine += 1;
1737
                                        if constexpr (bQuadraticMean)
1738
0
                                            dfTotalLine += SQUARE(double(val));
1739
                                        else
1740
0
                                            dfTotalLine += double(val);
1741
0
                                    }
1742
0
                                }
1743
1744
                                // Right pixel
1745
0
                                {
1746
0
                                    const int iX = nSrcXOff2 - 1;
1747
0
                                    const T val = pChunkShifted[iX];
1748
0
                                    if (pabyChunkNodataMask
1749
0
                                            [iX + static_cast<size_t>(iY) *
1750
0
                                                      nChunkXSize])
1751
0
                                    {
1752
0
                                        nCount++;
1753
0
                                        const double dfWeightX =
1754
0
                                            pasSrcX[iDstPixel].dfRightWeight;
1755
0
                                        dfTotalWeightLine += dfWeightX;
1756
                                        if constexpr (bQuadraticMean)
1757
0
                                            dfTotalLine +=
1758
                                                SQUARE(double(val)) * dfWeightX;
1759
                                        else
1760
0
                                            dfTotalLine +=
1761
0
                                                double(val) * dfWeightX;
1762
0
                                    }
1763
0
                                }
1764
0
                            }
1765
1766
0
                            const double dfWeightY =
1767
0
                                (iY == nSrcYOff)        ? dfBottomWeight
1768
0
                                : (iY + 1 == nSrcYOff2) ? dfTopWeight
1769
0
                                                        : 1.0;
1770
0
                            dfTotal += dfTotalLine * dfWeightY;
1771
0
                            dfTotalWeight += dfTotalWeightLine * dfWeightY;
1772
0
                        }
1773
1774
0
                        if (nCount == 0 ||
1775
0
                            (bPropagateNoData &&
1776
0
                             nCount <
1777
0
                                 static_cast<size_t>(nSrcYOff2 - nSrcYOff) *
1778
0
                                     (nSrcXOff2 - nSrcXOff)))
1779
0
                        {
1780
0
                            pDstScanline[iDstPixel] = tNoDataValue;
1781
0
                            continue;
1782
0
                        }
1783
0
                    }
1784
                    if constexpr (eWrkDataType == GDT_UInt8)
1785
0
                    {
1786
0
                        T nVal;
1787
                        if constexpr (bQuadraticMean)
1788
0
                            nVal = ComputeIntegerRMS<T, int>(dfTotal,
1789
                                                             dfTotalWeight);
1790
                        else
1791
0
                            nVal =
1792
0
                                static_cast<T>(dfTotal / dfTotalWeight + 0.5);
1793
0
                        if (bHasNoData && nVal == tNoDataValue)
1794
0
                            nVal = tReplacementVal;
1795
0
                        pDstScanline[iDstPixel] = nVal;
1796
                    }
1797
                    else if constexpr (eWrkDataType == GDT_UInt16)
1798
0
                    {
1799
0
                        T nVal;
1800
                        if constexpr (bQuadraticMean)
1801
0
                            nVal = ComputeIntegerRMS<T, uint64_t>(
1802
                                dfTotal, dfTotalWeight);
1803
                        else
1804
0
                            nVal =
1805
0
                                static_cast<T>(dfTotal / dfTotalWeight + 0.5);
1806
0
                        if (bHasNoData && nVal == tNoDataValue)
1807
0
                            nVal = tReplacementVal;
1808
0
                        pDstScanline[iDstPixel] = nVal;
1809
                    }
1810
                    else
1811
0
                    {
1812
0
                        T nVal;
1813
                        if constexpr (bQuadraticMean)
1814
0
                        {
1815
                            if constexpr (bUseMulFactor)
1816
0
                                nVal = static_cast<T>(
1817
0
                                    dfMulFactor *
1818
                                    sqrt(dfTotal / dfTotalWeight));
1819
                            else
1820
                                nVal = static_cast<T>(
1821
                                    sqrt(dfTotal / dfTotalWeight));
1822
                        }
1823
                        else
1824
0
                        {
1825
                            if constexpr (bUseMulFactor)
1826
0
                                nVal = static_cast<T>(
1827
                                    dfMulFactor * (dfTotal / dfTotalWeight));
1828
                            else
1829
                                nVal = static_cast<T>(dfTotal / dfTotalWeight);
1830
0
                        }
1831
0
                        if (bHasNoData && nVal == tNoDataValue)
1832
0
                            nVal = tReplacementVal;
1833
0
                        pDstScanline[iDstPixel] = nVal;
1834
0
                    }
1835
0
                }
1836
0
            }
1837
0
        }
1838
0
        else
1839
0
        {
1840
0
            nSrcYOff -= nChunkYOff;
1841
0
            nSrcYOff2 -= nChunkYOff;
1842
1843
0
            for (int iDstPixel = 0; iDstPixel < nDstXWidth; ++iDstPixel)
1844
0
            {
1845
0
                const int nSrcXOff = pasSrcX[iDstPixel].nLeftXOffShifted;
1846
0
                const int nSrcXOff2 = pasSrcX[iDstPixel].nRightXOffShifted;
1847
1848
0
                uint64_t nTotalR = 0;
1849
0
                uint64_t nTotalG = 0;
1850
0
                uint64_t nTotalB = 0;
1851
0
                size_t nCount = 0;
1852
1853
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
1854
0
                {
1855
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
1856
0
                    {
1857
0
                        const T val =
1858
0
                            pChunk[iX + static_cast<size_t>(iY) * nChunkXSize];
1859
                        // cppcheck-suppress unsignedLessThanZero
1860
0
                        if (val < 0 || val >= colorEntries.size())
1861
0
                            continue;
1862
0
                        const size_t idx = static_cast<size_t>(val);
1863
0
                        const auto &entry = colorEntries[idx];
1864
0
                        if (entry.c4)
1865
0
                        {
1866
                            if constexpr (bQuadraticMean)
1867
0
                            {
1868
0
                                nTotalR += SQUARE<int>(entry.c1);
1869
0
                                nTotalG += SQUARE<int>(entry.c2);
1870
0
                                nTotalB += SQUARE<int>(entry.c3);
1871
0
                                ++nCount;
1872
                            }
1873
                            else
1874
0
                            {
1875
0
                                nTotalR += entry.c1;
1876
0
                                nTotalG += entry.c2;
1877
0
                                nTotalB += entry.c3;
1878
0
                                ++nCount;
1879
0
                            }
1880
0
                        }
1881
0
                    }
1882
0
                }
1883
1884
0
                if (nCount == 0 ||
1885
0
                    (bPropagateNoData &&
1886
0
                     nCount < static_cast<size_t>(nSrcYOff2 - nSrcYOff) *
1887
0
                                  (nSrcXOff2 - nSrcXOff)))
1888
0
                {
1889
0
                    pDstScanline[iDstPixel] = tNoDataValue;
1890
0
                }
1891
0
                else
1892
0
                {
1893
0
                    GDALColorEntry color;
1894
                    if constexpr (bQuadraticMean)
1895
0
                    {
1896
0
                        color.c1 =
1897
0
                            static_cast<short>(sqrt(nTotalR / nCount) + 0.5);
1898
0
                        color.c2 =
1899
0
                            static_cast<short>(sqrt(nTotalG / nCount) + 0.5);
1900
0
                        color.c3 =
1901
0
                            static_cast<short>(sqrt(nTotalB / nCount) + 0.5);
1902
                    }
1903
                    else
1904
0
                    {
1905
0
                        color.c1 =
1906
0
                            static_cast<short>((nTotalR + nCount / 2) / nCount);
1907
0
                        color.c2 =
1908
0
                            static_cast<short>((nTotalG + nCount / 2) / nCount);
1909
0
                        color.c3 =
1910
0
                            static_cast<short>((nTotalB + nCount / 2) / nCount);
1911
0
                    }
1912
0
                    pDstScanline[iDstPixel] =
1913
0
                        static_cast<T>(BestColorEntry(colorEntries, color));
1914
0
                }
1915
0
            }
1916
0
        }
1917
0
    }
1918
1919
0
    CPLFree(pasSrcX);
1920
1921
0
    return CE_None;
1922
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<unsigned char, int, (GDALDataType)1, true>(GDALOverviewResampleArgs const&, unsigned char const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<unsigned short, double, (GDALDataType)2, true>(GDALOverviewResampleArgs const&, unsigned short const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<float, double, (GDALDataType)6, true>(GDALOverviewResampleArgs const&, float const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<double, double, (GDALDataType)7, true>(GDALOverviewResampleArgs const&, double const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<unsigned char, int, (GDALDataType)1, false>(GDALOverviewResampleArgs const&, unsigned char const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<unsigned short, unsigned int, (GDALDataType)2, false>(GDALOverviewResampleArgs const&, unsigned short const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<float, double, (GDALDataType)6, false>(GDALOverviewResampleArgs const&, float const*, void**)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMS_T<double, double, (GDALDataType)7, false>(GDALOverviewResampleArgs const&, double const*, void**)
1923
1924
template <bool bQuadraticMean>
1925
static CPLErr
1926
GDALResampleChunk_AverageOrRMSInternal(const GDALOverviewResampleArgs &args,
1927
                                       const void *pChunk, void **ppDstBuffer,
1928
                                       GDALDataType *peDstBufferDataType)
1929
0
{
1930
0
    *peDstBufferDataType = args.eWrkDataType;
1931
0
    switch (args.eWrkDataType)
1932
0
    {
1933
0
        case GDT_UInt8:
1934
0
        {
1935
0
            return GDALResampleChunk_AverageOrRMS_T<GByte, int, GDT_UInt8,
1936
0
                                                    bQuadraticMean>(
1937
0
                args, static_cast<const GByte *>(pChunk), ppDstBuffer);
1938
0
        }
1939
1940
0
        case GDT_UInt16:
1941
0
        {
1942
            if constexpr (bQuadraticMean)
1943
0
            {
1944
                // Use double as accumulation type, because UInt32 could overflow
1945
0
                return GDALResampleChunk_AverageOrRMS_T<
1946
0
                    GUInt16, double, GDT_UInt16, bQuadraticMean>(
1947
0
                    args, static_cast<const GUInt16 *>(pChunk), ppDstBuffer);
1948
            }
1949
            else
1950
0
            {
1951
0
                return GDALResampleChunk_AverageOrRMS_T<
1952
0
                    GUInt16, GUInt32, GDT_UInt16, bQuadraticMean>(
1953
0
                    args, static_cast<const GUInt16 *>(pChunk), ppDstBuffer);
1954
0
            }
1955
0
        }
1956
1957
0
        case GDT_Float32:
1958
0
        {
1959
0
            return GDALResampleChunk_AverageOrRMS_T<float, double, GDT_Float32,
1960
0
                                                    bQuadraticMean>(
1961
0
                args, static_cast<const float *>(pChunk), ppDstBuffer);
1962
0
        }
1963
1964
0
        case GDT_Float64:
1965
0
        {
1966
0
            return GDALResampleChunk_AverageOrRMS_T<double, double, GDT_Float64,
1967
0
                                                    bQuadraticMean>(
1968
0
                args, static_cast<const double *>(pChunk), ppDstBuffer);
1969
0
        }
1970
1971
0
        default:
1972
0
            break;
1973
0
    }
1974
1975
0
    CPLAssert(false);
1976
0
    return CE_Failure;
1977
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMSInternal<true>(GDALOverviewResampleArgs const&, void const*, void**, GDALDataType*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_AverageOrRMSInternal<false>(GDALOverviewResampleArgs const&, void const*, void**, GDALDataType*)
1978
1979
static CPLErr
1980
GDALResampleChunk_AverageOrRMS(const GDALOverviewResampleArgs &args,
1981
                               const void *pChunk, void **ppDstBuffer,
1982
                               GDALDataType *peDstBufferDataType)
1983
0
{
1984
0
    if (EQUAL(args.pszResampling, "RMS"))
1985
0
        return GDALResampleChunk_AverageOrRMSInternal<true>(
1986
0
            args, pChunk, ppDstBuffer, peDstBufferDataType);
1987
0
    else
1988
0
        return GDALResampleChunk_AverageOrRMSInternal<false>(
1989
0
            args, pChunk, ppDstBuffer, peDstBufferDataType);
1990
0
}
1991
1992
/************************************************************************/
1993
/*                      GDALResampleChunk_Gauss()                       */
1994
/************************************************************************/
1995
1996
static CPLErr GDALResampleChunk_Gauss(const GDALOverviewResampleArgs &args,
1997
                                      const void *pChunk, void **ppDstBuffer,
1998
                                      GDALDataType *peDstBufferDataType)
1999
2000
0
{
2001
0
    const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
2002
0
    const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
2003
0
    const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
2004
0
    const int nChunkXOff = args.nChunkXOff;
2005
0
    const int nChunkXSize = args.nChunkXSize;
2006
0
    const int nChunkYOff = args.nChunkYOff;
2007
0
    const int nChunkYSize = args.nChunkYSize;
2008
0
    const int nDstXOff = args.nDstXOff;
2009
0
    const int nDstXOff2 = args.nDstXOff2;
2010
0
    const int nDstYOff = args.nDstYOff;
2011
0
    const int nDstYOff2 = args.nDstYOff2;
2012
0
    const bool bHasNoData = args.bHasNoData;
2013
0
    double dfNoDataValue = args.dfNoDataValue;
2014
0
    const GDALColorTable *poColorTable = args.poColorTable;
2015
2016
0
    const double *const padfChunk = static_cast<const double *>(pChunk);
2017
2018
0
    *ppDstBuffer =
2019
0
        VSI_MALLOC3_VERBOSE(nDstXOff2 - nDstXOff, nDstYOff2 - nDstYOff,
2020
0
                            GDALGetDataTypeSizeBytes(GDT_Float64));
2021
0
    if (*ppDstBuffer == nullptr)
2022
0
    {
2023
0
        return CE_Failure;
2024
0
    }
2025
0
    *peDstBufferDataType = GDT_Float64;
2026
0
    double *const padfDstBuffer = static_cast<double *>(*ppDstBuffer);
2027
2028
    /* -------------------------------------------------------------------- */
2029
    /*      Create the filter kernel and allocate scanline buffer.          */
2030
    /* -------------------------------------------------------------------- */
2031
0
    int nGaussMatrixDim = 3;
2032
0
    const int *panGaussMatrix;
2033
0
    constexpr int anGaussMatrix3x3[] = {1, 2, 1, 2, 4, 2, 1, 2, 1};
2034
0
    constexpr int anGaussMatrix5x5[] = {1,  4, 6,  4,  1,  4, 16, 24, 16,
2035
0
                                        4,  6, 24, 36, 24, 6, 4,  16, 24,
2036
0
                                        16, 4, 1,  4,  6,  4, 1};
2037
0
    constexpr int anGaussMatrix7x7[] = {
2038
0
        1,   6,  15, 20,  15,  6,   1,   6,  36, 90,  120, 90,  36,
2039
0
        6,   15, 90, 225, 300, 225, 90,  15, 20, 120, 300, 400, 300,
2040
0
        120, 20, 15, 90,  225, 300, 225, 90, 15, 6,   36,  90,  120,
2041
0
        90,  36, 6,  1,   6,   15,  20,  15, 6,  1};
2042
2043
0
    const int nOXSize = args.nOvrXSize;
2044
0
    const int nOYSize = args.nOvrYSize;
2045
0
    const int nResYFactor = static_cast<int>(0.5 + dfYRatioDstToSrc);
2046
2047
    // matrix for gauss filter
2048
0
    if (nResYFactor <= 2)
2049
0
    {
2050
0
        panGaussMatrix = anGaussMatrix3x3;
2051
0
        nGaussMatrixDim = 3;
2052
0
    }
2053
0
    else if (nResYFactor <= 4)
2054
0
    {
2055
0
        panGaussMatrix = anGaussMatrix5x5;
2056
0
        nGaussMatrixDim = 5;
2057
0
    }
2058
0
    else
2059
0
    {
2060
0
        panGaussMatrix = anGaussMatrix7x7;
2061
0
        nGaussMatrixDim = 7;
2062
0
    }
2063
2064
#ifdef DEBUG_OUT_OF_BOUND_ACCESS
2065
    int *panGaussMatrixDup = static_cast<int *>(
2066
        CPLMalloc(sizeof(int) * nGaussMatrixDim * nGaussMatrixDim));
2067
    memcpy(panGaussMatrixDup, panGaussMatrix,
2068
           sizeof(int) * nGaussMatrixDim * nGaussMatrixDim);
2069
    panGaussMatrix = panGaussMatrixDup;
2070
#endif
2071
2072
0
    if (!bHasNoData)
2073
0
        dfNoDataValue = 0.0;
2074
2075
0
    std::vector<GDALColorEntry> colorEntries;
2076
0
    int nTransparentIdx = -1;
2077
0
    if (poColorTable)
2078
0
        colorEntries = ReadColorTable(*poColorTable, nTransparentIdx);
2079
2080
    // Force c4 of nodata entry to 0 so that GDALFindBestEntry() identifies
2081
    // it as nodata value.
2082
0
    if (bHasNoData && dfNoDataValue >= 0.0 &&
2083
0
        dfNoDataValue < colorEntries.size())
2084
0
        colorEntries[static_cast<int>(dfNoDataValue)].c4 = 0;
2085
2086
    // Or if we have no explicit nodata, but a color table entry that is
2087
    // transparent, consider it as the nodata value.
2088
0
    else if (!bHasNoData && nTransparentIdx >= 0)
2089
0
    {
2090
0
        dfNoDataValue = nTransparentIdx;
2091
0
    }
2092
2093
0
    const int nChunkRightXOff = nChunkXOff + nChunkXSize;
2094
0
    const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
2095
0
    const int nDstXWidth = nDstXOff2 - nDstXOff;
2096
2097
    /* ==================================================================== */
2098
    /*      Loop over destination scanlines.                                */
2099
    /* ==================================================================== */
2100
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
2101
0
    {
2102
0
        int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
2103
0
        int nSrcYOff2 =
2104
0
            static_cast<int>(0.5 + (iDstLine + 1) * dfYRatioDstToSrc) + 1;
2105
2106
0
        if (nSrcYOff < nChunkYOff)
2107
0
        {
2108
0
            nSrcYOff = nChunkYOff;
2109
0
            nSrcYOff2++;
2110
0
        }
2111
2112
0
        const int iSizeY = nSrcYOff2 - nSrcYOff;
2113
0
        nSrcYOff = nSrcYOff + iSizeY / 2 - nGaussMatrixDim / 2;
2114
0
        nSrcYOff2 = nSrcYOff + nGaussMatrixDim;
2115
2116
0
        if (nSrcYOff2 > nChunkBottomYOff ||
2117
0
            (dfYRatioDstToSrc > 1 && iDstLine == nOYSize - 1))
2118
0
        {
2119
0
            nSrcYOff2 = std::min(nChunkBottomYOff, nSrcYOff + nGaussMatrixDim);
2120
0
        }
2121
2122
0
        int nYShiftGaussMatrix = 0;
2123
0
        if (nSrcYOff < nChunkYOff)
2124
0
        {
2125
0
            nYShiftGaussMatrix = -(nSrcYOff - nChunkYOff);
2126
0
            nSrcYOff = nChunkYOff;
2127
0
        }
2128
2129
0
        const double *const padfSrcScanline =
2130
0
            padfChunk + ((nSrcYOff - nChunkYOff) * nChunkXSize);
2131
0
        const GByte *pabySrcScanlineNodataMask = nullptr;
2132
0
        if (pabyChunkNodataMask != nullptr)
2133
0
            pabySrcScanlineNodataMask =
2134
0
                pabyChunkNodataMask + ((nSrcYOff - nChunkYOff) * nChunkXSize);
2135
2136
        /* --------------------------------------------------------------------
2137
         */
2138
        /*      Loop over destination pixels */
2139
        /* --------------------------------------------------------------------
2140
         */
2141
0
        double *const padfDstScanline =
2142
0
            padfDstBuffer + (iDstLine - nDstYOff) * nDstXWidth;
2143
0
        for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
2144
0
        {
2145
0
            int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
2146
0
            int nSrcXOff2 =
2147
0
                static_cast<int>(0.5 + (iDstPixel + 1) * dfXRatioDstToSrc) + 1;
2148
2149
0
            if (nSrcXOff < nChunkXOff)
2150
0
            {
2151
0
                nSrcXOff = nChunkXOff;
2152
0
                nSrcXOff2++;
2153
0
            }
2154
2155
0
            const int iSizeX = nSrcXOff2 - nSrcXOff;
2156
0
            nSrcXOff = nSrcXOff + iSizeX / 2 - nGaussMatrixDim / 2;
2157
0
            nSrcXOff2 = nSrcXOff + nGaussMatrixDim;
2158
2159
0
            if (nSrcXOff2 > nChunkRightXOff ||
2160
0
                (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize - 1))
2161
0
            {
2162
0
                nSrcXOff2 =
2163
0
                    std::min(nChunkRightXOff, nSrcXOff + nGaussMatrixDim);
2164
0
            }
2165
2166
0
            int nXShiftGaussMatrix = 0;
2167
0
            if (nSrcXOff < nChunkXOff)
2168
0
            {
2169
0
                nXShiftGaussMatrix = -(nSrcXOff - nChunkXOff);
2170
0
                nSrcXOff = nChunkXOff;
2171
0
            }
2172
2173
0
            if (poColorTable == nullptr)
2174
0
            {
2175
0
                double dfTotal = 0.0;
2176
0
                GInt64 nCount = 0;
2177
0
                const int *panLineWeight =
2178
0
                    panGaussMatrix + nYShiftGaussMatrix * nGaussMatrixDim +
2179
0
                    nXShiftGaussMatrix;
2180
2181
0
                for (int iY = nSrcYOff; iY < nSrcYOff2;
2182
0
                     ++iY, panLineWeight += nGaussMatrixDim)
2183
0
                {
2184
0
                    for (int i = 0, iX = nSrcXOff; iX < nSrcXOff2; ++iX, ++i)
2185
0
                    {
2186
0
                        const double val =
2187
0
                            padfSrcScanline[iX - nChunkXOff +
2188
0
                                            static_cast<GPtrDiff_t>(iY -
2189
0
                                                                    nSrcYOff) *
2190
0
                                                nChunkXSize];
2191
0
                        if (pabySrcScanlineNodataMask == nullptr ||
2192
0
                            pabySrcScanlineNodataMask[iX - nChunkXOff +
2193
0
                                                      static_cast<GPtrDiff_t>(
2194
0
                                                          iY - nSrcYOff) *
2195
0
                                                          nChunkXSize])
2196
0
                        {
2197
0
                            const int nWeight = panLineWeight[i];
2198
0
                            dfTotal += val * nWeight;
2199
0
                            nCount += nWeight;
2200
0
                        }
2201
0
                    }
2202
0
                }
2203
2204
0
                if (nCount == 0)
2205
0
                {
2206
0
                    padfDstScanline[iDstPixel - nDstXOff] = dfNoDataValue;
2207
0
                }
2208
0
                else
2209
0
                {
2210
0
                    padfDstScanline[iDstPixel - nDstXOff] = dfTotal / nCount;
2211
0
                }
2212
0
            }
2213
0
            else
2214
0
            {
2215
0
                GInt64 nTotalR = 0;
2216
0
                GInt64 nTotalG = 0;
2217
0
                GInt64 nTotalB = 0;
2218
0
                GInt64 nTotalWeight = 0;
2219
0
                const int *panLineWeight =
2220
0
                    panGaussMatrix + nYShiftGaussMatrix * nGaussMatrixDim +
2221
0
                    nXShiftGaussMatrix;
2222
2223
0
                for (int iY = nSrcYOff; iY < nSrcYOff2;
2224
0
                     ++iY, panLineWeight += nGaussMatrixDim)
2225
0
                {
2226
0
                    for (int i = 0, iX = nSrcXOff; iX < nSrcXOff2; ++iX, ++i)
2227
0
                    {
2228
0
                        const double val =
2229
0
                            padfSrcScanline[iX - nChunkXOff +
2230
0
                                            static_cast<GPtrDiff_t>(iY -
2231
0
                                                                    nSrcYOff) *
2232
0
                                                nChunkXSize];
2233
0
                        if (val < 0 || val >= colorEntries.size())
2234
0
                            continue;
2235
2236
0
                        size_t idx = static_cast<size_t>(val);
2237
0
                        if (colorEntries[idx].c4)
2238
0
                        {
2239
0
                            const int nWeight = panLineWeight[i];
2240
0
                            nTotalR +=
2241
0
                                static_cast<GInt64>(colorEntries[idx].c1) *
2242
0
                                nWeight;
2243
0
                            nTotalG +=
2244
0
                                static_cast<GInt64>(colorEntries[idx].c2) *
2245
0
                                nWeight;
2246
0
                            nTotalB +=
2247
0
                                static_cast<GInt64>(colorEntries[idx].c3) *
2248
0
                                nWeight;
2249
0
                            nTotalWeight += nWeight;
2250
0
                        }
2251
0
                    }
2252
0
                }
2253
2254
0
                if (nTotalWeight == 0)
2255
0
                {
2256
0
                    padfDstScanline[iDstPixel - nDstXOff] = dfNoDataValue;
2257
0
                }
2258
0
                else
2259
0
                {
2260
0
                    GDALColorEntry color;
2261
2262
0
                    color.c1 = static_cast<short>((nTotalR + nTotalWeight / 2) /
2263
0
                                                  nTotalWeight);
2264
0
                    color.c2 = static_cast<short>((nTotalG + nTotalWeight / 2) /
2265
0
                                                  nTotalWeight);
2266
0
                    color.c3 = static_cast<short>((nTotalB + nTotalWeight / 2) /
2267
0
                                                  nTotalWeight);
2268
0
                    padfDstScanline[iDstPixel - nDstXOff] =
2269
0
                        BestColorEntry(colorEntries, color);
2270
0
                }
2271
0
            }
2272
0
        }
2273
0
    }
2274
2275
#ifdef DEBUG_OUT_OF_BOUND_ACCESS
2276
    CPLFree(panGaussMatrixDup);
2277
#endif
2278
2279
0
    return CE_None;
2280
0
}
2281
2282
/************************************************************************/
2283
/*                       GDALResampleChunk_Mode()                       */
2284
/************************************************************************/
2285
2286
template <class T> static inline bool IsSame(T a, T b)
2287
0
{
2288
0
    return a == b;
2289
0
}
Unexecuted instantiation: overview.cpp:bool IsSame<unsigned char>(unsigned char, unsigned char)
Unexecuted instantiation: overview.cpp:bool IsSame<signed char>(signed char, signed char)
Unexecuted instantiation: overview.cpp:bool IsSame<unsigned short>(unsigned short, unsigned short)
Unexecuted instantiation: overview.cpp:bool IsSame<unsigned int>(unsigned int, unsigned int)
Unexecuted instantiation: overview.cpp:bool IsSame<unsigned long>(unsigned long, unsigned long)
2290
2291
template <> bool IsSame<GFloat16>(GFloat16 a, GFloat16 b)
2292
0
{
2293
0
    return a == b || (CPLIsNan(a) && CPLIsNan(b));
2294
0
}
2295
2296
template <> bool IsSame<float>(float a, float b)
2297
0
{
2298
0
    return a == b || (std::isnan(a) && std::isnan(b));
2299
0
}
2300
2301
template <> bool IsSame<double>(double a, double b)
2302
0
{
2303
0
    return a == b || (std::isnan(a) && std::isnan(b));
2304
0
}
2305
2306
namespace
2307
{
2308
struct ComplexFloat16
2309
{
2310
    GFloat16 r;
2311
    GFloat16 i;
2312
};
2313
}  // namespace
2314
2315
template <> bool IsSame<ComplexFloat16>(ComplexFloat16 a, ComplexFloat16 b)
2316
0
{
2317
0
    return (a.r == b.r && a.i == b.i) ||
2318
0
           (CPLIsNan(a.r) && CPLIsNan(a.i) && CPLIsNan(b.r) && CPLIsNan(b.i));
2319
0
}
2320
2321
template <>
2322
bool IsSame<std::complex<float>>(std::complex<float> a, std::complex<float> b)
2323
0
{
2324
0
    return a == b || (std::isnan(a.real()) && std::isnan(a.imag()) &&
2325
0
                      std::isnan(b.real()) && std::isnan(b.imag()));
2326
0
}
2327
2328
template <>
2329
bool IsSame<std::complex<double>>(std::complex<double> a,
2330
                                  std::complex<double> b)
2331
0
{
2332
0
    return a == b || (std::isnan(a.real()) && std::isnan(a.imag()) &&
2333
0
                      std::isnan(b.real()) && std::isnan(b.imag()));
2334
0
}
2335
2336
template <class T>
2337
static CPLErr GDALResampleChunk_ModeT(const GDALOverviewResampleArgs &args,
2338
                                      const T *pChunk, T *const pDstBuffer)
2339
2340
0
{
2341
0
    const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
2342
0
    const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
2343
0
    const double dfSrcXDelta = args.dfSrcXDelta;
2344
0
    const double dfSrcYDelta = args.dfSrcYDelta;
2345
0
    const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
2346
0
    const int nChunkXOff = args.nChunkXOff;
2347
0
    const int nChunkXSize = args.nChunkXSize;
2348
0
    const int nChunkYOff = args.nChunkYOff;
2349
0
    const int nChunkYSize = args.nChunkYSize;
2350
0
    const int nDstXOff = args.nDstXOff;
2351
0
    const int nDstXOff2 = args.nDstXOff2;
2352
0
    const int nDstYOff = args.nDstYOff;
2353
0
    const int nDstYOff2 = args.nDstYOff2;
2354
0
    const bool bHasNoData = args.bHasNoData;
2355
0
    const GDALColorTable *poColorTable = args.poColorTable;
2356
0
    const int nDstXSize = nDstXOff2 - nDstXOff;
2357
2358
0
    T tNoDataValue;
2359
    if constexpr (std::is_same<T, ComplexFloat16>::value)
2360
0
    {
2361
0
        tNoDataValue.r = cpl::NumericLimits<GFloat16>::quiet_NaN();
2362
0
        tNoDataValue.i = cpl::NumericLimits<GFloat16>::quiet_NaN();
2363
    }
2364
    else if constexpr (std::is_same<T, std::complex<float>>::value ||
2365
                       std::is_same<T, std::complex<double>>::value)
2366
0
    {
2367
0
        using BaseT = typename T::value_type;
2368
0
        tNoDataValue =
2369
0
            std::complex<BaseT>(std::numeric_limits<BaseT>::quiet_NaN(),
2370
0
                                std::numeric_limits<BaseT>::quiet_NaN());
2371
    }
2372
0
    else if (!bHasNoData || !GDALIsValueInRange<T>(args.dfNoDataValue))
2373
0
        tNoDataValue = 0;
2374
0
    else
2375
0
        tNoDataValue = static_cast<T>(args.dfNoDataValue);
2376
2377
0
    using CountType = uint32_t;
2378
0
    CountType nMaxNumPx = 0;
2379
0
    T *paVals = nullptr;
2380
0
    CountType *panCounts = nullptr;
2381
2382
0
    const int nChunkRightXOff = nChunkXOff + nChunkXSize;
2383
0
    const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
2384
0
    std::vector<int> anVals(256, 0);
2385
2386
    /* ==================================================================== */
2387
    /*      Loop over destination scanlines.                                */
2388
    /* ==================================================================== */
2389
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
2390
0
    {
2391
0
        const double dfSrcYOff = dfSrcYDelta + iDstLine * dfYRatioDstToSrc;
2392
0
        int nSrcYOff = static_cast<int>(dfSrcYOff + 1e-8);
2393
#ifdef only_pixels_with_more_than_10_pct_participation
2394
        // When oversampling, don't take into account pixels that have a tiny
2395
        // participation in the resulting pixel
2396
        if (dfYRatioDstToSrc > 1 && dfSrcYOff - nSrcYOff > 0.9 &&
2397
            nSrcYOff < nChunkBottomYOff)
2398
            nSrcYOff++;
2399
#endif
2400
0
        if (nSrcYOff < nChunkYOff)
2401
0
            nSrcYOff = nChunkYOff;
2402
2403
0
        const double dfSrcYOff2 =
2404
0
            dfSrcYDelta + (iDstLine + 1) * dfYRatioDstToSrc;
2405
0
        int nSrcYOff2 = static_cast<int>(ceil(dfSrcYOff2 - 1e-8));
2406
#ifdef only_pixels_with_more_than_10_pct_participation
2407
        // When oversampling, don't take into account pixels that have a tiny
2408
        // participation in the resulting pixel
2409
        if (dfYRatioDstToSrc > 1 && nSrcYOff2 - dfSrcYOff2 > 0.9 &&
2410
            nSrcYOff2 > nChunkYOff)
2411
            nSrcYOff2--;
2412
#endif
2413
0
        if (nSrcYOff2 == nSrcYOff)
2414
0
            ++nSrcYOff2;
2415
0
        if (nSrcYOff2 > nChunkBottomYOff)
2416
0
            nSrcYOff2 = nChunkBottomYOff;
2417
2418
0
        const T *const paSrcScanline =
2419
0
            pChunk +
2420
0
            (static_cast<GPtrDiff_t>(nSrcYOff - nChunkYOff) * nChunkXSize);
2421
0
        const GByte *pabySrcScanlineNodataMask = nullptr;
2422
0
        if (pabyChunkNodataMask != nullptr)
2423
0
            pabySrcScanlineNodataMask =
2424
0
                pabyChunkNodataMask +
2425
0
                static_cast<GPtrDiff_t>(nSrcYOff - nChunkYOff) * nChunkXSize;
2426
2427
0
        T *const paDstScanline = pDstBuffer + (iDstLine - nDstYOff) * nDstXSize;
2428
        /* --------------------------------------------------------------------
2429
         */
2430
        /*      Loop over destination pixels */
2431
        /* --------------------------------------------------------------------
2432
         */
2433
0
        for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
2434
0
        {
2435
0
            const double dfSrcXOff = dfSrcXDelta + iDstPixel * dfXRatioDstToSrc;
2436
            // Apply some epsilon to avoid numerical precision issues
2437
0
            int nSrcXOff = static_cast<int>(dfSrcXOff + 1e-8);
2438
#ifdef only_pixels_with_more_than_10_pct_participation
2439
            // When oversampling, don't take into account pixels that have a
2440
            // tiny participation in the resulting pixel
2441
            if (dfXRatioDstToSrc > 1 && dfSrcXOff - nSrcXOff > 0.9 &&
2442
                nSrcXOff < nChunkRightXOff)
2443
                nSrcXOff++;
2444
#endif
2445
0
            if (nSrcXOff < nChunkXOff)
2446
0
                nSrcXOff = nChunkXOff;
2447
2448
0
            const double dfSrcXOff2 =
2449
0
                dfSrcXDelta + (iDstPixel + 1) * dfXRatioDstToSrc;
2450
0
            int nSrcXOff2 = static_cast<int>(ceil(dfSrcXOff2 - 1e-8));
2451
#ifdef only_pixels_with_more_than_10_pct_participation
2452
            // When oversampling, don't take into account pixels that have a
2453
            // tiny participation in the resulting pixel
2454
            if (dfXRatioDstToSrc > 1 && nSrcXOff2 - dfSrcXOff2 > 0.9 &&
2455
                nSrcXOff2 > nChunkXOff)
2456
                nSrcXOff2--;
2457
#endif
2458
0
            if (nSrcXOff2 == nSrcXOff)
2459
0
                nSrcXOff2++;
2460
0
            if (nSrcXOff2 > nChunkRightXOff)
2461
0
                nSrcXOff2 = nChunkRightXOff;
2462
2463
0
            bool bRegularProcessing = false;
2464
            if constexpr (!std::is_same<T, GByte>::value)
2465
0
                bRegularProcessing = true;
2466
0
            else if (poColorTable && poColorTable->GetColorEntryCount() > 256)
2467
0
                bRegularProcessing = true;
2468
2469
0
            if (bRegularProcessing)
2470
0
            {
2471
                // Sanity check to make sure the allocation of paVals and
2472
                // panCounts don't overflow.
2473
0
                static_assert(sizeof(CountType) <= sizeof(size_t));
2474
0
                if (nSrcYOff2 - nSrcYOff <= 0 || nSrcXOff2 - nSrcXOff <= 0 ||
2475
0
                    static_cast<CountType>(nSrcYOff2 - nSrcYOff) >
2476
0
                        (std::numeric_limits<CountType>::max() /
2477
0
                         std::max(sizeof(T), sizeof(CountType))) /
2478
0
                            static_cast<CountType>(nSrcXOff2 - nSrcXOff))
2479
0
                {
2480
0
                    CPLError(CE_Failure, CPLE_NotSupported,
2481
0
                             "Too big downsampling factor");
2482
0
                    CPLFree(paVals);
2483
0
                    CPLFree(panCounts);
2484
0
                    return CE_Failure;
2485
0
                }
2486
0
                const CountType nNumPx =
2487
0
                    static_cast<CountType>(nSrcYOff2 - nSrcYOff) *
2488
0
                    (nSrcXOff2 - nSrcXOff);
2489
0
                CountType iMaxInd = 0;
2490
0
                CountType iMaxVal = 0;
2491
2492
0
                if (paVals == nullptr || nNumPx > nMaxNumPx)
2493
0
                {
2494
0
                    T *paValsNew = static_cast<T *>(
2495
0
                        VSI_REALLOC_VERBOSE(paVals, nNumPx * sizeof(T)));
2496
0
                    CountType *panCountsNew =
2497
0
                        static_cast<CountType *>(VSI_REALLOC_VERBOSE(
2498
0
                            panCounts, nNumPx * sizeof(CountType)));
2499
0
                    if (paValsNew != nullptr)
2500
0
                        paVals = paValsNew;
2501
0
                    if (panCountsNew != nullptr)
2502
0
                        panCounts = panCountsNew;
2503
0
                    if (paValsNew == nullptr || panCountsNew == nullptr)
2504
0
                    {
2505
0
                        CPLFree(paVals);
2506
0
                        CPLFree(panCounts);
2507
0
                        return CE_Failure;
2508
0
                    }
2509
0
                    nMaxNumPx = nNumPx;
2510
0
                }
2511
2512
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
2513
0
                {
2514
0
                    const GPtrDiff_t iTotYOff =
2515
0
                        static_cast<GPtrDiff_t>(iY - nSrcYOff) * nChunkXSize -
2516
0
                        nChunkXOff;
2517
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
2518
0
                    {
2519
0
                        if (pabySrcScanlineNodataMask == nullptr ||
2520
0
                            pabySrcScanlineNodataMask[iX + iTotYOff])
2521
0
                        {
2522
0
                            const T val = paSrcScanline[iX + iTotYOff];
2523
0
                            CountType i = 0;  // Used after for.
2524
2525
                            // Check array for existing entry.
2526
0
                            for (; i < iMaxInd; ++i)
2527
0
                            {
2528
0
                                if (IsSame(paVals[i], val))
2529
0
                                {
2530
0
                                    if (++panCounts[i] > panCounts[iMaxVal])
2531
0
                                    {
2532
0
                                        iMaxVal = i;
2533
0
                                    }
2534
0
                                    break;
2535
0
                                }
2536
0
                            }
2537
2538
                            // Add to arr if entry not already there.
2539
0
                            if (i == iMaxInd)
2540
0
                            {
2541
0
                                paVals[iMaxInd] = val;
2542
0
                                panCounts[iMaxInd] = 1;
2543
2544
0
                                if (iMaxInd == 0)
2545
0
                                {
2546
0
                                    iMaxVal = iMaxInd;
2547
0
                                }
2548
2549
0
                                ++iMaxInd;
2550
0
                            }
2551
0
                        }
2552
0
                    }
2553
0
                }
2554
2555
0
                if (iMaxInd == 0)
2556
0
                    paDstScanline[iDstPixel - nDstXOff] = tNoDataValue;
2557
0
                else
2558
0
                    paDstScanline[iDstPixel - nDstXOff] = paVals[iMaxVal];
2559
0
            }
2560
            else if constexpr (std::is_same<T, GByte>::value)
2561
            // ( eSrcDataType == GDT_UInt8 && nEntryCount < 256 )
2562
0
            {
2563
                // So we go here for a paletted or non-paletted byte band.
2564
                // The input values are then between 0 and 255.
2565
0
                int nMaxVal = 0;
2566
0
                int iMaxInd = -1;
2567
2568
                // The cost of this zeroing might be high. Perhaps we should
2569
                // just use the above generic case, and go to this one if the
2570
                // number of source pixels is large enough
2571
0
                std::fill(anVals.begin(), anVals.end(), 0);
2572
2573
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
2574
0
                {
2575
0
                    const GPtrDiff_t iTotYOff =
2576
0
                        static_cast<GPtrDiff_t>(iY - nSrcYOff) * nChunkXSize -
2577
0
                        nChunkXOff;
2578
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
2579
0
                    {
2580
0
                        const T val = paSrcScanline[iX + iTotYOff];
2581
0
                        if (!bHasNoData || val != tNoDataValue)
2582
0
                        {
2583
0
                            int nVal = static_cast<int>(val);
2584
0
                            if (++anVals[nVal] > nMaxVal)
2585
0
                            {
2586
                                // Sum the density.
2587
                                // Is it the most common value so far?
2588
0
                                iMaxInd = nVal;
2589
0
                                nMaxVal = anVals[nVal];
2590
0
                            }
2591
0
                        }
2592
0
                    }
2593
0
                }
2594
2595
0
                if (iMaxInd == -1)
2596
0
                    paDstScanline[iDstPixel - nDstXOff] = tNoDataValue;
2597
0
                else
2598
0
                    paDstScanline[iDstPixel - nDstXOff] =
2599
0
                        static_cast<T>(iMaxInd);
2600
0
            }
2601
0
        }
2602
0
    }
2603
2604
0
    CPLFree(paVals);
2605
0
    CPLFree(panCounts);
2606
2607
0
    return CE_None;
2608
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<unsigned char>(GDALOverviewResampleArgs const&, unsigned char const*, unsigned char*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<signed char>(GDALOverviewResampleArgs const&, signed char const*, signed char*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<unsigned short>(GDALOverviewResampleArgs const&, unsigned short const*, unsigned short*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<unsigned int>(GDALOverviewResampleArgs const&, unsigned int const*, unsigned int*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<unsigned long>(GDALOverviewResampleArgs const&, unsigned long const*, unsigned long*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<cpl::Float16>(GDALOverviewResampleArgs const&, cpl::Float16 const*, cpl::Float16*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<float>(GDALOverviewResampleArgs const&, float const*, float*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<double>(GDALOverviewResampleArgs const&, double const*, double*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<(anonymous namespace)::ComplexFloat16>(GDALOverviewResampleArgs const&, (anonymous namespace)::ComplexFloat16 const*, (anonymous namespace)::ComplexFloat16*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<std::__1::complex<float> >(GDALOverviewResampleArgs const&, std::__1::complex<float> const*, std::__1::complex<float>*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ModeT<std::__1::complex<double> >(GDALOverviewResampleArgs const&, std::__1::complex<double> const*, std::__1::complex<double>*)
2609
2610
static CPLErr GDALResampleChunk_Mode(const GDALOverviewResampleArgs &args,
2611
                                     const void *pChunk, void **ppDstBuffer,
2612
                                     GDALDataType *peDstBufferDataType)
2613
0
{
2614
0
    *ppDstBuffer = VSI_MALLOC3_VERBOSE(
2615
0
        args.nDstXOff2 - args.nDstXOff, args.nDstYOff2 - args.nDstYOff,
2616
0
        GDALGetDataTypeSizeBytes(args.eWrkDataType));
2617
0
    if (*ppDstBuffer == nullptr)
2618
0
    {
2619
0
        return CE_Failure;
2620
0
    }
2621
2622
0
    CPLAssert(args.eSrcDataType == args.eWrkDataType);
2623
2624
0
    *peDstBufferDataType = args.eWrkDataType;
2625
0
    switch (args.eWrkDataType)
2626
0
    {
2627
        // For mode resampling, as no computation is done, only the
2628
        // size of the data type matters... except for Byte where we have
2629
        // special processing. And for floating point values
2630
0
        case GDT_UInt8:
2631
0
        {
2632
0
            return GDALResampleChunk_ModeT(args,
2633
0
                                           static_cast<const GByte *>(pChunk),
2634
0
                                           static_cast<GByte *>(*ppDstBuffer));
2635
0
        }
2636
2637
0
        case GDT_Int8:
2638
0
        {
2639
0
            return GDALResampleChunk_ModeT(args,
2640
0
                                           static_cast<const int8_t *>(pChunk),
2641
0
                                           static_cast<int8_t *>(*ppDstBuffer));
2642
0
        }
2643
2644
0
        case GDT_Int16:
2645
0
        case GDT_UInt16:
2646
0
        {
2647
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 2);
2648
0
            return GDALResampleChunk_ModeT(
2649
0
                args, static_cast<const uint16_t *>(pChunk),
2650
0
                static_cast<uint16_t *>(*ppDstBuffer));
2651
0
        }
2652
2653
0
        case GDT_CInt16:
2654
0
        case GDT_Int32:
2655
0
        case GDT_UInt32:
2656
0
        {
2657
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 4);
2658
0
            return GDALResampleChunk_ModeT(
2659
0
                args, static_cast<const uint32_t *>(pChunk),
2660
0
                static_cast<uint32_t *>(*ppDstBuffer));
2661
0
        }
2662
2663
0
        case GDT_CInt32:
2664
0
        case GDT_Int64:
2665
0
        case GDT_UInt64:
2666
0
        {
2667
0
            CPLAssert(GDALGetDataTypeSizeBytes(args.eWrkDataType) == 8);
2668
0
            return GDALResampleChunk_ModeT(
2669
0
                args, static_cast<const uint64_t *>(pChunk),
2670
0
                static_cast<uint64_t *>(*ppDstBuffer));
2671
0
        }
2672
2673
0
        case GDT_Float16:
2674
0
        {
2675
0
            return GDALResampleChunk_ModeT(
2676
0
                args, static_cast<const GFloat16 *>(pChunk),
2677
0
                static_cast<GFloat16 *>(*ppDstBuffer));
2678
0
        }
2679
2680
0
        case GDT_Float32:
2681
0
        {
2682
0
            return GDALResampleChunk_ModeT(args,
2683
0
                                           static_cast<const float *>(pChunk),
2684
0
                                           static_cast<float *>(*ppDstBuffer));
2685
0
        }
2686
2687
0
        case GDT_Float64:
2688
0
        {
2689
0
            return GDALResampleChunk_ModeT(args,
2690
0
                                           static_cast<const double *>(pChunk),
2691
0
                                           static_cast<double *>(*ppDstBuffer));
2692
0
        }
2693
2694
0
        case GDT_CFloat16:
2695
0
        {
2696
0
            return GDALResampleChunk_ModeT(
2697
0
                args, static_cast<const ComplexFloat16 *>(pChunk),
2698
0
                static_cast<ComplexFloat16 *>(*ppDstBuffer));
2699
0
        }
2700
2701
0
        case GDT_CFloat32:
2702
0
        {
2703
0
            return GDALResampleChunk_ModeT(
2704
0
                args, static_cast<const std::complex<float> *>(pChunk),
2705
0
                static_cast<std::complex<float> *>(*ppDstBuffer));
2706
0
        }
2707
2708
0
        case GDT_CFloat64:
2709
0
        {
2710
0
            return GDALResampleChunk_ModeT(
2711
0
                args, static_cast<const std::complex<double> *>(pChunk),
2712
0
                static_cast<std::complex<double> *>(*ppDstBuffer));
2713
0
        }
2714
2715
0
        case GDT_Unknown:
2716
0
        case GDT_TypeCount:
2717
0
            break;
2718
0
    }
2719
2720
0
    CPLAssert(false);
2721
0
    return CE_Failure;
2722
0
}
2723
2724
/************************************************************************/
2725
/*                 GDALResampleConvolutionHorizontal()                  */
2726
/************************************************************************/
2727
2728
template <class T>
2729
static inline double
2730
GDALResampleConvolutionHorizontal(const T *pChunk, const double *padfWeights,
2731
                                  int nSrcPixelCount)
2732
0
{
2733
0
    double dfVal1 = 0.0;
2734
0
    double dfVal2 = 0.0;
2735
0
    int i = 0;  // Used after for.
2736
    // Intel Compiler 2024.0.2.29 (maybe other versions?) crashes on this
2737
    // manually (untypical) unrolled loop in -O2 and -O3:
2738
    // https://github.com/OSGeo/gdal/issues/9508
2739
0
#if !defined(__INTEL_CLANG_COMPILER)
2740
0
    for (; i < nSrcPixelCount - 3; i += 4)
2741
0
    {
2742
0
        dfVal1 += double(pChunk[i + 0]) * padfWeights[i];
2743
0
        dfVal1 += double(pChunk[i + 1]) * padfWeights[i + 1];
2744
0
        dfVal2 += double(pChunk[i + 2]) * padfWeights[i + 2];
2745
0
        dfVal2 += double(pChunk[i + 3]) * padfWeights[i + 3];
2746
0
    }
2747
0
#endif
2748
0
    for (; i < nSrcPixelCount; ++i)
2749
0
    {
2750
0
        dfVal1 += double(pChunk[i]) * padfWeights[i];
2751
0
    }
2752
0
    return dfVal1 + dfVal2;
2753
0
}
Unexecuted instantiation: overview.cpp:double GDALResampleConvolutionHorizontal<float>(float const*, double const*, int)
Unexecuted instantiation: overview.cpp:double GDALResampleConvolutionHorizontal<double>(double const*, double const*, int)
2754
2755
template <class T, bool bHasNaN>
2756
static inline void GDALResampleConvolutionHorizontalWithMask(
2757
    const T *pChunk, const GByte *pabyMask, const double *padfWeights,
2758
    int nSrcPixelCount, double &dfWeightValMaskSum, double &dfWeightMaskSum,
2759
    double &dfWeightSum)
2760
0
{
2761
0
    dfWeightValMaskSum = 0;
2762
0
    dfWeightMaskSum = 0;
2763
0
    dfWeightSum = 0;
2764
0
    int i = 0;
2765
0
    for (; i < nSrcPixelCount - 3; i += 4)
2766
0
    {
2767
0
        double dfWeightMask0 = padfWeights[i + 0] * pabyMask[i + 0];
2768
0
        double dfWeightMask1 = padfWeights[i + 1] * pabyMask[i + 1];
2769
0
        double dfWeightMask2 = padfWeights[i + 2] * pabyMask[i + 2];
2770
0
        double dfWeightMask3 = padfWeights[i + 3] * pabyMask[i + 3];
2771
2772
0
        const auto MulNaNAware = [](double v, double &w, double &val)
2773
0
        {
2774
            if constexpr (bHasNaN)
2775
0
            {
2776
0
                if (std::isnan(v))
2777
0
                {
2778
0
                    w = 0;
2779
0
                    return;
2780
0
                }
2781
0
            }
2782
0
            val += v * w;
2783
0
        };
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontalWithMask<float, true>(float const*, unsigned char const*, double const*, int, double&, double&, double&)::{lambda(double, double&, double&)#1}::operator()(double, double&, double&) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontalWithMask<float, false>(float const*, unsigned char const*, double const*, int, double&, double&, double&)::{lambda(double, double&, double&)#1}::operator()(double, double&, double&) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontalWithMask<double, true>(double const*, unsigned char const*, double const*, int, double&, double&, double&)::{lambda(double, double&, double&)#1}::operator()(double, double&, double&) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontalWithMask<double, false>(double const*, unsigned char const*, double const*, int, double&, double&, double&)::{lambda(double, double&, double&)#1}::operator()(double, double&, double&) const
2784
2785
0
        MulNaNAware(double(pChunk[i + 0]), dfWeightMask0, dfWeightValMaskSum);
2786
0
        MulNaNAware(double(pChunk[i + 1]), dfWeightMask1, dfWeightValMaskSum);
2787
0
        MulNaNAware(double(pChunk[i + 2]), dfWeightMask2, dfWeightValMaskSum);
2788
0
        MulNaNAware(double(pChunk[i + 3]), dfWeightMask3, dfWeightValMaskSum);
2789
0
        dfWeightMaskSum +=
2790
0
            dfWeightMask0 + dfWeightMask1 + dfWeightMask2 + dfWeightMask3;
2791
0
        dfWeightSum += padfWeights[i + 0] + padfWeights[i + 1] +
2792
0
                       padfWeights[i + 2] + padfWeights[i + 3];
2793
0
    }
2794
0
    for (; i < nSrcPixelCount; ++i)
2795
0
    {
2796
0
        const double dfWeightMask = padfWeights[i] * pabyMask[i];
2797
        if constexpr (bHasNaN)
2798
0
        {
2799
0
            if (!std::isnan(pChunk[i]))
2800
0
            {
2801
0
                dfWeightValMaskSum += double(pChunk[i]) * dfWeightMask;
2802
0
                dfWeightMaskSum += dfWeightMask;
2803
0
                dfWeightSum += padfWeights[i];
2804
0
            }
2805
        }
2806
        else
2807
0
        {
2808
0
            dfWeightValMaskSum += double(pChunk[i]) * dfWeightMask;
2809
0
            dfWeightMaskSum += dfWeightMask;
2810
0
            dfWeightSum += padfWeights[i];
2811
0
        }
2812
0
    }
2813
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMask<float, true>(float const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMask<float, false>(float const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMask<double, true>(double const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMask<double, false>(double const*, unsigned char const*, double const*, int, double&, double&, double&)
2814
2815
template <class T, bool bHasNaN>
2816
static inline void GDALResampleConvolutionHorizontal_3rows(
2817
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2818
    const double *padfWeights, int nSrcPixelCount, double &dfRes1,
2819
    double &dfRes2, double &dfRes3)
2820
0
{
2821
0
    double dfVal1 = 0.0;
2822
0
    double dfVal2 = 0.0;
2823
0
    double dfVal3 = 0.0;
2824
0
    double dfVal4 = 0.0;
2825
0
    double dfVal5 = 0.0;
2826
0
    double dfVal6 = 0.0;
2827
0
    int i = 0;  // Used after for.
2828
2829
0
    const auto MulNaNAware = [](double a, double w)
2830
0
    {
2831
        if constexpr (bHasNaN)
2832
0
        {
2833
0
            if (std::isnan(a))
2834
0
                return 0.0;
2835
0
        }
2836
0
        return a * w;
2837
0
    };
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontal_3rows<float, true>(float const*, float const*, float const*, double const*, int, double&, double&, double&)::{lambda(double, double)#1}::operator()(double, double) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontal_3rows<float, false>(float const*, float const*, float const*, double const*, int, double&, double&, double&)::{lambda(double, double)#1}::operator()(double, double) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontal_3rows<double, true>(double const*, double const*, double const*, double const*, int, double&, double&, double&)::{lambda(double, double)#1}::operator()(double, double) const
Unexecuted instantiation: overview.cpp:GDALResampleConvolutionHorizontal_3rows<double, false>(double const*, double const*, double const*, double const*, int, double&, double&, double&)::{lambda(double, double)#1}::operator()(double, double) const
2838
2839
0
    for (; i < nSrcPixelCount - 3; i += 4)
2840
0
    {
2841
0
        dfVal1 += MulNaNAware(double(pChunkRow1[i + 0]), padfWeights[i + 0]);
2842
0
        dfVal1 += MulNaNAware(double(pChunkRow1[i + 1]), padfWeights[i + 1]);
2843
0
        dfVal2 += MulNaNAware(double(pChunkRow1[i + 2]), padfWeights[i + 2]);
2844
0
        dfVal2 += MulNaNAware(double(pChunkRow1[i + 3]), padfWeights[i + 3]);
2845
0
        dfVal3 += MulNaNAware(double(pChunkRow2[i + 0]), padfWeights[i + 0]);
2846
0
        dfVal3 += MulNaNAware(double(pChunkRow2[i + 1]), padfWeights[i + 1]);
2847
0
        dfVal4 += MulNaNAware(double(pChunkRow2[i + 2]), padfWeights[i + 2]);
2848
0
        dfVal4 += MulNaNAware(double(pChunkRow2[i + 3]), padfWeights[i + 3]);
2849
0
        dfVal5 += MulNaNAware(double(pChunkRow3[i + 0]), padfWeights[i + 0]);
2850
0
        dfVal5 += MulNaNAware(double(pChunkRow3[i + 1]), padfWeights[i + 1]);
2851
0
        dfVal6 += MulNaNAware(double(pChunkRow3[i + 2]), padfWeights[i + 2]);
2852
0
        dfVal6 += MulNaNAware(double(pChunkRow3[i + 3]), padfWeights[i + 3]);
2853
0
    }
2854
0
    for (; i < nSrcPixelCount; ++i)
2855
0
    {
2856
0
        dfVal1 += MulNaNAware(double(pChunkRow1[i]), padfWeights[i]);
2857
0
        dfVal3 += MulNaNAware(double(pChunkRow2[i]), padfWeights[i]);
2858
0
        dfVal5 += MulNaNAware(double(pChunkRow3[i]), padfWeights[i]);
2859
0
    }
2860
0
    dfRes1 = dfVal1 + dfVal2;
2861
0
    dfRes2 = dfVal3 + dfVal4;
2862
0
    dfRes3 = dfVal5 + dfVal6;
2863
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows<float, true>(float const*, float const*, float const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows<float, false>(float const*, float const*, float const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows<double, true>(double const*, double const*, double const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows<double, false>(double const*, double const*, double const*, double const*, int, double&, double&, double&)
2864
2865
template <class T, bool bHasNaN>
2866
static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows(
2867
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2868
    const double *padfWeights, int nSrcPixelCount, double &dfRes1,
2869
    double &dfRes2, double &dfRes3)
2870
0
{
2871
0
    GDALResampleConvolutionHorizontal_3rows<T, bHasNaN>(
2872
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeights, nSrcPixelCount, dfRes1,
2873
0
        dfRes2, dfRes3);
2874
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<float, true>(float const*, float const*, float const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<float, false>(float const*, float const*, float const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<double, true>(double const*, double const*, double const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<double, false>(double const*, double const*, double const*, double const*, int, double&, double&, double&)
2875
2876
template <class T, bool bHasNaN>
2877
static inline void GDALResampleConvolutionHorizontalPixelCount4_3rows(
2878
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
2879
    const double *padfWeights, double &dfRes1, double &dfRes2, double &dfRes3)
2880
0
{
2881
0
    GDALResampleConvolutionHorizontal_3rows<T, bHasNaN>(
2882
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeights, 4, dfRes1, dfRes2,
2883
0
        dfRes3);
2884
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows<float, true>(float const*, float const*, float const*, double const*, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows<float, false>(float const*, float const*, float const*, double const*, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows<double, true>(double const*, double const*, double const*, double const*, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows<double, false>(double const*, double const*, double const*, double const*, double&, double&, double&)
2885
2886
/************************************************************************/
2887
/*                  GDALResampleConvolutionVertical()                   */
2888
/************************************************************************/
2889
2890
template <class T>
2891
static inline double
2892
GDALResampleConvolutionVertical(const T *pChunk, size_t nStride,
2893
                                const double *padfWeights, int nSrcLineCount)
2894
0
{
2895
0
    double dfVal1 = 0.0;
2896
0
    double dfVal2 = 0.0;
2897
0
    int i = 0;
2898
0
    size_t j = 0;
2899
0
    for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2900
0
    {
2901
0
        dfVal1 += pChunk[j + 0 * nStride] * padfWeights[i + 0];
2902
0
        dfVal1 += pChunk[j + 1 * nStride] * padfWeights[i + 1];
2903
0
        dfVal2 += pChunk[j + 2 * nStride] * padfWeights[i + 2];
2904
0
        dfVal2 += pChunk[j + 3 * nStride] * padfWeights[i + 3];
2905
0
    }
2906
0
    for (; i < nSrcLineCount; ++i, j += nStride)
2907
0
    {
2908
0
        dfVal1 += pChunk[j] * padfWeights[i];
2909
0
    }
2910
0
    return dfVal1 + dfVal2;
2911
0
}
2912
2913
template <class T>
2914
static inline void GDALResampleConvolutionVertical_2cols(
2915
    const T *pChunk, size_t nStride, const double *padfWeights,
2916
    int nSrcLineCount, double &dfRes1, double &dfRes2)
2917
0
{
2918
0
    double dfVal1 = 0.0;
2919
0
    double dfVal2 = 0.0;
2920
0
    double dfVal3 = 0.0;
2921
0
    double dfVal4 = 0.0;
2922
0
    int i = 0;
2923
0
    size_t j = 0;
2924
0
    for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2925
0
    {
2926
0
        dfVal1 += pChunk[j + 0 + 0 * nStride] * padfWeights[i + 0];
2927
0
        dfVal3 += pChunk[j + 1 + 0 * nStride] * padfWeights[i + 0];
2928
0
        dfVal1 += pChunk[j + 0 + 1 * nStride] * padfWeights[i + 1];
2929
0
        dfVal3 += pChunk[j + 1 + 1 * nStride] * padfWeights[i + 1];
2930
0
        dfVal2 += pChunk[j + 0 + 2 * nStride] * padfWeights[i + 2];
2931
0
        dfVal4 += pChunk[j + 1 + 2 * nStride] * padfWeights[i + 2];
2932
0
        dfVal2 += pChunk[j + 0 + 3 * nStride] * padfWeights[i + 3];
2933
0
        dfVal4 += pChunk[j + 1 + 3 * nStride] * padfWeights[i + 3];
2934
0
    }
2935
0
    for (; i < nSrcLineCount; ++i, j += nStride)
2936
0
    {
2937
0
        dfVal1 += pChunk[j + 0] * padfWeights[i];
2938
0
        dfVal3 += pChunk[j + 1] * padfWeights[i];
2939
0
    }
2940
0
    dfRes1 = dfVal1 + dfVal2;
2941
0
    dfRes2 = dfVal3 + dfVal4;
2942
0
}
2943
2944
#ifdef USE_SSE2
2945
2946
#ifdef __AVX__
2947
/************************************************************************/
2948
/*              GDALResampleConvolutionVertical_16cols<T>               */
2949
/************************************************************************/
2950
2951
template <class T>
2952
static inline void
2953
GDALResampleConvolutionVertical_16cols(const T *pChunk, size_t nStride,
2954
                                       const double *padfWeights,
2955
                                       int nSrcLineCount, float *afDest)
2956
{
2957
    int i = 0;
2958
    size_t j = 0;
2959
    XMMReg4Double v_acc0 = XMMReg4Double::Zero();
2960
    XMMReg4Double v_acc1 = XMMReg4Double::Zero();
2961
    XMMReg4Double v_acc2 = XMMReg4Double::Zero();
2962
    XMMReg4Double v_acc3 = XMMReg4Double::Zero();
2963
    for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
2964
    {
2965
        XMMReg4Double w0 =
2966
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 0);
2967
        XMMReg4Double w1 =
2968
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 1);
2969
        XMMReg4Double w2 =
2970
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 2);
2971
        XMMReg4Double w3 =
2972
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 3);
2973
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 0 * nStride) * w0;
2974
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 0 * nStride) * w0;
2975
        v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 0 * nStride) * w0;
2976
        v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 0 * nStride) * w0;
2977
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 1 * nStride) * w1;
2978
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 1 * nStride) * w1;
2979
        v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 1 * nStride) * w1;
2980
        v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 1 * nStride) * w1;
2981
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 2 * nStride) * w2;
2982
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 2 * nStride) * w2;
2983
        v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 2 * nStride) * w2;
2984
        v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 2 * nStride) * w2;
2985
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 3 * nStride) * w3;
2986
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 3 * nStride) * w3;
2987
        v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8 + 3 * nStride) * w3;
2988
        v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12 + 3 * nStride) * w3;
2989
    }
2990
    for (; i < nSrcLineCount; ++i, j += nStride)
2991
    {
2992
        XMMReg4Double w = XMMReg4Double::Load1ValHighAndLow(padfWeights + i);
2993
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0) * w;
2994
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4) * w;
2995
        v_acc2 += XMMReg4Double::Load4Val(pChunk + j + 8) * w;
2996
        v_acc3 += XMMReg4Double::Load4Val(pChunk + j + 12) * w;
2997
    }
2998
    v_acc0.Store4Val(afDest);
2999
    v_acc1.Store4Val(afDest + 4);
3000
    v_acc2.Store4Val(afDest + 8);
3001
    v_acc3.Store4Val(afDest + 12);
3002
}
3003
3004
template <class T>
3005
static inline void GDALResampleConvolutionVertical_16cols(const T *, int,
3006
                                                          const double *, int,
3007
                                                          double *)
3008
{
3009
    // Cannot be reached
3010
    CPLAssert(false);
3011
}
3012
3013
#else
3014
3015
/************************************************************************/
3016
/*               GDALResampleConvolutionVertical_8cols<T>               */
3017
/************************************************************************/
3018
3019
template <class T>
3020
static inline void
3021
GDALResampleConvolutionVertical_8cols(const T *pChunk, size_t nStride,
3022
                                      const double *padfWeights,
3023
                                      int nSrcLineCount, float *afDest)
3024
0
{
3025
0
    int i = 0;
3026
0
    size_t j = 0;
3027
0
    XMMReg4Double v_acc0 = XMMReg4Double::Zero();
3028
0
    XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3029
0
    for (; i < nSrcLineCount - 3; i += 4, j += 4 * nStride)
3030
0
    {
3031
0
        XMMReg4Double w0 =
3032
0
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 0);
3033
0
        XMMReg4Double w1 =
3034
0
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 1);
3035
0
        XMMReg4Double w2 =
3036
0
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 2);
3037
0
        XMMReg4Double w3 =
3038
0
            XMMReg4Double::Load1ValHighAndLow(padfWeights + i + 3);
3039
0
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 0 * nStride) * w0;
3040
0
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 0 * nStride) * w0;
3041
0
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 1 * nStride) * w1;
3042
0
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 1 * nStride) * w1;
3043
0
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 2 * nStride) * w2;
3044
0
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 2 * nStride) * w2;
3045
0
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0 + 3 * nStride) * w3;
3046
0
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4 + 3 * nStride) * w3;
3047
0
    }
3048
0
    for (; i < nSrcLineCount; ++i, j += nStride)
3049
0
    {
3050
0
        XMMReg4Double w = XMMReg4Double::Load1ValHighAndLow(padfWeights + i);
3051
0
        v_acc0 += XMMReg4Double::Load4Val(pChunk + j + 0) * w;
3052
0
        v_acc1 += XMMReg4Double::Load4Val(pChunk + j + 4) * w;
3053
0
    }
3054
0
    v_acc0.Store4Val(afDest);
3055
0
    v_acc1.Store4Val(afDest + 4);
3056
0
}
3057
3058
template <class T>
3059
static inline void GDALResampleConvolutionVertical_8cols(const T *, int,
3060
                                                         const double *, int,
3061
                                                         double *)
3062
{
3063
    // Cannot be reached
3064
    CPLAssert(false);
3065
}
3066
3067
#endif  // __AVX__
3068
3069
/************************************************************************/
3070
/*               GDALResampleConvolutionHorizontalSSE2<T>               */
3071
/************************************************************************/
3072
3073
template <class T>
3074
static inline double GDALResampleConvolutionHorizontalSSE2(
3075
    const T *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3076
0
{
3077
0
    XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3078
0
    XMMReg4Double v_acc2 = XMMReg4Double::Zero();
3079
0
    int i = 0;  // Used after for.
3080
0
    for (; i < nSrcPixelCount - 7; i += 8)
3081
0
    {
3082
        // Retrieve the pixel & accumulate
3083
0
        const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunk + i);
3084
0
        const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunk + i + 4);
3085
0
        const XMMReg4Double v_weight1 =
3086
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3087
0
        const XMMReg4Double v_weight2 =
3088
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i + 4);
3089
3090
0
        v_acc1 += v_pixels1 * v_weight1;
3091
0
        v_acc2 += v_pixels2 * v_weight2;
3092
0
    }
3093
3094
0
    v_acc1 += v_acc2;
3095
3096
0
    double dfVal = v_acc1.GetHorizSum();
3097
0
    for (; i < nSrcPixelCount; ++i)
3098
0
    {
3099
0
        dfVal += pChunk[i] * padfWeightsAligned[i];
3100
0
    }
3101
0
    return dfVal;
3102
0
}
Unexecuted instantiation: overview.cpp:double GDALResampleConvolutionHorizontalSSE2<unsigned char>(unsigned char const*, double const*, int)
Unexecuted instantiation: overview.cpp:double GDALResampleConvolutionHorizontalSSE2<unsigned short>(unsigned short const*, double const*, int)
3103
3104
/************************************************************************/
3105
/*               GDALResampleConvolutionHorizontal<GByte>               */
3106
/************************************************************************/
3107
3108
template <>
3109
inline double GDALResampleConvolutionHorizontal<GByte>(
3110
    const GByte *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3111
0
{
3112
0
    return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned,
3113
0
                                                 nSrcPixelCount);
3114
0
}
3115
3116
template <>
3117
inline double GDALResampleConvolutionHorizontal<GUInt16>(
3118
    const GUInt16 *pChunk, const double *padfWeightsAligned, int nSrcPixelCount)
3119
0
{
3120
0
    return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned,
3121
0
                                                 nSrcPixelCount);
3122
0
}
3123
3124
/************************************************************************/
3125
/*           GDALResampleConvolutionHorizontalWithMaskSSE2<T>           */
3126
/************************************************************************/
3127
3128
template <class T>
3129
static inline void GDALResampleConvolutionHorizontalWithMaskSSE2(
3130
    const T *pChunk, const GByte *pabyMask, const double *padfWeightsAligned,
3131
    int nSrcPixelCount, double &dfWeightValMaskSum, double &dfWeightMaskSum,
3132
    double &dfWeightSum)
3133
0
{
3134
0
    int i = 0;  // Used after for.
3135
0
    XMMReg4Double v_acc_val_mask_weight = XMMReg4Double::Zero();
3136
0
    XMMReg4Double v_acc_mask_weight = XMMReg4Double::Zero();
3137
0
    XMMReg4Double v_acc_weight = XMMReg4Double::Zero();
3138
0
    for (; i < nSrcPixelCount - 3; i += 4)
3139
0
    {
3140
0
        const XMMReg4Double v_pixels = XMMReg4Double::Load4Val(pChunk + i);
3141
0
        const XMMReg4Double v_mask = XMMReg4Double::Load4Val(pabyMask + i);
3142
0
        XMMReg4Double v_weight =
3143
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3144
0
        v_acc_weight += v_weight;
3145
0
        v_weight *= v_mask;
3146
0
        v_acc_val_mask_weight += v_pixels * v_weight;
3147
0
        v_acc_mask_weight += v_weight;
3148
0
    }
3149
3150
0
    dfWeightValMaskSum = v_acc_val_mask_weight.GetHorizSum();
3151
0
    dfWeightMaskSum = v_acc_mask_weight.GetHorizSum();
3152
0
    dfWeightSum = v_acc_weight.GetHorizSum();
3153
0
    for (; i < nSrcPixelCount; ++i)
3154
0
    {
3155
0
        const double dfWeight = padfWeightsAligned[i];
3156
0
        const double dfWeightMask = dfWeight * pabyMask[i];
3157
0
        dfWeightValMaskSum += pChunk[i] * dfWeightMask;
3158
0
        dfWeightMaskSum += dfWeightMask;
3159
0
        dfWeightSum += dfWeight;
3160
0
    }
3161
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMaskSSE2<unsigned char>(unsigned char const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalWithMaskSSE2<unsigned short>(unsigned short const*, unsigned char const*, double const*, int, double&, double&, double&)
3162
3163
/************************************************************************/
3164
/*           GDALResampleConvolutionHorizontalWithMask<GByte>           */
3165
/************************************************************************/
3166
3167
template <>
3168
inline void GDALResampleConvolutionHorizontalWithMask<GByte, false>(
3169
    const GByte *pChunk, const GByte *pabyMask,
3170
    const double *padfWeightsAligned, int nSrcPixelCount,
3171
    double &dfWeightValMaskSum, double &dfWeightMaskSum, double &dfWeightSum)
3172
0
{
3173
0
    GDALResampleConvolutionHorizontalWithMaskSSE2(
3174
0
        pChunk, pabyMask, padfWeightsAligned, nSrcPixelCount,
3175
0
        dfWeightValMaskSum, dfWeightMaskSum, dfWeightSum);
3176
0
}
3177
3178
template <>
3179
inline void GDALResampleConvolutionHorizontalWithMask<GUInt16, false>(
3180
    const GUInt16 *pChunk, const GByte *pabyMask,
3181
    const double *padfWeightsAligned, int nSrcPixelCount,
3182
    double &dfWeightValMaskSum, double &dfWeightMaskSum, double &dfWeightSum)
3183
0
{
3184
0
    GDALResampleConvolutionHorizontalWithMaskSSE2(
3185
0
        pChunk, pabyMask, padfWeightsAligned, nSrcPixelCount,
3186
0
        dfWeightValMaskSum, dfWeightMaskSum, dfWeightSum);
3187
0
}
3188
3189
/************************************************************************/
3190
/*           GDALResampleConvolutionHorizontal_3rows_SSE2<T>            */
3191
/************************************************************************/
3192
3193
template <class T>
3194
static inline void GDALResampleConvolutionHorizontal_3rows_SSE2(
3195
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3196
    const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3197
    double &dfRes2, double &dfRes3)
3198
0
{
3199
0
    XMMReg4Double v_acc1 = XMMReg4Double::Zero(),
3200
0
                  v_acc2 = XMMReg4Double::Zero(),
3201
0
                  v_acc3 = XMMReg4Double::Zero();
3202
0
    int i = 0;
3203
0
    for (; i < nSrcPixelCount - 7; i += 8)
3204
0
    {
3205
        // Retrieve the pixel & accumulate.
3206
0
        XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1 + i);
3207
0
        XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow1 + i + 4);
3208
0
        const XMMReg4Double v_weight1 =
3209
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3210
0
        const XMMReg4Double v_weight2 =
3211
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i + 4);
3212
3213
0
        v_acc1 += v_pixels1 * v_weight1;
3214
0
        v_acc1 += v_pixels2 * v_weight2;
3215
3216
0
        v_pixels1 = XMMReg4Double::Load4Val(pChunkRow2 + i);
3217
0
        v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2 + i + 4);
3218
0
        v_acc2 += v_pixels1 * v_weight1;
3219
0
        v_acc2 += v_pixels2 * v_weight2;
3220
3221
0
        v_pixels1 = XMMReg4Double::Load4Val(pChunkRow3 + i);
3222
0
        v_pixels2 = XMMReg4Double::Load4Val(pChunkRow3 + i + 4);
3223
0
        v_acc3 += v_pixels1 * v_weight1;
3224
0
        v_acc3 += v_pixels2 * v_weight2;
3225
0
    }
3226
3227
0
    dfRes1 = v_acc1.GetHorizSum();
3228
0
    dfRes2 = v_acc2.GetHorizSum();
3229
0
    dfRes3 = v_acc3.GetHorizSum();
3230
0
    for (; i < nSrcPixelCount; ++i)
3231
0
    {
3232
0
        dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
3233
0
        dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
3234
0
        dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
3235
0
    }
3236
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows_SSE2<unsigned char>(unsigned char const*, unsigned char const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontal_3rows_SSE2<unsigned short>(unsigned short const*, unsigned short const*, unsigned short const*, double const*, int, double&, double&, double&)
3237
3238
/************************************************************************/
3239
/*            GDALResampleConvolutionHorizontal_3rows<GByte>            */
3240
/************************************************************************/
3241
3242
template <>
3243
inline void GDALResampleConvolutionHorizontal_3rows<GByte, false>(
3244
    const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3245
    const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3246
    double &dfRes2, double &dfRes3)
3247
0
{
3248
0
    GDALResampleConvolutionHorizontal_3rows_SSE2(
3249
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3250
0
        dfRes1, dfRes2, dfRes3);
3251
0
}
3252
3253
template <>
3254
inline void GDALResampleConvolutionHorizontal_3rows<GUInt16, false>(
3255
    const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3256
    const GUInt16 *pChunkRow3, const double *padfWeightsAligned,
3257
    int nSrcPixelCount, double &dfRes1, double &dfRes2, double &dfRes3)
3258
0
{
3259
0
    GDALResampleConvolutionHorizontal_3rows_SSE2(
3260
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3261
0
        dfRes1, dfRes2, dfRes3);
3262
0
}
3263
3264
/************************************************************************/
3265
/*    GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2<T>    */
3266
/************************************************************************/
3267
3268
template <class T>
3269
static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3270
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3271
    const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3272
    double &dfRes2, double &dfRes3)
3273
0
{
3274
0
    XMMReg4Double v_acc1 = XMMReg4Double::Zero();
3275
0
    XMMReg4Double v_acc2 = XMMReg4Double::Zero();
3276
0
    XMMReg4Double v_acc3 = XMMReg4Double::Zero();
3277
0
    int i = 0;  // Use after for.
3278
0
    for (; i < nSrcPixelCount - 3; i += 4)
3279
0
    {
3280
        // Retrieve the pixel & accumulate.
3281
0
        const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1 + i);
3282
0
        const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2 + i);
3283
0
        const XMMReg4Double v_pixels3 = XMMReg4Double::Load4Val(pChunkRow3 + i);
3284
0
        const XMMReg4Double v_weight =
3285
0
            XMMReg4Double::Load4ValAligned(padfWeightsAligned + i);
3286
3287
0
        v_acc1 += v_pixels1 * v_weight;
3288
0
        v_acc2 += v_pixels2 * v_weight;
3289
0
        v_acc3 += v_pixels3 * v_weight;
3290
0
    }
3291
3292
0
    dfRes1 = v_acc1.GetHorizSum();
3293
0
    dfRes2 = v_acc2.GetHorizSum();
3294
0
    dfRes3 = v_acc3.GetHorizSum();
3295
3296
0
    for (; i < nSrcPixelCount; ++i)
3297
0
    {
3298
0
        dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
3299
0
        dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
3300
0
        dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
3301
0
    }
3302
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2<unsigned char>(unsigned char const*, unsigned char const*, unsigned char const*, double const*, int, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2<unsigned short>(unsigned short const*, unsigned short const*, unsigned short const*, double const*, int, double&, double&, double&)
3303
3304
/************************************************************************/
3305
/*    GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte>     */
3306
/************************************************************************/
3307
3308
template <>
3309
inline void
3310
GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte, false>(
3311
    const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3312
    const double *padfWeightsAligned, int nSrcPixelCount, double &dfRes1,
3313
    double &dfRes2, double &dfRes3)
3314
0
{
3315
0
    GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3316
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3317
0
        dfRes1, dfRes2, dfRes3);
3318
0
}
3319
3320
template <>
3321
inline void
3322
GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GUInt16, false>(
3323
    const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3324
    const GUInt16 *pChunkRow3, const double *padfWeightsAligned,
3325
    int nSrcPixelCount, double &dfRes1, double &dfRes2, double &dfRes3)
3326
0
{
3327
0
    GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
3328
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, nSrcPixelCount,
3329
0
        dfRes1, dfRes2, dfRes3);
3330
0
}
3331
3332
/************************************************************************/
3333
/*      GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2<T>      */
3334
/************************************************************************/
3335
3336
template <class T>
3337
static inline void GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3338
    const T *pChunkRow1, const T *pChunkRow2, const T *pChunkRow3,
3339
    const double *padfWeightsAligned, double &dfRes1, double &dfRes2,
3340
    double &dfRes3)
3341
0
{
3342
0
    const XMMReg4Double v_weight =
3343
0
        XMMReg4Double::Load4ValAligned(padfWeightsAligned);
3344
3345
    // Retrieve the pixel & accumulate.
3346
0
    const XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1);
3347
0
    const XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2);
3348
0
    const XMMReg4Double v_pixels3 = XMMReg4Double::Load4Val(pChunkRow3);
3349
3350
0
    XMMReg4Double v_acc1 = v_pixels1 * v_weight;
3351
0
    XMMReg4Double v_acc2 = v_pixels2 * v_weight;
3352
0
    XMMReg4Double v_acc3 = v_pixels3 * v_weight;
3353
3354
0
    dfRes1 = v_acc1.GetHorizSum();
3355
0
    dfRes2 = v_acc2.GetHorizSum();
3356
0
    dfRes3 = v_acc3.GetHorizSum();
3357
0
}
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2<unsigned char>(unsigned char const*, unsigned char const*, unsigned char const*, double const*, double&, double&, double&)
Unexecuted instantiation: overview.cpp:void GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2<unsigned short>(unsigned short const*, unsigned short const*, unsigned short const*, double const*, double&, double&, double&)
3358
3359
/************************************************************************/
3360
/*      GDALResampleConvolutionHorizontalPixelCount4_3rows<GByte>       */
3361
/************************************************************************/
3362
3363
template <>
3364
inline void GDALResampleConvolutionHorizontalPixelCount4_3rows<GByte, false>(
3365
    const GByte *pChunkRow1, const GByte *pChunkRow2, const GByte *pChunkRow3,
3366
    const double *padfWeightsAligned, double &dfRes1, double &dfRes2,
3367
    double &dfRes3)
3368
0
{
3369
0
    GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3370
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, dfRes1, dfRes2,
3371
0
        dfRes3);
3372
0
}
3373
3374
template <>
3375
inline void GDALResampleConvolutionHorizontalPixelCount4_3rows<GUInt16, false>(
3376
    const GUInt16 *pChunkRow1, const GUInt16 *pChunkRow2,
3377
    const GUInt16 *pChunkRow3, const double *padfWeightsAligned, double &dfRes1,
3378
    double &dfRes2, double &dfRes3)
3379
0
{
3380
0
    GDALResampleConvolutionHorizontalPixelCount4_3rows_SSE2(
3381
0
        pChunkRow1, pChunkRow2, pChunkRow3, padfWeightsAligned, dfRes1, dfRes2,
3382
0
        dfRes3);
3383
0
}
3384
3385
#endif  // USE_SSE2
3386
3387
/************************************************************************/
3388
/*                   GDALResampleChunk_Convolution()                    */
3389
/************************************************************************/
3390
3391
template <class T, class Twork, GDALDataType eWrkDataType,
3392
          bool bKernelWithNegativeWeights, bool bNeedRescale>
3393
static CPLErr GDALResampleChunk_ConvolutionT(
3394
    const GDALOverviewResampleArgs &args, const T *pChunk, void *pDstBuffer,
3395
    FilterFuncType pfnFilterFunc, FilterFunc4ValuesType pfnFilterFunc4Values,
3396
    int nKernelRadius, float fMaxVal)
3397
3398
0
{
3399
0
    const double dfXRatioDstToSrc = args.dfXRatioDstToSrc;
3400
0
    const double dfYRatioDstToSrc = args.dfYRatioDstToSrc;
3401
0
    const double dfSrcXDelta = args.dfSrcXDelta;
3402
0
    const double dfSrcYDelta = args.dfSrcYDelta;
3403
0
    constexpr int nBands = 1;
3404
0
    const GByte *pabyChunkNodataMask = args.pabyChunkNodataMask;
3405
0
    const int nChunkXOff = args.nChunkXOff;
3406
0
    const int nChunkXSize = args.nChunkXSize;
3407
0
    const int nChunkYOff = args.nChunkYOff;
3408
0
    const int nChunkYSize = args.nChunkYSize;
3409
0
    const int nDstXOff = args.nDstXOff;
3410
0
    const int nDstXOff2 = args.nDstXOff2;
3411
0
    const int nDstYOff = args.nDstYOff;
3412
0
    const int nDstYOff2 = args.nDstYOff2;
3413
0
    const bool bHasNoData = args.bHasNoData;
3414
0
    double dfNoDataValue = args.dfNoDataValue;
3415
3416
0
    if (!bHasNoData)
3417
0
        dfNoDataValue = 0.0;
3418
0
    const auto dstDataType = args.eOvrDataType;
3419
0
    const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(dstDataType);
3420
0
    const double dfReplacementVal =
3421
0
        bHasNoData ? GDALGetNoDataReplacementValue(dstDataType, dfNoDataValue)
3422
0
                   : dfNoDataValue;
3423
    // cppcheck-suppress unreadVariable
3424
0
    const int isIntegerDT = GDALDataTypeIsInteger(dstDataType);
3425
0
    const bool bNoDataValueInt64Valid =
3426
0
        isIntegerDT && GDALIsValueExactAs<GInt64>(dfNoDataValue);
3427
0
    const auto nNodataValueInt64 =
3428
0
        bNoDataValueInt64Valid ? static_cast<GInt64>(dfNoDataValue) : 0;
3429
0
    constexpr int nWrkDataTypeSize = static_cast<int>(sizeof(Twork));
3430
3431
    // TODO: we should have some generic function to do this.
3432
0
    Twork fDstMin = cpl::NumericLimits<Twork>::lowest();
3433
0
    Twork fDstMax = cpl::NumericLimits<Twork>::max();
3434
0
    if (dstDataType == GDT_UInt8)
3435
0
    {
3436
0
        fDstMin = std::numeric_limits<GByte>::min();
3437
0
        fDstMax = std::numeric_limits<GByte>::max();
3438
0
    }
3439
0
    else if (dstDataType == GDT_Int8)
3440
0
    {
3441
0
        fDstMin = std::numeric_limits<GInt8>::min();
3442
0
        fDstMax = std::numeric_limits<GInt8>::max();
3443
0
    }
3444
0
    else if (dstDataType == GDT_UInt16)
3445
0
    {
3446
0
        fDstMin = std::numeric_limits<GUInt16>::min();
3447
0
        fDstMax = std::numeric_limits<GUInt16>::max();
3448
0
    }
3449
0
    else if (dstDataType == GDT_Int16)
3450
0
    {
3451
0
        fDstMin = std::numeric_limits<GInt16>::min();
3452
0
        fDstMax = std::numeric_limits<GInt16>::max();
3453
0
    }
3454
0
    else if (dstDataType == GDT_UInt32)
3455
0
    {
3456
0
        fDstMin = static_cast<Twork>(std::numeric_limits<GUInt32>::min());
3457
0
        fDstMax = static_cast<Twork>(std::numeric_limits<GUInt32>::max());
3458
0
    }
3459
0
    else if (dstDataType == GDT_Int32)
3460
0
    {
3461
        // cppcheck-suppress unreadVariable
3462
0
        fDstMin = static_cast<Twork>(std::numeric_limits<GInt32>::min());
3463
        // cppcheck-suppress unreadVariable
3464
0
        fDstMax = static_cast<Twork>(std::numeric_limits<GInt32>::max());
3465
0
    }
3466
0
    else if (dstDataType == GDT_UInt64)
3467
0
    {
3468
        // cppcheck-suppress unreadVariable
3469
0
        fDstMin = static_cast<Twork>(std::numeric_limits<uint64_t>::min());
3470
        // cppcheck-suppress unreadVariable
3471
        // (1 << 64) - 2048: largest uint64 value a double can hold
3472
0
        fDstMax = static_cast<Twork>(18446744073709549568ULL);
3473
0
    }
3474
0
    else if (dstDataType == GDT_Int64)
3475
0
    {
3476
        // cppcheck-suppress unreadVariable
3477
0
        fDstMin = static_cast<Twork>(std::numeric_limits<int64_t>::min());
3478
        // cppcheck-suppress unreadVariable
3479
        // (1 << 63) - 1024: largest int64 that a double can hold
3480
0
        fDstMax = static_cast<Twork>(9223372036854774784LL);
3481
0
    }
3482
3483
0
    bool bHasNaN = false;
3484
0
    if (pabyChunkNodataMask)
3485
0
    {
3486
        if constexpr (std::is_floating_point_v<T>)
3487
0
        {
3488
0
            for (size_t i = 0;
3489
0
                 i < static_cast<size_t>(nChunkXSize) * nChunkYSize; ++i)
3490
0
            {
3491
0
                if (std::isnan(pChunk[i]))
3492
0
                {
3493
0
                    bHasNaN = true;
3494
0
                    break;
3495
0
                }
3496
0
            }
3497
0
        }
3498
0
    }
3499
3500
0
    auto replaceValIfNodata = [bHasNoData, isIntegerDT, fDstMin, fDstMax,
3501
0
                               bNoDataValueInt64Valid, nNodataValueInt64,
3502
0
                               dfNoDataValue, dfReplacementVal](Twork fVal)
3503
0
    {
3504
0
        if (!bHasNoData)
3505
0
            return fVal;
3506
3507
        // Clamp value before comparing to nodata: this is only needed for
3508
        // kernels with negative weights (Lanczos)
3509
0
        Twork fClamped = fVal;
3510
0
        if (fClamped < fDstMin)
3511
0
            fClamped = fDstMin;
3512
0
        else if (fClamped > fDstMax)
3513
0
            fClamped = fDstMax;
3514
0
        if (isIntegerDT)
3515
0
        {
3516
0
            if (bNoDataValueInt64Valid)
3517
0
            {
3518
0
                const double fClampedRounded = double(std::round(fClamped));
3519
0
                if (fClampedRounded >=
3520
0
                        static_cast<double>(static_cast<Twork>(
3521
0
                            std::numeric_limits<int64_t>::min())) &&
3522
0
                    fClampedRounded <= static_cast<double>(static_cast<Twork>(
3523
0
                                           9223372036854774784LL)) &&
3524
0
                    nNodataValueInt64 ==
3525
0
                        static_cast<GInt64>(std::round(fClamped)))
3526
0
                {
3527
                    // Do not use the nodata value
3528
0
                    return static_cast<Twork>(dfReplacementVal);
3529
0
                }
3530
0
            }
3531
0
        }
3532
0
        else if (dfNoDataValue == static_cast<double>(fClamped))
3533
0
        {
3534
            // Do not use the nodata value
3535
0
            return static_cast<Twork>(dfReplacementVal);
3536
0
        }
3537
0
        return fClamped;
3538
0
    };
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, true, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double)#1}::operator()(double) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double)#1}::operator()(double) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(float)#1}::operator()(float) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, false>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double)#1}::operator()(double) const
3539
3540
    /* -------------------------------------------------------------------- */
3541
    /*      Allocate work buffers.                                          */
3542
    /* -------------------------------------------------------------------- */
3543
0
    const int nDstXSize = nDstXOff2 - nDstXOff;
3544
0
    Twork *pafWrkScanline = nullptr;
3545
0
    if (dstDataType != eWrkDataType)
3546
0
    {
3547
0
        pafWrkScanline =
3548
0
            static_cast<Twork *>(VSI_MALLOC2_VERBOSE(nDstXSize, sizeof(Twork)));
3549
0
        if (pafWrkScanline == nullptr)
3550
0
            return CE_Failure;
3551
0
    }
3552
3553
0
    const double dfXScale = 1.0 / dfXRatioDstToSrc;
3554
0
    const double dfXScaleWeight = (dfXScale >= 1.0) ? 1.0 : dfXScale;
3555
0
    const double dfXScaledRadius = nKernelRadius / dfXScaleWeight;
3556
0
    const double dfYScale = 1.0 / dfYRatioDstToSrc;
3557
0
    const double dfYScaleWeight = (dfYScale >= 1.0) ? 1.0 : dfYScale;
3558
0
    const double dfYScaledRadius = nKernelRadius / dfYScaleWeight;
3559
3560
0
    const uint64_t nWeightCount = static_cast<uint64_t>(
3561
0
        2 + 2 * std::max(dfXScaledRadius, dfYScaledRadius) + 0.5);
3562
0
    if (nWeightCount > std::numeric_limits<uint32_t>::max() / sizeof(double))
3563
0
    {
3564
0
        VSIFree(pafWrkScanline);
3565
0
        CPLError(CE_Failure, CPLE_NotSupported,
3566
0
                 "Too large downsampling factor");
3567
0
        return CE_Failure;
3568
0
    }
3569
3570
    // Temporary array to store result of horizontal filter.
3571
0
    double *const padfHorizontalFiltered = static_cast<double *>(
3572
0
        VSI_MALLOC3_VERBOSE(nChunkYSize, nDstXSize, sizeof(double) * nBands));
3573
    // To store convolution coefficients.
3574
0
    double *const padfWeights =
3575
0
        static_cast<double *>(VSI_MALLOC_ALIGNED_AUTO_VERBOSE(
3576
0
            static_cast<size_t>(nWeightCount) * sizeof(double)));
3577
3578
0
    GByte *pabyChunkNodataMaskHorizontalFiltered = nullptr;
3579
0
    if (pabyChunkNodataMask)
3580
0
        pabyChunkNodataMaskHorizontalFiltered =
3581
0
            static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nChunkYSize, nDstXSize));
3582
0
    if (padfHorizontalFiltered == nullptr || padfWeights == nullptr ||
3583
0
        (pabyChunkNodataMask != nullptr &&
3584
0
         pabyChunkNodataMaskHorizontalFiltered == nullptr))
3585
0
    {
3586
0
        VSIFree(pafWrkScanline);
3587
0
        VSIFree(padfHorizontalFiltered);
3588
0
        VSIFreeAligned(padfWeights);
3589
0
        VSIFree(pabyChunkNodataMaskHorizontalFiltered);
3590
0
        return CE_Failure;
3591
0
    }
3592
3593
    /* ==================================================================== */
3594
    /*      First pass: horizontal filter                                   */
3595
    /* ==================================================================== */
3596
0
    const int nChunkRightXOff = nChunkXOff + nChunkXSize;
3597
0
#ifdef USE_SSE2
3598
0
    const bool bSrcPixelCountLess8 = dfXScaledRadius < 4;
3599
0
#endif
3600
0
    for (int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; ++iDstPixel)
3601
0
    {
3602
0
        const double dfSrcPixel =
3603
0
            (iDstPixel + 0.5) * dfXRatioDstToSrc + dfSrcXDelta;
3604
0
        const int nSrcPixelStart = std::max(
3605
0
            static_cast<int>(floor(dfSrcPixel - dfXScaledRadius + 0.5)),
3606
0
            nChunkXOff);
3607
0
        const int nSrcPixelStop =
3608
0
            std::min(static_cast<int>(dfSrcPixel + dfXScaledRadius + 0.5),
3609
0
                     nChunkRightXOff);
3610
#if 0
3611
        if( nSrcPixelStart < nChunkXOff && nChunkXOff > 0 )
3612
        {
3613
            printf( "truncated iDstPixel = %d\n", iDstPixel );/*ok*/
3614
        }
3615
        if( nSrcPixelStop > nChunkRightXOff && nChunkRightXOff < nSrcWidth )
3616
        {
3617
            printf( "truncated iDstPixel = %d\n", iDstPixel );/*ok*/
3618
        }
3619
#endif
3620
0
        const int nSrcPixelCount = nSrcPixelStop - nSrcPixelStart;
3621
0
        double dfWeightSum = 0.0;
3622
3623
        // Compute convolution coefficients.
3624
0
        int nSrcPixel = nSrcPixelStart;
3625
0
        double dfX = dfXScaleWeight * (nSrcPixel - dfSrcPixel + 0.5);
3626
0
        for (; nSrcPixel < nSrcPixelStop - 3; nSrcPixel += 4)
3627
0
        {
3628
0
            padfWeights[nSrcPixel - nSrcPixelStart] = dfX;
3629
0
            dfX += dfXScaleWeight;
3630
0
            padfWeights[nSrcPixel + 1 - nSrcPixelStart] = dfX;
3631
0
            dfX += dfXScaleWeight;
3632
0
            padfWeights[nSrcPixel + 2 - nSrcPixelStart] = dfX;
3633
0
            dfX += dfXScaleWeight;
3634
0
            padfWeights[nSrcPixel + 3 - nSrcPixelStart] = dfX;
3635
0
            dfX += dfXScaleWeight;
3636
0
            dfWeightSum +=
3637
0
                pfnFilterFunc4Values(padfWeights + nSrcPixel - nSrcPixelStart);
3638
0
        }
3639
0
        for (; nSrcPixel < nSrcPixelStop; ++nSrcPixel, dfX += dfXScaleWeight)
3640
0
        {
3641
0
            const double dfWeight = pfnFilterFunc(dfX);
3642
0
            padfWeights[nSrcPixel - nSrcPixelStart] = dfWeight;
3643
0
            dfWeightSum += dfWeight;
3644
0
        }
3645
3646
0
        const int nHeight = nChunkYSize * nBands;
3647
0
        if (pabyChunkNodataMask == nullptr)
3648
0
        {
3649
            // For floating-point data types, we must scale down a bit values
3650
            // if input values are close to +/- std::numeric_limits<T>::max()
3651
#ifdef OLD_CPPCHECK
3652
            constexpr double mulFactor = 1;
3653
#else
3654
0
            constexpr double mulFactor =
3655
0
                (bNeedRescale &&
3656
0
                 (std::is_same_v<T, float> || std::is_same_v<T, double>))
3657
0
                    ? 2
3658
0
                    : 1;
3659
0
#endif
3660
3661
0
            if (dfWeightSum != 0)
3662
0
            {
3663
0
                const double dfInvWeightSum = 1.0 / (mulFactor * dfWeightSum);
3664
0
                for (int i = 0; i < nSrcPixelCount; ++i)
3665
0
                {
3666
0
                    padfWeights[i] *= dfInvWeightSum;
3667
0
                }
3668
0
            }
3669
3670
0
            const auto ScaleValue = [
3671
#ifdef _MSC_VER
3672
                                        mulFactor
3673
#endif
3674
0
            ](double dfVal, [[maybe_unused]] const T *inputValues,
3675
0
                                    [[maybe_unused]] int nInputValues)
3676
0
            {
3677
0
                constexpr bool isFloat =
3678
0
                    std::is_same_v<T, float> || std::is_same_v<T, double>;
3679
                if constexpr (isFloat)
3680
0
                {
3681
0
                    if (std::isfinite(dfVal))
3682
0
                    {
3683
0
                        return std::clamp(dfVal,
3684
0
                                          -std::numeric_limits<double>::max() /
3685
0
                                              mulFactor,
3686
0
                                          std::numeric_limits<double>::max() /
3687
0
                                              mulFactor) *
3688
0
                               mulFactor;
3689
0
                    }
3690
                    else if constexpr (bKernelWithNegativeWeights)
3691
0
                    {
3692
0
                        if (std::isnan(dfVal))
3693
0
                        {
3694
                            // Either one of the input value is NaN or they are +/-Inf
3695
0
                            const bool isPositive = inputValues[0] >= 0;
3696
0
                            for (int i = 0; i < nInputValues; ++i)
3697
0
                            {
3698
0
                                if (std::isnan(inputValues[i]))
3699
0
                                    return dfVal;
3700
                                // cppcheck-suppress knownConditionTrueFalse
3701
0
                                if ((inputValues[i] >= 0) != isPositive)
3702
0
                                    return dfVal;
3703
0
                            }
3704
                            // All values are positive or negative infinity
3705
0
                            return static_cast<double>(inputValues[0]);
3706
0
                        }
3707
0
                    }
3708
0
                }
3709
0
                return dfVal;
3710
0
            };
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, float const*, int)#1}::operator()(double, float const*, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, true, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int)#1}::operator()(double, double const*, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, float const*, int)#1}::operator()(double, float const*, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int)#1}::operator()(double, double const*, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, float const*, int)#1}::operator()(double, float const*, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, false>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int)#1}::operator()(double, double const*, int) const
3711
3712
0
            int iSrcLineOff = 0;
3713
0
#ifdef USE_SSE2
3714
0
            if (nSrcPixelCount == 4)
3715
0
            {
3716
0
                for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3717
0
                {
3718
0
                    const size_t j =
3719
0
                        static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3720
0
                        (nSrcPixelStart - nChunkXOff);
3721
0
                    double dfVal1 = 0.0;
3722
0
                    double dfVal2 = 0.0;
3723
0
                    double dfVal3 = 0.0;
3724
                    if constexpr (std::is_floating_point_v<T>)
3725
0
                    {
3726
0
                        if (bHasNaN)
3727
0
                        {
3728
0
                            GDALResampleConvolutionHorizontalPixelCount4_3rows<
3729
0
                                T, true>(pChunk + j, pChunk + j + nChunkXSize,
3730
0
                                         pChunk + j + 2 * nChunkXSize,
3731
0
                                         padfWeights, dfVal1, dfVal2, dfVal3);
3732
0
                        }
3733
0
                        else
3734
0
                        {
3735
0
                            GDALResampleConvolutionHorizontalPixelCount4_3rows<
3736
0
                                T, false>(pChunk + j, pChunk + j + nChunkXSize,
3737
0
                                          pChunk + j + 2 * nChunkXSize,
3738
0
                                          padfWeights, dfVal1, dfVal2, dfVal3);
3739
0
                        }
3740
                    }
3741
                    else
3742
0
                    {
3743
0
                        GDALResampleConvolutionHorizontalPixelCount4_3rows<
3744
0
                            T, false>(pChunk + j, pChunk + j + nChunkXSize,
3745
0
                                      pChunk + j + 2 * nChunkXSize, padfWeights,
3746
0
                                      dfVal1, dfVal2, dfVal3);
3747
0
                    }
3748
0
                    padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3749
0
                                               nDstXSize +
3750
0
                                           iDstPixel - nDstXOff] =
3751
0
                        ScaleValue(dfVal1, pChunk + j, 4);
3752
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3753
0
                                            1) *
3754
0
                                               nDstXSize +
3755
0
                                           iDstPixel - nDstXOff] =
3756
0
                        ScaleValue(dfVal2, pChunk + j + nChunkXSize, 4);
3757
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3758
0
                                            2) *
3759
0
                                               nDstXSize +
3760
0
                                           iDstPixel - nDstXOff] =
3761
0
                        ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize, 4);
3762
0
                }
3763
0
            }
3764
0
            else if (bSrcPixelCountLess8)
3765
0
            {
3766
0
                for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3767
0
                {
3768
0
                    const size_t j =
3769
0
                        static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3770
0
                        (nSrcPixelStart - nChunkXOff);
3771
0
                    double dfVal1 = 0.0;
3772
0
                    double dfVal2 = 0.0;
3773
0
                    double dfVal3 = 0.0;
3774
                    if constexpr (std::is_floating_point_v<T>)
3775
0
                    {
3776
0
                        if (bHasNaN)
3777
0
                        {
3778
0
                            GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3779
0
                                T, true>(pChunk + j, pChunk + j + nChunkXSize,
3780
0
                                         pChunk + j + 2 * nChunkXSize,
3781
0
                                         padfWeights, nSrcPixelCount, dfVal1,
3782
0
                                         dfVal2, dfVal3);
3783
0
                        }
3784
0
                        else
3785
0
                        {
3786
0
                            GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3787
0
                                T, false>(pChunk + j, pChunk + j + nChunkXSize,
3788
0
                                          pChunk + j + 2 * nChunkXSize,
3789
0
                                          padfWeights, nSrcPixelCount, dfVal1,
3790
0
                                          dfVal2, dfVal3);
3791
0
                        }
3792
                    }
3793
                    else
3794
0
                    {
3795
0
                        GDALResampleConvolutionHorizontalPixelCountLess8_3rows<
3796
0
                            T, false>(pChunk + j, pChunk + j + nChunkXSize,
3797
0
                                      pChunk + j + 2 * nChunkXSize, padfWeights,
3798
0
                                      nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3799
0
                    }
3800
0
                    padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3801
0
                                               nDstXSize +
3802
0
                                           iDstPixel - nDstXOff] =
3803
0
                        ScaleValue(dfVal1, pChunk + j, nSrcPixelCount);
3804
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3805
0
                                            1) *
3806
0
                                               nDstXSize +
3807
0
                                           iDstPixel - nDstXOff] =
3808
0
                        ScaleValue(dfVal2, pChunk + j + nChunkXSize,
3809
0
                                   nSrcPixelCount);
3810
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3811
0
                                            2) *
3812
0
                                               nDstXSize +
3813
0
                                           iDstPixel - nDstXOff] =
3814
0
                        ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize,
3815
0
                                   nSrcPixelCount);
3816
0
                }
3817
0
            }
3818
0
            else
3819
0
#endif
3820
0
            {
3821
0
                for (; iSrcLineOff < nHeight - 2; iSrcLineOff += 3)
3822
0
                {
3823
0
                    const size_t j =
3824
0
                        static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3825
0
                        (nSrcPixelStart - nChunkXOff);
3826
0
                    double dfVal1 = 0.0;
3827
0
                    double dfVal2 = 0.0;
3828
0
                    double dfVal3 = 0.0;
3829
                    if constexpr (std::is_floating_point_v<T>)
3830
0
                    {
3831
0
                        if (bHasNaN)
3832
0
                        {
3833
0
                            GDALResampleConvolutionHorizontal_3rows<T, true>(
3834
0
                                pChunk + j, pChunk + j + nChunkXSize,
3835
0
                                pChunk + j + 2 * nChunkXSize, padfWeights,
3836
0
                                nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3837
0
                        }
3838
0
                        else
3839
0
                        {
3840
0
                            GDALResampleConvolutionHorizontal_3rows<T, false>(
3841
0
                                pChunk + j, pChunk + j + nChunkXSize,
3842
0
                                pChunk + j + 2 * nChunkXSize, padfWeights,
3843
0
                                nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3844
0
                        }
3845
                    }
3846
                    else
3847
0
                    {
3848
0
                        GDALResampleConvolutionHorizontal_3rows<T, false>(
3849
0
                            pChunk + j, pChunk + j + nChunkXSize,
3850
0
                            pChunk + j + 2 * nChunkXSize, padfWeights,
3851
0
                            nSrcPixelCount, dfVal1, dfVal2, dfVal3);
3852
0
                    }
3853
0
                    padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3854
0
                                               nDstXSize +
3855
0
                                           iDstPixel - nDstXOff] =
3856
0
                        ScaleValue(dfVal1, pChunk + j, nSrcPixelCount);
3857
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3858
0
                                            1) *
3859
0
                                               nDstXSize +
3860
0
                                           iDstPixel - nDstXOff] =
3861
0
                        ScaleValue(dfVal2, pChunk + j + nChunkXSize,
3862
0
                                   nSrcPixelCount);
3863
0
                    padfHorizontalFiltered[(static_cast<size_t>(iSrcLineOff) +
3864
0
                                            2) *
3865
0
                                               nDstXSize +
3866
0
                                           iDstPixel - nDstXOff] =
3867
0
                        ScaleValue(dfVal3, pChunk + j + 2 * nChunkXSize,
3868
0
                                   nSrcPixelCount);
3869
0
                }
3870
0
            }
3871
0
            for (; iSrcLineOff < nHeight; ++iSrcLineOff)
3872
0
            {
3873
0
                const size_t j =
3874
0
                    static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3875
0
                    (nSrcPixelStart - nChunkXOff);
3876
0
                const double dfVal = GDALResampleConvolutionHorizontal(
3877
0
                    pChunk + j, padfWeights, nSrcPixelCount);
3878
0
                padfHorizontalFiltered[static_cast<size_t>(iSrcLineOff) *
3879
0
                                           nDstXSize +
3880
0
                                       iDstPixel - nDstXOff] =
3881
0
                    ScaleValue(dfVal, pChunk + j, nSrcPixelCount);
3882
0
            }
3883
0
        }
3884
0
        else
3885
0
        {
3886
0
            for (int iSrcLineOff = 0; iSrcLineOff < nHeight; ++iSrcLineOff)
3887
0
            {
3888
0
                const size_t j =
3889
0
                    static_cast<size_t>(iSrcLineOff) * nChunkXSize +
3890
0
                    (nSrcPixelStart - nChunkXOff);
3891
3892
0
                if (bKernelWithNegativeWeights)
3893
0
                {
3894
0
                    int nConsecutiveValid = 0;
3895
0
                    int nMaxConsecutiveValid = 0;
3896
0
                    for (int k = 0; k < nSrcPixelCount; k++)
3897
0
                    {
3898
0
                        if (pabyChunkNodataMask[j + k])
3899
0
                            nConsecutiveValid++;
3900
0
                        else if (nConsecutiveValid)
3901
0
                        {
3902
0
                            nMaxConsecutiveValid = std::max(
3903
0
                                nMaxConsecutiveValid, nConsecutiveValid);
3904
0
                            nConsecutiveValid = 0;
3905
0
                        }
3906
0
                    }
3907
0
                    nMaxConsecutiveValid =
3908
0
                        std::max(nMaxConsecutiveValid, nConsecutiveValid);
3909
0
                    if (nMaxConsecutiveValid < nSrcPixelCount / 2)
3910
0
                    {
3911
0
                        const size_t nTempOffset =
3912
0
                            static_cast<size_t>(iSrcLineOff) * nDstXSize +
3913
0
                            iDstPixel - nDstXOff;
3914
0
                        padfHorizontalFiltered[nTempOffset] = 0.0;
3915
0
                        pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0;
3916
0
                        continue;
3917
0
                    }
3918
0
                }
3919
3920
0
                double dfSumWeightedVal = 0.0;
3921
0
                double dfSumWeightedAlpha = 0.0;
3922
                if constexpr (std::is_floating_point_v<T>)
3923
0
                {
3924
0
                    if (bHasNaN)
3925
0
                    {
3926
0
                        GDALResampleConvolutionHorizontalWithMask<T, true>(
3927
0
                            pChunk + j, pabyChunkNodataMask + j, padfWeights,
3928
0
                            nSrcPixelCount, dfSumWeightedVal,
3929
0
                            dfSumWeightedAlpha, dfWeightSum);
3930
0
                    }
3931
0
                    else
3932
0
                    {
3933
0
                        GDALResampleConvolutionHorizontalWithMask<T, false>(
3934
0
                            pChunk + j, pabyChunkNodataMask + j, padfWeights,
3935
0
                            nSrcPixelCount, dfSumWeightedVal,
3936
0
                            dfSumWeightedAlpha, dfWeightSum);
3937
0
                    }
3938
                }
3939
                else
3940
0
                {
3941
0
                    GDALResampleConvolutionHorizontalWithMask<T, false>(
3942
0
                        pChunk + j, pabyChunkNodataMask + j, padfWeights,
3943
0
                        nSrcPixelCount, dfSumWeightedVal, dfSumWeightedAlpha,
3944
0
                        dfWeightSum);
3945
0
                }
3946
0
                const size_t nTempOffset =
3947
0
                    static_cast<size_t>(iSrcLineOff) * nDstXSize + iDstPixel -
3948
0
                    nDstXOff;
3949
0
                if (dfSumWeightedAlpha > 0.0)
3950
0
                {
3951
0
                    padfHorizontalFiltered[nTempOffset] =
3952
0
                        dfSumWeightedVal / dfSumWeightedAlpha;
3953
                    // Not entirely clear if clamping values in the horizontal filter
3954
                    // is the right thing to do, but otherwise, for
3955
                    // https://github.com/OSGeo/gdal/issues/14728
3956
                    // with very small values of alpha, we get very strong under
3957
                    // and over shoots.
3958
                    if constexpr (std::is_same_v<T, uint8_t>)
3959
0
                    {
3960
0
                        padfHorizontalFiltered[nTempOffset] = std::clamp(
3961
0
                            padfHorizontalFiltered[nTempOffset], 0.0, 255.0);
3962
                    }
3963
                    else if constexpr (std::is_same_v<T, uint16_t>)
3964
0
                    {
3965
0
                        padfHorizontalFiltered[nTempOffset] = std::clamp(
3966
0
                            padfHorizontalFiltered[nTempOffset], 0.0, 65535.0);
3967
0
                    }
3968
0
                    const double dfAlpha = dfSumWeightedAlpha / dfWeightSum;
3969
0
                    pabyChunkNodataMaskHorizontalFiltered[nTempOffset] =
3970
0
                        static_cast<uint8_t>(std::min(dfAlpha + 0.5, 255.0));
3971
0
                }
3972
0
                else
3973
0
                {
3974
0
                    padfHorizontalFiltered[nTempOffset] = 0.0;
3975
0
                    pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0;
3976
0
                }
3977
0
            }
3978
0
        }
3979
0
    }
3980
3981
    /* ==================================================================== */
3982
    /*      Second pass: vertical filter                                    */
3983
    /* ==================================================================== */
3984
0
    const int nChunkBottomYOff = nChunkYOff + nChunkYSize;
3985
3986
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
3987
0
    {
3988
0
        Twork *const pafDstScanline =
3989
0
            pafWrkScanline
3990
0
                ? pafWrkScanline
3991
0
                : static_cast<Twork *>(pDstBuffer) +
3992
0
                      static_cast<size_t>(iDstLine - nDstYOff) * nDstXSize;
3993
3994
0
        const double dfSrcLine =
3995
0
            (iDstLine + 0.5) * dfYRatioDstToSrc + dfSrcYDelta;
3996
0
        const int nSrcLineStart =
3997
0
            std::max(static_cast<int>(floor(dfSrcLine - dfYScaledRadius + 0.5)),
3998
0
                     nChunkYOff);
3999
0
        const int nSrcLineStop =
4000
0
            std::min(static_cast<int>(dfSrcLine + dfYScaledRadius + 0.5),
4001
0
                     nChunkBottomYOff);
4002
#if 0
4003
        if( nSrcLineStart < nChunkYOff &&
4004
            nChunkYOff > 0 )
4005
        {
4006
            printf( "truncated iDstLine = %d\n", iDstLine );/*ok*/
4007
        }
4008
        if( nSrcLineStop > nChunkBottomYOff && nChunkBottomYOff < nSrcHeight )
4009
        {
4010
            printf( "truncated iDstLine = %d\n", iDstLine );/*ok*/
4011
        }
4012
#endif
4013
0
        const int nSrcLineCount = nSrcLineStop - nSrcLineStart;
4014
0
        double dfWeightSum = 0.0;
4015
4016
        // Compute convolution coefficients.
4017
0
        int nSrcLine = nSrcLineStart;  // Used after for.
4018
0
        double dfY = dfYScaleWeight * (nSrcLine - dfSrcLine + 0.5);
4019
0
        for (; nSrcLine < nSrcLineStop - 3;
4020
0
             nSrcLine += 4, dfY += 4 * dfYScaleWeight)
4021
0
        {
4022
0
            padfWeights[nSrcLine - nSrcLineStart] = dfY;
4023
0
            padfWeights[nSrcLine + 1 - nSrcLineStart] = dfY + dfYScaleWeight;
4024
0
            padfWeights[nSrcLine + 2 - nSrcLineStart] =
4025
0
                dfY + 2 * dfYScaleWeight;
4026
0
            padfWeights[nSrcLine + 3 - nSrcLineStart] =
4027
0
                dfY + 3 * dfYScaleWeight;
4028
0
            dfWeightSum +=
4029
0
                pfnFilterFunc4Values(padfWeights + nSrcLine - nSrcLineStart);
4030
0
        }
4031
0
        for (; nSrcLine < nSrcLineStop; ++nSrcLine, dfY += dfYScaleWeight)
4032
0
        {
4033
0
            const double dfWeight = pfnFilterFunc(dfY);
4034
0
            padfWeights[nSrcLine - nSrcLineStart] = dfWeight;
4035
0
            dfWeightSum += dfWeight;
4036
0
        }
4037
4038
0
        if (pabyChunkNodataMask == nullptr)
4039
0
        {
4040
            // For floating-point data types, we must scale down a bit values
4041
            // if input values are close to +/- std::numeric_limits<T>::max()
4042
#ifdef OLD_CPPCHECK
4043
            constexpr double mulFactor = 1;
4044
#else
4045
0
            constexpr double mulFactor =
4046
0
                (bNeedRescale &&
4047
0
                 (std::is_same_v<T, float> || std::is_same_v<T, double>))
4048
0
                    ? 2
4049
0
                    : 1;
4050
0
#endif
4051
4052
0
            if (dfWeightSum != 0)
4053
0
            {
4054
0
                const double dfInvWeightSum = 1.0 / (mulFactor * dfWeightSum);
4055
0
                for (int i = 0; i < nSrcLineCount; ++i)
4056
0
                    padfWeights[i] *= dfInvWeightSum;
4057
0
            }
4058
4059
0
            int iFilteredPixelOff = 0;  // Used after for.
4060
            // j used after for.
4061
0
            size_t j =
4062
0
                (nSrcLineStart - nChunkYOff) * static_cast<size_t>(nDstXSize);
4063
0
#ifdef USE_SSE2
4064
            if constexpr ((!bNeedRescale || !std::is_same_v<T, float>) &&
4065
                          eWrkDataType == GDT_Float32)
4066
0
            {
4067
#ifdef __AVX__
4068
                for (; iFilteredPixelOff < nDstXSize - 15;
4069
                     iFilteredPixelOff += 16, j += 16)
4070
                {
4071
                    GDALResampleConvolutionVertical_16cols(
4072
                        padfHorizontalFiltered + j, nDstXSize, padfWeights,
4073
                        nSrcLineCount, pafDstScanline + iFilteredPixelOff);
4074
                    if (bHasNoData)
4075
                    {
4076
                        for (int k = 0; k < 16; k++)
4077
                        {
4078
                            pafDstScanline[iFilteredPixelOff + k] =
4079
                                replaceValIfNodata(
4080
                                    pafDstScanline[iFilteredPixelOff + k]);
4081
                        }
4082
                    }
4083
                }
4084
#else
4085
0
                for (; iFilteredPixelOff < nDstXSize - 7;
4086
0
                     iFilteredPixelOff += 8, j += 8)
4087
0
                {
4088
0
                    GDALResampleConvolutionVertical_8cols(
4089
0
                        padfHorizontalFiltered + j, nDstXSize, padfWeights,
4090
0
                        nSrcLineCount, pafDstScanline + iFilteredPixelOff);
4091
0
                    if (bHasNoData)
4092
0
                    {
4093
0
                        for (int k = 0; k < 8; k++)
4094
0
                        {
4095
0
                            pafDstScanline[iFilteredPixelOff + k] =
4096
0
                                replaceValIfNodata(
4097
0
                                    pafDstScanline[iFilteredPixelOff + k]);
4098
0
                        }
4099
0
                    }
4100
0
                }
4101
0
#endif
4102
4103
0
                for (; iFilteredPixelOff < nDstXSize; iFilteredPixelOff++, j++)
4104
0
                {
4105
0
                    const Twork fVal =
4106
0
                        static_cast<Twork>(GDALResampleConvolutionVertical(
4107
0
                            padfHorizontalFiltered + j, nDstXSize, padfWeights,
4108
0
                            nSrcLineCount));
4109
0
                    pafDstScanline[iFilteredPixelOff] =
4110
0
                        replaceValIfNodata(fVal);
4111
0
                }
4112
            }
4113
            else
4114
#endif
4115
0
            {
4116
0
                const auto ScaleValue = [
4117
#ifdef _MSC_VER
4118
                                            mulFactor
4119
#endif
4120
0
                ](double dfVal, [[maybe_unused]] const double *inputValues,
4121
0
                                        [[maybe_unused]] int nStride,
4122
0
                                        [[maybe_unused]] int nInputValues)
4123
0
                {
4124
0
                    constexpr bool isFloat =
4125
0
                        std::is_same_v<T, float> || std::is_same_v<T, double>;
4126
                    if constexpr (isFloat)
4127
0
                    {
4128
0
                        if (std::isfinite(dfVal))
4129
0
                        {
4130
0
                            return std::clamp(
4131
0
                                       dfVal,
4132
0
                                       static_cast<double>(
4133
0
                                           -std::numeric_limits<Twork>::max()) /
4134
0
                                           mulFactor,
4135
0
                                       static_cast<double>(
4136
0
                                           std::numeric_limits<Twork>::max()) /
4137
0
                                           mulFactor) *
4138
0
                                   mulFactor;
4139
0
                        }
4140
                        else if constexpr (bKernelWithNegativeWeights)
4141
0
                        {
4142
0
                            if (std::isnan(dfVal))
4143
0
                            {
4144
                                // Either one of the input value is NaN or they are +/-Inf
4145
0
                                const bool isPositive = inputValues[0] >= 0;
4146
0
                                for (int i = 0; i < nInputValues; ++i)
4147
0
                                {
4148
0
                                    if (std::isnan(inputValues[i * nStride]))
4149
0
                                        return dfVal;
4150
                                    // cppcheck-suppress knownConditionTrueFalse
4151
0
                                    if ((inputValues[i] >= 0) != isPositive)
4152
0
                                        return dfVal;
4153
0
                                }
4154
                                // All values are positive or negative infinity
4155
0
                                return inputValues[0];
4156
0
                            }
4157
0
                        }
4158
0
                    }
4159
4160
0
                    return dfVal;
4161
0
                };
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int, int)#1}::operator()(double, double const*, int, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, true, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int, int)#1}::operator()(double, double const*, int, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int, int)#1}::operator()(double, double const*, int, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int, int)#1}::operator()(double, double const*, int, int) const
Unexecuted instantiation: overview.cpp:GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, false>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)::{lambda(double, double const*, int, int)#1}::operator()(double, double const*, int, int) const
4162
4163
0
                for (; iFilteredPixelOff < nDstXSize - 1;
4164
0
                     iFilteredPixelOff += 2, j += 2)
4165
0
                {
4166
0
                    double dfVal1 = 0.0;
4167
0
                    double dfVal2 = 0.0;
4168
0
                    GDALResampleConvolutionVertical_2cols(
4169
0
                        padfHorizontalFiltered + j, nDstXSize, padfWeights,
4170
0
                        nSrcLineCount, dfVal1, dfVal2);
4171
0
                    pafDstScanline[iFilteredPixelOff] =
4172
0
                        replaceValIfNodata(static_cast<Twork>(
4173
0
                            ScaleValue(dfVal1, padfHorizontalFiltered + j,
4174
0
                                       nDstXSize, nSrcLineCount)));
4175
0
                    pafDstScanline[iFilteredPixelOff + 1] =
4176
0
                        replaceValIfNodata(static_cast<Twork>(
4177
0
                            ScaleValue(dfVal2, padfHorizontalFiltered + j + 1,
4178
0
                                       nDstXSize, nSrcLineCount)));
4179
0
                }
4180
0
                if (iFilteredPixelOff < nDstXSize)
4181
0
                {
4182
0
                    const double dfVal = GDALResampleConvolutionVertical(
4183
0
                        padfHorizontalFiltered + j, nDstXSize, padfWeights,
4184
0
                        nSrcLineCount);
4185
0
                    pafDstScanline[iFilteredPixelOff] =
4186
0
                        replaceValIfNodata(static_cast<Twork>(
4187
0
                            ScaleValue(dfVal, padfHorizontalFiltered + j,
4188
0
                                       nDstXSize, nSrcLineCount)));
4189
0
                }
4190
0
            }
4191
0
        }
4192
0
        else
4193
0
        {
4194
0
            for (int iFilteredPixelOff = 0; iFilteredPixelOff < nDstXSize;
4195
0
                 ++iFilteredPixelOff)
4196
0
            {
4197
0
                double dfVal = 0.0;
4198
0
                dfWeightSum = 0.0;
4199
0
                size_t j = (nSrcLineStart - nChunkYOff) *
4200
0
                               static_cast<size_t>(nDstXSize) +
4201
0
                           iFilteredPixelOff;
4202
0
                if (bKernelWithNegativeWeights)
4203
0
                {
4204
0
                    int nConsecutiveValid = 0;
4205
0
                    int nMaxConsecutiveValid = 0;
4206
0
                    for (int i = 0; i < nSrcLineCount; ++i, j += nDstXSize)
4207
0
                    {
4208
0
                        const double dfWeight =
4209
0
                            padfWeights[i] *
4210
0
                            pabyChunkNodataMaskHorizontalFiltered[j];
4211
0
                        if (pabyChunkNodataMaskHorizontalFiltered[j])
4212
0
                        {
4213
0
                            nConsecutiveValid++;
4214
0
                        }
4215
0
                        else if (nConsecutiveValid)
4216
0
                        {
4217
0
                            nMaxConsecutiveValid = std::max(
4218
0
                                nMaxConsecutiveValid, nConsecutiveValid);
4219
0
                            nConsecutiveValid = 0;
4220
0
                        }
4221
0
                        dfVal += padfHorizontalFiltered[j] * dfWeight;
4222
0
                        dfWeightSum += dfWeight;
4223
0
                    }
4224
0
                    nMaxConsecutiveValid =
4225
0
                        std::max(nMaxConsecutiveValid, nConsecutiveValid);
4226
0
                    if (nMaxConsecutiveValid < nSrcLineCount / 2)
4227
0
                    {
4228
0
                        pafDstScanline[iFilteredPixelOff] =
4229
0
                            static_cast<Twork>(dfNoDataValue);
4230
0
                        continue;
4231
0
                    }
4232
0
                }
4233
0
                else
4234
0
                {
4235
0
                    for (int i = 0; i < nSrcLineCount; ++i, j += nDstXSize)
4236
0
                    {
4237
0
                        const double dfWeight =
4238
0
                            padfWeights[i] *
4239
0
                            pabyChunkNodataMaskHorizontalFiltered[j];
4240
0
                        dfVal += padfHorizontalFiltered[j] * dfWeight;
4241
0
                        dfWeightSum += dfWeight;
4242
0
                    }
4243
0
                }
4244
0
                if (dfWeightSum > 0.0)
4245
0
                {
4246
0
                    pafDstScanline[iFilteredPixelOff] = replaceValIfNodata(
4247
0
                        static_cast<Twork>(dfVal / dfWeightSum));
4248
0
                }
4249
0
                else
4250
0
                {
4251
0
                    pafDstScanline[iFilteredPixelOff] =
4252
0
                        static_cast<Twork>(dfNoDataValue);
4253
0
                }
4254
0
            }
4255
0
        }
4256
4257
0
        if (fMaxVal != 0.0f)
4258
0
        {
4259
            if constexpr (std::is_same_v<T, double>)
4260
0
            {
4261
0
                for (int i = 0; i < nDstXSize; ++i)
4262
0
                {
4263
0
                    if (pafDstScanline[i] > static_cast<double>(fMaxVal))
4264
0
                        pafDstScanline[i] = static_cast<double>(fMaxVal);
4265
0
                }
4266
            }
4267
            else
4268
0
            {
4269
0
                for (int i = 0; i < nDstXSize; ++i)
4270
0
                {
4271
0
                    if (pafDstScanline[i] > fMaxVal)
4272
0
                        pafDstScanline[i] = fMaxVal;
4273
0
                }
4274
0
            }
4275
0
        }
4276
4277
0
        if (pafWrkScanline)
4278
0
        {
4279
0
            GDALCopyWords64(pafWrkScanline, eWrkDataType, nWrkDataTypeSize,
4280
0
                            static_cast<GByte *>(pDstBuffer) +
4281
0
                                static_cast<size_t>(iDstLine - nDstYOff) *
4282
0
                                    nDstXSize * nDstDataTypeSize,
4283
0
                            dstDataType, nDstDataTypeSize, nDstXSize);
4284
0
        }
4285
0
    }
4286
4287
0
    VSIFree(pafWrkScanline);
4288
0
    VSIFreeAligned(padfWeights);
4289
0
    VSIFree(padfHorizontalFiltered);
4290
0
    VSIFree(pabyChunkNodataMaskHorizontalFiltered);
4291
4292
0
    return CE_None;
4293
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, true, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, true, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, true>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, true>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned char, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, unsigned char const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<unsigned short, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, unsigned short const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<float, float, (GDALDataType)6, false, false>(GDALOverviewResampleArgs const&, float const*, void*, double (*)(double), double (*)(double*), int, float)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionT<double, double, (GDALDataType)7, false, false>(GDALOverviewResampleArgs const&, double const*, void*, double (*)(double), double (*)(double*), int, float)
4294
4295
template <bool bKernelWithNegativeWeights, bool bNeedRescale>
4296
static CPLErr
4297
GDALResampleChunk_ConvolutionInternal(const GDALOverviewResampleArgs &args,
4298
                                      const void *pChunk, void **ppDstBuffer,
4299
                                      GDALDataType *peDstBufferDataType)
4300
0
{
4301
0
    GDALResampleAlg eResample;
4302
0
    if (EQUAL(args.pszResampling, "BILINEAR"))
4303
0
        eResample = GRA_Bilinear;
4304
0
    else if (EQUAL(args.pszResampling, "CUBIC"))
4305
0
        eResample = GRA_Cubic;
4306
0
    else if (EQUAL(args.pszResampling, "CUBICSPLINE"))
4307
0
        eResample = GRA_CubicSpline;
4308
0
    else if (EQUAL(args.pszResampling, "LANCZOS"))
4309
0
        eResample = GRA_Lanczos;
4310
0
    else
4311
0
    {
4312
0
        CPLAssert(false);
4313
0
        return CE_Failure;
4314
0
    }
4315
0
    const int nKernelRadius = GWKGetFilterRadius(eResample);
4316
0
    FilterFuncType pfnFilterFunc = GWKGetFilterFunc(eResample);
4317
0
    const FilterFunc4ValuesType pfnFilterFunc4Values =
4318
0
        GWKGetFilterFunc4Values(eResample);
4319
4320
0
    float fMaxVal = 0.f;
4321
    // Cubic, etc... can have overshoots, so make sure we clamp values to the
4322
    // maximum value if NBITS is set.
4323
0
    if (eResample != GRA_Bilinear && args.nOvrNBITS > 0 &&
4324
0
        (args.eOvrDataType == GDT_UInt8 || args.eOvrDataType == GDT_UInt16 ||
4325
0
         args.eOvrDataType == GDT_UInt32))
4326
0
    {
4327
0
        int nBits = args.nOvrNBITS;
4328
0
        if (nBits == GDALGetDataTypeSizeBits(args.eOvrDataType))
4329
0
            nBits = 0;
4330
0
        if (nBits > 0 && nBits < 32)
4331
0
            fMaxVal = static_cast<float>((1U << nBits) - 1);
4332
0
    }
4333
4334
0
    *ppDstBuffer = VSI_MALLOC3_VERBOSE(
4335
0
        args.nDstXOff2 - args.nDstXOff, args.nDstYOff2 - args.nDstYOff,
4336
0
        GDALGetDataTypeSizeBytes(args.eOvrDataType));
4337
0
    if (*ppDstBuffer == nullptr)
4338
0
    {
4339
0
        return CE_Failure;
4340
0
    }
4341
0
    *peDstBufferDataType = args.eOvrDataType;
4342
4343
0
    switch (args.eWrkDataType)
4344
0
    {
4345
0
        case GDT_UInt8:
4346
0
        {
4347
0
            return GDALResampleChunk_ConvolutionT<GByte, float, GDT_Float32,
4348
0
                                                  bKernelWithNegativeWeights,
4349
0
                                                  bNeedRescale>(
4350
0
                args, static_cast<const GByte *>(pChunk), *ppDstBuffer,
4351
0
                pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4352
0
        }
4353
4354
0
        case GDT_UInt16:
4355
0
        {
4356
0
            return GDALResampleChunk_ConvolutionT<GUInt16, float, GDT_Float32,
4357
0
                                                  bKernelWithNegativeWeights,
4358
0
                                                  bNeedRescale>(
4359
0
                args, static_cast<const GUInt16 *>(pChunk), *ppDstBuffer,
4360
0
                pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4361
0
        }
4362
4363
0
        case GDT_Float32:
4364
0
        {
4365
0
            return GDALResampleChunk_ConvolutionT<float, float, GDT_Float32,
4366
0
                                                  bKernelWithNegativeWeights,
4367
0
                                                  bNeedRescale>(
4368
0
                args, static_cast<const float *>(pChunk), *ppDstBuffer,
4369
0
                pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4370
0
        }
4371
4372
0
        case GDT_Float64:
4373
0
        {
4374
0
            return GDALResampleChunk_ConvolutionT<double, double, GDT_Float64,
4375
0
                                                  bKernelWithNegativeWeights,
4376
0
                                                  bNeedRescale>(
4377
0
                args, static_cast<const double *>(pChunk), *ppDstBuffer,
4378
0
                pfnFilterFunc, pfnFilterFunc4Values, nKernelRadius, fMaxVal);
4379
0
        }
4380
4381
0
        default:
4382
0
            break;
4383
0
    }
4384
4385
0
    CPLAssert(false);
4386
0
    return CE_Failure;
4387
0
}
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionInternal<true, true>(GDALOverviewResampleArgs const&, void const*, void**, GDALDataType*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionInternal<false, true>(GDALOverviewResampleArgs const&, void const*, void**, GDALDataType*)
Unexecuted instantiation: overview.cpp:CPLErr GDALResampleChunk_ConvolutionInternal<false, false>(GDALOverviewResampleArgs const&, void const*, void**, GDALDataType*)
4388
4389
static CPLErr
4390
GDALResampleChunk_Convolution(const GDALOverviewResampleArgs &args,
4391
                              const void *pChunk, void **ppDstBuffer,
4392
                              GDALDataType *peDstBufferDataType)
4393
0
{
4394
0
    if (EQUAL(args.pszResampling, "CUBIC") ||
4395
0
        EQUAL(args.pszResampling, "LANCZOS"))
4396
0
        return GDALResampleChunk_ConvolutionInternal<
4397
0
            /* bKernelWithNegativeWeights=*/true, /* bNeedRescale = */ true>(
4398
0
            args, pChunk, ppDstBuffer, peDstBufferDataType);
4399
0
    else if (EQUAL(args.pszResampling, "CUBICSPLINE"))
4400
0
        return GDALResampleChunk_ConvolutionInternal<false, true>(
4401
0
            args, pChunk, ppDstBuffer, peDstBufferDataType);
4402
0
    else
4403
0
        return GDALResampleChunk_ConvolutionInternal<false, false>(
4404
0
            args, pChunk, ppDstBuffer, peDstBufferDataType);
4405
0
}
4406
4407
/************************************************************************/
4408
/*                       GDALResampleChunkC32R()                        */
4409
/************************************************************************/
4410
4411
static CPLErr GDALResampleChunkC32R(const int nSrcWidth, const int nSrcHeight,
4412
                                    const float *pafChunk, const int nChunkYOff,
4413
                                    const int nChunkYSize, const int nDstYOff,
4414
                                    const int nDstYOff2, const int nOvrXSize,
4415
                                    const int nOvrYSize, void **ppDstBuffer,
4416
                                    GDALDataType *peDstBufferDataType,
4417
                                    const char *pszResampling)
4418
4419
0
{
4420
0
    enum Method
4421
0
    {
4422
0
        NEAR,
4423
0
        AVERAGE,
4424
0
        AVERAGE_MAGPHASE,
4425
0
        RMS,
4426
0
    };
4427
4428
0
    Method eMethod = NEAR;
4429
0
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
4430
0
    {
4431
0
        eMethod = NEAR;
4432
0
    }
4433
0
    else if (EQUAL(pszResampling, "AVERAGE_MAGPHASE"))
4434
0
    {
4435
0
        eMethod = AVERAGE_MAGPHASE;
4436
0
    }
4437
0
    else if (EQUAL(pszResampling, "RMS"))
4438
0
    {
4439
0
        eMethod = RMS;
4440
0
    }
4441
0
    else if (STARTS_WITH_CI(pszResampling, "AVER"))
4442
0
    {
4443
0
        eMethod = AVERAGE;
4444
0
    }
4445
0
    else
4446
0
    {
4447
0
        CPLError(
4448
0
            CE_Failure, CPLE_NotSupported,
4449
0
            "Resampling method %s is not supported for complex data types. "
4450
0
            "Only NEAREST, AVERAGE, AVERAGE_MAGPHASE and RMS are supported",
4451
0
            pszResampling);
4452
0
        return CE_Failure;
4453
0
    }
4454
4455
0
    const int nOXSize = nOvrXSize;
4456
0
    *ppDstBuffer = VSI_MALLOC3_VERBOSE(nOXSize, nDstYOff2 - nDstYOff,
4457
0
                                       GDALGetDataTypeSizeBytes(GDT_CFloat32));
4458
0
    if (*ppDstBuffer == nullptr)
4459
0
    {
4460
0
        return CE_Failure;
4461
0
    }
4462
0
    float *const pafDstBuffer = static_cast<float *>(*ppDstBuffer);
4463
0
    *peDstBufferDataType = GDT_CFloat32;
4464
4465
0
    const int nOYSize = nOvrYSize;
4466
0
    const double dfXRatioDstToSrc = static_cast<double>(nSrcWidth) / nOXSize;
4467
0
    const double dfYRatioDstToSrc = static_cast<double>(nSrcHeight) / nOYSize;
4468
4469
    /* ==================================================================== */
4470
    /*      Loop over destination scanlines.                                */
4471
    /* ==================================================================== */
4472
0
    for (int iDstLine = nDstYOff; iDstLine < nDstYOff2; ++iDstLine)
4473
0
    {
4474
0
        int nSrcYOff = static_cast<int>(0.5 + iDstLine * dfYRatioDstToSrc);
4475
0
        if (nSrcYOff < nChunkYOff)
4476
0
            nSrcYOff = nChunkYOff;
4477
4478
0
        int nSrcYOff2 =
4479
0
            static_cast<int>(0.5 + (iDstLine + 1) * dfYRatioDstToSrc);
4480
0
        if (nSrcYOff2 == nSrcYOff)
4481
0
            nSrcYOff2++;
4482
4483
0
        if (nSrcYOff2 > nSrcHeight || iDstLine == nOYSize - 1)
4484
0
        {
4485
0
            if (nSrcYOff == nSrcHeight && nSrcHeight - 1 >= nChunkYOff)
4486
0
                nSrcYOff = nSrcHeight - 1;
4487
0
            nSrcYOff2 = nSrcHeight;
4488
0
        }
4489
0
        if (nSrcYOff2 > nChunkYOff + nChunkYSize)
4490
0
            nSrcYOff2 = nChunkYOff + nChunkYSize;
4491
4492
0
        const float *const pafSrcScanline =
4493
0
            pafChunk +
4494
0
            (static_cast<size_t>(nSrcYOff - nChunkYOff) * nSrcWidth) * 2;
4495
0
        float *const pafDstScanline =
4496
0
            pafDstBuffer +
4497
0
            static_cast<size_t>(iDstLine - nDstYOff) * 2 * nOXSize;
4498
4499
        /* --------------------------------------------------------------------
4500
         */
4501
        /*      Loop over destination pixels */
4502
        /* --------------------------------------------------------------------
4503
         */
4504
0
        for (int iDstPixel = 0; iDstPixel < nOXSize; ++iDstPixel)
4505
0
        {
4506
0
            const size_t iDstPixelSZ = static_cast<size_t>(iDstPixel);
4507
0
            int nSrcXOff = static_cast<int>(0.5 + iDstPixel * dfXRatioDstToSrc);
4508
0
            int nSrcXOff2 =
4509
0
                static_cast<int>(0.5 + (iDstPixel + 1) * dfXRatioDstToSrc);
4510
0
            if (nSrcXOff2 == nSrcXOff)
4511
0
                nSrcXOff2++;
4512
0
            if (nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize - 1)
4513
0
            {
4514
0
                if (nSrcXOff == nSrcWidth && nSrcWidth - 1 >= 0)
4515
0
                    nSrcXOff = nSrcWidth - 1;
4516
0
                nSrcXOff2 = nSrcWidth;
4517
0
            }
4518
0
            const size_t nSrcXOffSZ = static_cast<size_t>(nSrcXOff);
4519
4520
0
            if (eMethod == NEAR)
4521
0
            {
4522
0
                pafDstScanline[iDstPixelSZ * 2] =
4523
0
                    pafSrcScanline[nSrcXOffSZ * 2];
4524
0
                pafDstScanline[iDstPixelSZ * 2 + 1] =
4525
0
                    pafSrcScanline[nSrcXOffSZ * 2 + 1];
4526
0
            }
4527
0
            else if (eMethod == AVERAGE_MAGPHASE)
4528
0
            {
4529
0
                double dfTotalR = 0.0;
4530
0
                double dfTotalI = 0.0;
4531
0
                double dfTotalM = 0.0;
4532
0
                size_t nCount = 0;
4533
4534
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4535
0
                {
4536
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4537
0
                    {
4538
0
                        const double dfR = double(
4539
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4540
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4541
0
                                               nSrcWidth * 2]);
4542
0
                        const double dfI = double(
4543
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4544
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4545
0
                                               nSrcWidth * 2 +
4546
0
                                           1]);
4547
0
                        dfTotalR += dfR;
4548
0
                        dfTotalI += dfI;
4549
0
                        dfTotalM += std::hypot(dfR, dfI);
4550
0
                        ++nCount;
4551
0
                    }
4552
0
                }
4553
4554
0
                CPLAssert(nCount > 0);
4555
0
                if (nCount == 0)
4556
0
                {
4557
0
                    pafDstScanline[iDstPixelSZ * 2] = 0.0;
4558
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4559
0
                }
4560
0
                else
4561
0
                {
4562
0
                    pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4563
0
                        dfTotalR / static_cast<double>(nCount));
4564
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4565
0
                        dfTotalI / static_cast<double>(nCount));
4566
0
                    const double dfM =
4567
0
                        double(std::hypot(pafDstScanline[iDstPixelSZ * 2],
4568
0
                                          pafDstScanline[iDstPixelSZ * 2 + 1]));
4569
0
                    const double dfDesiredM =
4570
0
                        dfTotalM / static_cast<double>(nCount);
4571
0
                    double dfRatio = 1.0;
4572
0
                    if (dfM != 0.0)
4573
0
                        dfRatio = dfDesiredM / dfM;
4574
4575
0
                    pafDstScanline[iDstPixelSZ * 2] *=
4576
0
                        static_cast<float>(dfRatio);
4577
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] *=
4578
0
                        static_cast<float>(dfRatio);
4579
0
                }
4580
0
            }
4581
0
            else if (eMethod == RMS)
4582
0
            {
4583
0
                double dfTotalR = 0.0;
4584
0
                double dfTotalI = 0.0;
4585
0
                size_t nCount = 0;
4586
4587
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4588
0
                {
4589
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4590
0
                    {
4591
0
                        const double dfR = double(
4592
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4593
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4594
0
                                               nSrcWidth * 2]);
4595
0
                        const double dfI = double(
4596
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4597
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4598
0
                                               nSrcWidth * 2 +
4599
0
                                           1]);
4600
4601
0
                        dfTotalR += SQUARE(dfR);
4602
0
                        dfTotalI += SQUARE(dfI);
4603
4604
0
                        ++nCount;
4605
0
                    }
4606
0
                }
4607
4608
0
                CPLAssert(nCount > 0);
4609
0
                if (nCount == 0)
4610
0
                {
4611
0
                    pafDstScanline[iDstPixelSZ * 2] = 0.0;
4612
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4613
0
                }
4614
0
                else
4615
0
                {
4616
                    /* compute RMS */
4617
0
                    pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4618
0
                        sqrt(dfTotalR / static_cast<double>(nCount)));
4619
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4620
0
                        sqrt(dfTotalI / static_cast<double>(nCount)));
4621
0
                }
4622
0
            }
4623
0
            else if (eMethod == AVERAGE)
4624
0
            {
4625
0
                double dfTotalR = 0.0;
4626
0
                double dfTotalI = 0.0;
4627
0
                size_t nCount = 0;
4628
4629
0
                for (int iY = nSrcYOff; iY < nSrcYOff2; ++iY)
4630
0
                {
4631
0
                    for (int iX = nSrcXOff; iX < nSrcXOff2; ++iX)
4632
0
                    {
4633
                        // TODO(schwehr): Maybe use std::complex?
4634
0
                        dfTotalR += double(
4635
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4636
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4637
0
                                               nSrcWidth * 2]);
4638
0
                        dfTotalI += double(
4639
0
                            pafSrcScanline[static_cast<size_t>(iX) * 2 +
4640
0
                                           static_cast<size_t>(iY - nSrcYOff) *
4641
0
                                               nSrcWidth * 2 +
4642
0
                                           1]);
4643
0
                        ++nCount;
4644
0
                    }
4645
0
                }
4646
4647
0
                CPLAssert(nCount > 0);
4648
0
                if (nCount == 0)
4649
0
                {
4650
0
                    pafDstScanline[iDstPixelSZ * 2] = 0.0;
4651
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = 0.0;
4652
0
                }
4653
0
                else
4654
0
                {
4655
0
                    pafDstScanline[iDstPixelSZ * 2] = static_cast<float>(
4656
0
                        dfTotalR / static_cast<double>(nCount));
4657
0
                    pafDstScanline[iDstPixelSZ * 2 + 1] = static_cast<float>(
4658
0
                        dfTotalI / static_cast<double>(nCount));
4659
0
                }
4660
0
            }
4661
0
        }
4662
0
    }
4663
4664
0
    return CE_None;
4665
0
}
4666
4667
/************************************************************************/
4668
/*                  GDALRegenerateCascadingOverviews()                  */
4669
/*                                                                      */
4670
/*      Generate a list of overviews in order from largest to           */
4671
/*      smallest, computing each from the next larger.                  */
4672
/************************************************************************/
4673
4674
static CPLErr GDALRegenerateCascadingOverviews(
4675
    GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands,
4676
    const char *pszResampling, GDALProgressFunc pfnProgress,
4677
    void *pProgressData, CSLConstList papszOptions)
4678
4679
0
{
4680
    /* -------------------------------------------------------------------- */
4681
    /*      First, we must put the overviews in order from largest to       */
4682
    /*      smallest.                                                       */
4683
    /* -------------------------------------------------------------------- */
4684
0
    for (int i = 0; i < nOverviews - 1; ++i)
4685
0
    {
4686
0
        for (int j = 0; j < nOverviews - i - 1; ++j)
4687
0
        {
4688
0
            if (papoOvrBands[j]->GetXSize() *
4689
0
                    static_cast<float>(papoOvrBands[j]->GetYSize()) <
4690
0
                papoOvrBands[j + 1]->GetXSize() *
4691
0
                    static_cast<float>(papoOvrBands[j + 1]->GetYSize()))
4692
0
            {
4693
0
                GDALRasterBand *poTempBand = papoOvrBands[j];
4694
0
                papoOvrBands[j] = papoOvrBands[j + 1];
4695
0
                papoOvrBands[j + 1] = poTempBand;
4696
0
            }
4697
0
        }
4698
0
    }
4699
4700
    /* -------------------------------------------------------------------- */
4701
    /*      Count total pixels so we can prepare appropriate scaled         */
4702
    /*      progress functions.                                             */
4703
    /* -------------------------------------------------------------------- */
4704
0
    double dfTotalPixels = 0.0;
4705
4706
0
    for (int i = 0; i < nOverviews; ++i)
4707
0
    {
4708
0
        dfTotalPixels += papoOvrBands[i]->GetXSize() *
4709
0
                         static_cast<double>(papoOvrBands[i]->GetYSize());
4710
0
    }
4711
4712
    /* -------------------------------------------------------------------- */
4713
    /*      Generate all the bands.                                         */
4714
    /* -------------------------------------------------------------------- */
4715
0
    double dfPixelsProcessed = 0.0;
4716
4717
0
    CPLStringList aosOptions(papszOptions);
4718
0
    aosOptions.SetNameValue("CASCADING", "YES");
4719
0
    for (int i = 0; i < nOverviews; ++i)
4720
0
    {
4721
0
        GDALRasterBand *poBaseBand = poSrcBand;
4722
0
        if (i != 0)
4723
0
            poBaseBand = papoOvrBands[i - 1];
4724
4725
0
        double dfPixels = papoOvrBands[i]->GetXSize() *
4726
0
                          static_cast<double>(papoOvrBands[i]->GetYSize());
4727
4728
0
        void *pScaledProgressData = GDALCreateScaledProgress(
4729
0
            dfPixelsProcessed / dfTotalPixels,
4730
0
            (dfPixelsProcessed + dfPixels) / dfTotalPixels, pfnProgress,
4731
0
            pProgressData);
4732
4733
0
        const CPLErr eErr = GDALRegenerateOverviewsEx(
4734
0
            poBaseBand, 1,
4735
0
            reinterpret_cast<GDALRasterBandH *>(papoOvrBands) + i,
4736
0
            pszResampling, GDALScaledProgress, pScaledProgressData,
4737
0
            aosOptions.List());
4738
0
        GDALDestroyScaledProgress(pScaledProgressData);
4739
4740
0
        if (eErr != CE_None)
4741
0
            return eErr;
4742
4743
0
        dfPixelsProcessed += dfPixels;
4744
4745
        // Only do the bit2grayscale promotion on the base band.
4746
0
        if (STARTS_WITH_CI(pszResampling,
4747
0
                           "AVERAGE_BIT2G" /* AVERAGE_BIT2GRAYSCALE */))
4748
0
            pszResampling = "AVERAGE";
4749
0
    }
4750
4751
0
    return CE_None;
4752
0
}
4753
4754
/************************************************************************/
4755
/*                      GDALGetResampleFunction()                       */
4756
/************************************************************************/
4757
4758
GDALResampleFunction GDALGetResampleFunction(const char *pszResampling,
4759
                                             int *pnRadius)
4760
0
{
4761
0
    if (pnRadius)
4762
0
        *pnRadius = 0;
4763
0
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
4764
0
        return GDALResampleChunk_Near;
4765
0
    else if (STARTS_WITH_CI(pszResampling, "AVER") ||
4766
0
             EQUAL(pszResampling, "RMS"))
4767
0
        return GDALResampleChunk_AverageOrRMS;
4768
0
    else if (EQUAL(pszResampling, "GAUSS"))
4769
0
    {
4770
0
        if (pnRadius)
4771
0
            *pnRadius = 1;
4772
0
        return GDALResampleChunk_Gauss;
4773
0
    }
4774
0
    else if (EQUAL(pszResampling, "MODE"))
4775
0
        return GDALResampleChunk_Mode;
4776
0
    else if (EQUAL(pszResampling, "CUBIC"))
4777
0
    {
4778
0
        if (pnRadius)
4779
0
            *pnRadius = GWKGetFilterRadius(GRA_Cubic);
4780
0
        return GDALResampleChunk_Convolution;
4781
0
    }
4782
0
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
4783
0
    {
4784
0
        if (pnRadius)
4785
0
            *pnRadius = GWKGetFilterRadius(GRA_CubicSpline);
4786
0
        return GDALResampleChunk_Convolution;
4787
0
    }
4788
0
    else if (EQUAL(pszResampling, "LANCZOS"))
4789
0
    {
4790
0
        if (pnRadius)
4791
0
            *pnRadius = GWKGetFilterRadius(GRA_Lanczos);
4792
0
        return GDALResampleChunk_Convolution;
4793
0
    }
4794
0
    else if (EQUAL(pszResampling, "BILINEAR"))
4795
0
    {
4796
0
        if (pnRadius)
4797
0
            *pnRadius = GWKGetFilterRadius(GRA_Bilinear);
4798
0
        return GDALResampleChunk_Convolution;
4799
0
    }
4800
0
    else
4801
0
    {
4802
0
        CPLError(
4803
0
            CE_Failure, CPLE_AppDefined,
4804
0
            "GDALGetResampleFunction: Unsupported resampling method \"%s\".",
4805
0
            pszResampling);
4806
0
        return nullptr;
4807
0
    }
4808
0
}
4809
4810
/************************************************************************/
4811
/*                       GDALGetOvrWorkDataType()                       */
4812
/************************************************************************/
4813
4814
GDALDataType GDALGetOvrWorkDataType(const char *pszResampling,
4815
                                    GDALDataType eSrcDataType)
4816
0
{
4817
0
    if (STARTS_WITH_CI(pszResampling, "NEAR") || EQUAL(pszResampling, "MODE"))
4818
0
    {
4819
0
        return eSrcDataType;
4820
0
    }
4821
0
    else if (eSrcDataType == GDT_UInt8 &&
4822
0
             (STARTS_WITH_CI(pszResampling, "AVER") ||
4823
0
              EQUAL(pszResampling, "RMS") || EQUAL(pszResampling, "CUBIC") ||
4824
0
              EQUAL(pszResampling, "CUBICSPLINE") ||
4825
0
              EQUAL(pszResampling, "LANCZOS") ||
4826
0
              EQUAL(pszResampling, "BILINEAR") || EQUAL(pszResampling, "MODE")))
4827
0
    {
4828
0
        return GDT_UInt8;
4829
0
    }
4830
0
    else if (eSrcDataType == GDT_UInt16 &&
4831
0
             (STARTS_WITH_CI(pszResampling, "AVER") ||
4832
0
              EQUAL(pszResampling, "RMS") || EQUAL(pszResampling, "CUBIC") ||
4833
0
              EQUAL(pszResampling, "CUBICSPLINE") ||
4834
0
              EQUAL(pszResampling, "LANCZOS") ||
4835
0
              EQUAL(pszResampling, "BILINEAR") || EQUAL(pszResampling, "MODE")))
4836
0
    {
4837
0
        return GDT_UInt16;
4838
0
    }
4839
0
    else if (EQUAL(pszResampling, "GAUSS"))
4840
0
        return GDT_Float64;
4841
4842
0
    if (eSrcDataType == GDT_UInt8 || eSrcDataType == GDT_Int8 ||
4843
0
        eSrcDataType == GDT_UInt16 || eSrcDataType == GDT_Int16 ||
4844
0
        eSrcDataType == GDT_Float32)
4845
0
    {
4846
0
        return GDT_Float32;
4847
0
    }
4848
0
    return GDT_Float64;
4849
0
}
4850
4851
namespace
4852
{
4853
// Structure to hold a pointer to free with CPLFree()
4854
struct PointerHolder
4855
{
4856
    void *ptr = nullptr;
4857
4858
0
    template <class T> explicit PointerHolder(T *&ptrIn) : ptr(ptrIn)
4859
0
    {
4860
0
        ptrIn = nullptr;
4861
0
    }
Unexecuted instantiation: overview.cpp:(anonymous namespace)::PointerHolder::PointerHolder<void>(void*&)
Unexecuted instantiation: overview.cpp:(anonymous namespace)::PointerHolder::PointerHolder<unsigned char>(unsigned char*&)
4862
4863
    template <class T>
4864
    explicit PointerHolder(std::unique_ptr<T, VSIFreeReleaser> ptrIn)
4865
0
        : ptr(ptrIn.release())
4866
0
    {
4867
0
    }
Unexecuted instantiation: overview.cpp:(anonymous namespace)::PointerHolder::PointerHolder<unsigned char>(std::__1::unique_ptr<unsigned char, VSIFreeReleaser>)
Unexecuted instantiation: overview.cpp:(anonymous namespace)::PointerHolder::PointerHolder<void>(std::__1::unique_ptr<void, VSIFreeReleaser>)
4868
4869
    ~PointerHolder()
4870
0
    {
4871
0
        CPLFree(ptr);
4872
0
    }
4873
4874
    PointerHolder(const PointerHolder &) = delete;
4875
    PointerHolder &operator=(const PointerHolder &) = delete;
4876
};
4877
}  // namespace
4878
4879
/************************************************************************/
4880
/*                      GDALRegenerateOverviews()                       */
4881
/************************************************************************/
4882
4883
/**
4884
 * \brief Generate downsampled overviews.
4885
 *
4886
 * This function will generate one or more overview images from a base image
4887
 * using the requested downsampling algorithm.  Its primary use is for
4888
 * generating overviews via GDALDataset::BuildOverviews(), but it can also be
4889
 * used to generate downsampled images in one file from another outside the
4890
 * overview architecture.
4891
 *
4892
 * The output bands need to exist in advance.
4893
 *
4894
 * The full set of resampling algorithms is documented in
4895
 * GDALDataset::BuildOverviews().
4896
 *
4897
 * This function will honour properly NODATA_VALUES tuples (special dataset
4898
 * metadata) so that only a given RGB triplet (in case of a RGB image) will be
4899
 * considered as the nodata value and not each value of the triplet
4900
 * independently per band.
4901
 *
4902
 * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
4903
 * to "ALL_CPUS" or a integer value to specify the number of threads to use for
4904
 * overview computation.
4905
 *
4906
 * @param hSrcBand the source (base level) band.
4907
 * @param nOverviewCount the number of downsampled bands being generated.
4908
 * @param pahOvrBands the list of downsampled bands to be generated.
4909
 * @param pszResampling Resampling algorithm (e.g. "AVERAGE").
4910
 * @param pfnProgress progress report function.
4911
 * @param pProgressData progress function callback data.
4912
 * @return CE_None on success or CE_Failure on failure.
4913
 */
4914
CPLErr GDALRegenerateOverviews(GDALRasterBandH hSrcBand, int nOverviewCount,
4915
                               GDALRasterBandH *pahOvrBands,
4916
                               const char *pszResampling,
4917
                               GDALProgressFunc pfnProgress,
4918
                               void *pProgressData)
4919
4920
0
{
4921
0
    return GDALRegenerateOverviewsEx(hSrcBand, nOverviewCount, pahOvrBands,
4922
0
                                     pszResampling, pfnProgress, pProgressData,
4923
0
                                     nullptr);
4924
0
}
4925
4926
/************************************************************************/
4927
/*                     GDALRegenerateOverviewsEx()                      */
4928
/************************************************************************/
4929
4930
constexpr int RADIUS_TO_DIAMETER = 2;
4931
4932
/**
4933
 * \brief Generate downsampled overviews.
4934
 *
4935
 * This function will generate one or more overview images from a base image
4936
 * using the requested downsampling algorithm.  Its primary use is for
4937
 * generating overviews via GDALDataset::BuildOverviews(), but it can also be
4938
 * used to generate downsampled images in one file from another outside the
4939
 * overview architecture.
4940
 *
4941
 * The output bands need to exist in advance.
4942
 *
4943
 * The full set of resampling algorithms is documented in
4944
 * GDALDataset::BuildOverviews().
4945
 *
4946
 * This function will honour properly NODATA_VALUES tuples (special dataset
4947
 * metadata) so that only a given RGB triplet (in case of a RGB image) will be
4948
 * considered as the nodata value and not each value of the triplet
4949
 * independently per band.
4950
 *
4951
 * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
4952
 * to "ALL_CPUS" or a integer value to specify the number of threads to use for
4953
 * overview computation.
4954
 *
4955
 * @param hSrcBand the source (base level) band.
4956
 * @param nOverviewCount the number of downsampled bands being generated.
4957
 * @param pahOvrBands the list of downsampled bands to be generated.
4958
 * @param pszResampling Resampling algorithm (e.g. "AVERAGE").
4959
 * @param pfnProgress progress report function.
4960
 * @param pProgressData progress function callback data.
4961
 * @param papszOptions NULL terminated list of options as key=value pairs, or
4962
 * NULL
4963
 * @return CE_None on success or CE_Failure on failure.
4964
 * @since GDAL 3.6
4965
 */
4966
CPLErr GDALRegenerateOverviewsEx(GDALRasterBandH hSrcBand, int nOverviewCount,
4967
                                 GDALRasterBandH *pahOvrBands,
4968
                                 const char *pszResampling,
4969
                                 GDALProgressFunc pfnProgress,
4970
                                 void *pProgressData, CSLConstList papszOptions)
4971
4972
0
{
4973
0
    GDALRasterBand *poSrcBand = GDALRasterBand::FromHandle(hSrcBand);
4974
0
    GDALRasterBand **papoOvrBands =
4975
0
        reinterpret_cast<GDALRasterBand **>(pahOvrBands);
4976
4977
0
    if (pfnProgress == nullptr)
4978
0
        pfnProgress = GDALDummyProgress;
4979
4980
0
    if (EQUAL(pszResampling, "NONE"))
4981
0
        return CE_None;
4982
4983
0
    int nKernelRadius = 0;
4984
0
    GDALResampleFunction pfnResampleFn =
4985
0
        GDALGetResampleFunction(pszResampling, &nKernelRadius);
4986
4987
0
    if (pfnResampleFn == nullptr)
4988
0
        return CE_Failure;
4989
4990
    /* -------------------------------------------------------------------- */
4991
    /*      Check color tables...                                           */
4992
    /* -------------------------------------------------------------------- */
4993
0
    GDALColorTable *poColorTable = nullptr;
4994
4995
0
    if ((STARTS_WITH_CI(pszResampling, "AVER") || EQUAL(pszResampling, "RMS") ||
4996
0
         EQUAL(pszResampling, "MODE") || EQUAL(pszResampling, "GAUSS")) &&
4997
0
        poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
4998
0
    {
4999
0
        poColorTable = poSrcBand->GetColorTable();
5000
0
        if (poColorTable != nullptr)
5001
0
        {
5002
0
            if (poColorTable->GetPaletteInterpretation() != GPI_RGB)
5003
0
            {
5004
0
                CPLError(CE_Warning, CPLE_AppDefined,
5005
0
                         "Computing overviews on palette index raster bands "
5006
0
                         "with a palette whose color interpretation is not RGB "
5007
0
                         "will probably lead to unexpected results.");
5008
0
                poColorTable = nullptr;
5009
0
            }
5010
0
            else if (poColorTable->IsIdentity())
5011
0
            {
5012
0
                poColorTable = nullptr;
5013
0
            }
5014
0
        }
5015
0
        else
5016
0
        {
5017
0
            CPLError(CE_Warning, CPLE_AppDefined,
5018
0
                     "Computing overviews on palette index raster bands "
5019
0
                     "without a palette will probably lead to unexpected "
5020
0
                     "results.");
5021
0
        }
5022
0
    }
5023
    // Not ready yet
5024
0
    else if ((EQUAL(pszResampling, "CUBIC") ||
5025
0
              EQUAL(pszResampling, "CUBICSPLINE") ||
5026
0
              EQUAL(pszResampling, "LANCZOS") ||
5027
0
              EQUAL(pszResampling, "BILINEAR")) &&
5028
0
             poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
5029
0
    {
5030
0
        CPLError(CE_Warning, CPLE_AppDefined,
5031
0
                 "Computing %s overviews on palette index raster bands "
5032
0
                 "will probably lead to unexpected results.",
5033
0
                 pszResampling);
5034
0
    }
5035
5036
    // If we have a nodata mask and we are doing something more complicated
5037
    // than nearest neighbouring, we have to fetch to nodata mask.
5038
5039
0
    GDALRasterBand *poMaskBand = nullptr;
5040
0
    bool bUseNoDataMask = false;
5041
0
    bool bCanUseCascaded = true;
5042
5043
0
    if (!STARTS_WITH_CI(pszResampling, "NEAR"))
5044
0
    {
5045
        // Special case if we are an alpha/mask band. We want it to be
5046
        // considered as the mask band to avoid alpha=0 to be taken into account
5047
        // in average computation.
5048
0
        if (poSrcBand->IsMaskBand())
5049
0
        {
5050
0
            poMaskBand = poSrcBand;
5051
0
            bUseNoDataMask = true;
5052
0
        }
5053
0
        else
5054
0
        {
5055
0
            poMaskBand = poSrcBand->GetMaskBand();
5056
0
            const int nMaskFlags = poSrcBand->GetMaskFlags();
5057
0
            bCanUseCascaded =
5058
0
                (nMaskFlags == GMF_NODATA || nMaskFlags == GMF_ALL_VALID);
5059
0
            bUseNoDataMask = (nMaskFlags & GMF_ALL_VALID) == 0;
5060
0
        }
5061
0
    }
5062
5063
0
    int nHasNoData = 0;
5064
0
    const double dfNoDataValue = poSrcBand->GetNoDataValue(&nHasNoData);
5065
0
    const bool bHasNoData = CPL_TO_BOOL(nHasNoData);
5066
0
    const bool bPropagateNoData =
5067
0
        CPLTestBool(CPLGetConfigOption("GDAL_OVR_PROPAGATE_NODATA", "NO"));
5068
5069
0
    if (poSrcBand->GetBand() == 1 && bUseNoDataMask &&
5070
0
        CSLFetchNameValue(papszOptions, "CASCADING") == nullptr)
5071
0
    {
5072
0
        std::string osDetailMessage;
5073
0
        if (poSrcBand->HasConflictingMaskSources(&osDetailMessage, false))
5074
0
        {
5075
0
            CPLError(
5076
0
                CE_Warning, CPLE_AppDefined, "%s%s", osDetailMessage.c_str(),
5077
0
                bHasNoData
5078
0
                    ? "Only the nodata value will be taken into account."
5079
0
                    : "Only the first listed one will be taken into account.");
5080
0
        }
5081
0
    }
5082
5083
    /* -------------------------------------------------------------------- */
5084
    /*      If we are operating on multiple overviews, and using            */
5085
    /*      averaging, lets do them in cascading order to reduce the        */
5086
    /*      amount of computation.                                          */
5087
    /* -------------------------------------------------------------------- */
5088
5089
    // In case the mask made be computed from another band of the dataset,
5090
    // we can't use cascaded generation, as the computation of the overviews
5091
    // of the band used for the mask band may not have yet occurred (#3033).
5092
0
    if ((STARTS_WITH_CI(pszResampling, "AVER") ||
5093
0
         EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "RMS") ||
5094
0
         EQUAL(pszResampling, "CUBIC") || EQUAL(pszResampling, "CUBICSPLINE") ||
5095
0
         EQUAL(pszResampling, "LANCZOS") || EQUAL(pszResampling, "BILINEAR") ||
5096
0
         EQUAL(pszResampling, "MODE")) &&
5097
0
        nOverviewCount > 1 && bCanUseCascaded)
5098
0
        return GDALRegenerateCascadingOverviews(
5099
0
            poSrcBand, nOverviewCount, papoOvrBands, pszResampling, pfnProgress,
5100
0
            pProgressData, papszOptions);
5101
5102
    /* -------------------------------------------------------------------- */
5103
    /*      Setup one horizontal swath to read from the raw buffer.         */
5104
    /* -------------------------------------------------------------------- */
5105
0
    int nFRXBlockSize = 0;
5106
0
    int nFRYBlockSize = 0;
5107
0
    poSrcBand->GetBlockSize(&nFRXBlockSize, &nFRYBlockSize);
5108
5109
0
    const GDALDataType eSrcDataType = poSrcBand->GetRasterDataType();
5110
0
    const bool bUseGenericResampleFn = STARTS_WITH_CI(pszResampling, "NEAR") ||
5111
0
                                       EQUAL(pszResampling, "MODE") ||
5112
0
                                       !GDALDataTypeIsComplex(eSrcDataType);
5113
0
    const GDALDataType eWrkDataType =
5114
0
        bUseGenericResampleFn
5115
0
            ? GDALGetOvrWorkDataType(pszResampling, eSrcDataType)
5116
0
            : GDT_CFloat32;
5117
5118
0
    const int nWidth = poSrcBand->GetXSize();
5119
0
    const int nHeight = poSrcBand->GetYSize();
5120
5121
0
    int nMaxOvrFactor = 1;
5122
0
    for (int iOverview = 0; iOverview < nOverviewCount; ++iOverview)
5123
0
    {
5124
0
        const int nDstWidth = papoOvrBands[iOverview]->GetXSize();
5125
0
        const int nDstHeight = papoOvrBands[iOverview]->GetYSize();
5126
0
        nMaxOvrFactor = std::max(
5127
0
            nMaxOvrFactor,
5128
0
            static_cast<int>(static_cast<double>(nWidth) / nDstWidth + 0.5));
5129
0
        nMaxOvrFactor = std::max(
5130
0
            nMaxOvrFactor,
5131
0
            static_cast<int>(static_cast<double>(nHeight) / nDstHeight + 0.5));
5132
0
    }
5133
5134
0
    int nFullResYChunk = nFRYBlockSize;
5135
0
    int nMaxChunkYSizeQueried = 0;
5136
5137
0
    const auto UpdateChunkHeightAndGetChunkSize =
5138
0
        [&nFullResYChunk, &nMaxChunkYSizeQueried, nKernelRadius, nMaxOvrFactor,
5139
0
         eWrkDataType, nWidth]()
5140
0
    {
5141
        // Make sure that round(nChunkYOff / nMaxOvrFactor) < round((nChunkYOff
5142
        // + nFullResYChunk) / nMaxOvrFactor)
5143
0
        if (nMaxOvrFactor > INT_MAX / RADIUS_TO_DIAMETER)
5144
0
        {
5145
0
            return GINTBIG_MAX;
5146
0
        }
5147
0
        nFullResYChunk =
5148
0
            std::max(nFullResYChunk, RADIUS_TO_DIAMETER * nMaxOvrFactor);
5149
0
        if ((nKernelRadius > 0 &&
5150
0
             nMaxOvrFactor > INT_MAX / (RADIUS_TO_DIAMETER * nKernelRadius)) ||
5151
0
            nFullResYChunk >
5152
0
                INT_MAX - RADIUS_TO_DIAMETER * nKernelRadius * nMaxOvrFactor)
5153
0
        {
5154
0
            return GINTBIG_MAX;
5155
0
        }
5156
0
        nMaxChunkYSizeQueried =
5157
0
            nFullResYChunk + RADIUS_TO_DIAMETER * nKernelRadius * nMaxOvrFactor;
5158
0
        if (GDALGetDataTypeSizeBytes(eWrkDataType) >
5159
0
            std::numeric_limits<int64_t>::max() /
5160
0
                (static_cast<int64_t>(nMaxChunkYSizeQueried) * nWidth))
5161
0
        {
5162
0
            return GINTBIG_MAX;
5163
0
        }
5164
0
        return static_cast<GIntBig>(GDALGetDataTypeSizeBytes(eWrkDataType)) *
5165
0
               nMaxChunkYSizeQueried * nWidth;
5166
0
    };
5167
5168
0
    const char *pszChunkYSize =
5169
0
        CPLGetConfigOption("GDAL_OVR_CHUNKYSIZE", nullptr);
5170
0
#ifndef __COVERITY__
5171
    // Only configurable for debug / testing
5172
0
    if (pszChunkYSize)
5173
0
    {
5174
0
        nFullResYChunk = atoi(pszChunkYSize);
5175
0
    }
5176
0
#endif
5177
5178
    // Only configurable for debug / testing
5179
0
    const int nChunkMaxSize =
5180
0
        atoi(CPLGetConfigOption("GDAL_OVR_CHUNK_MAX_SIZE", "10485760"));
5181
5182
0
    auto nChunkSize = UpdateChunkHeightAndGetChunkSize();
5183
0
    if (nChunkSize > nChunkMaxSize)
5184
0
    {
5185
0
        if (poColorTable == nullptr && nFRXBlockSize < nWidth &&
5186
0
            !GDALDataTypeIsComplex(eSrcDataType) &&
5187
0
            (!STARTS_WITH_CI(pszResampling, "AVER") ||
5188
0
             EQUAL(pszResampling, "AVERAGE")))
5189
0
        {
5190
            // If this is tiled, then use GDALRegenerateOverviewsMultiBand()
5191
            // which use a block based strategy, which is much less memory
5192
            // hungry.
5193
0
            return GDALRegenerateOverviewsMultiBand(
5194
0
                1, &poSrcBand, nOverviewCount, &papoOvrBands, pszResampling,
5195
0
                pfnProgress, pProgressData, papszOptions);
5196
0
        }
5197
0
        else if (nOverviewCount > 1 && STARTS_WITH_CI(pszResampling, "NEAR"))
5198
0
        {
5199
0
            return GDALRegenerateCascadingOverviews(
5200
0
                poSrcBand, nOverviewCount, papoOvrBands, pszResampling,
5201
0
                pfnProgress, pProgressData, papszOptions);
5202
0
        }
5203
0
    }
5204
0
    else if (pszChunkYSize == nullptr)
5205
0
    {
5206
        // Try to get as close as possible to nChunkMaxSize
5207
0
        while (nChunkSize < nChunkMaxSize / 2)
5208
0
        {
5209
0
            nFullResYChunk *= 2;
5210
0
            nChunkSize = UpdateChunkHeightAndGetChunkSize();
5211
0
        }
5212
0
    }
5213
5214
    // Structure describing a resampling job
5215
0
    struct OvrJob
5216
0
    {
5217
        // Buffers to free when job is finished
5218
0
        std::shared_ptr<PointerHolder> oSrcMaskBufferHolder{};
5219
0
        std::shared_ptr<PointerHolder> oSrcBufferHolder{};
5220
0
        std::unique_ptr<PointerHolder> oDstBufferHolder{};
5221
5222
0
        GDALRasterBand *poDstBand = nullptr;
5223
5224
        // Input parameters of pfnResampleFn
5225
0
        GDALResampleFunction pfnResampleFn = nullptr;
5226
0
        int nSrcWidth = 0;
5227
0
        int nSrcHeight = 0;
5228
0
        int nDstWidth = 0;
5229
0
        GDALOverviewResampleArgs args{};
5230
0
        const void *pChunk = nullptr;
5231
0
        bool bUseGenericResampleFn = false;
5232
5233
        // Output values of resampling function
5234
0
        CPLErr eErr = CE_Failure;
5235
0
        void *pDstBuffer = nullptr;
5236
0
        GDALDataType eDstBufferDataType = GDT_Unknown;
5237
5238
0
        void SetSrcMaskBufferHolder(
5239
0
            const std::shared_ptr<PointerHolder> &oSrcMaskBufferHolderIn)
5240
0
        {
5241
0
            oSrcMaskBufferHolder = oSrcMaskBufferHolderIn;
5242
0
        }
5243
5244
0
        void SetSrcBufferHolder(
5245
0
            const std::shared_ptr<PointerHolder> &oSrcBufferHolderIn)
5246
0
        {
5247
0
            oSrcBufferHolder = oSrcBufferHolderIn;
5248
0
        }
5249
5250
0
        void NotifyFinished()
5251
0
        {
5252
0
            std::lock_guard guard(mutex);
5253
0
            bFinished = true;
5254
0
            cv.notify_one();
5255
0
        }
5256
5257
0
        bool IsFinished()
5258
0
        {
5259
0
            std::lock_guard guard(mutex);
5260
0
            return bFinished;
5261
0
        }
5262
5263
0
        void WaitFinished()
5264
0
        {
5265
0
            std::unique_lock oGuard(mutex);
5266
0
            while (!bFinished)
5267
0
            {
5268
0
                cv.wait(oGuard);
5269
0
            }
5270
0
        }
5271
5272
0
      private:
5273
        // Synchronization
5274
0
        bool bFinished = false;
5275
0
        std::mutex mutex{};
5276
0
        std::condition_variable cv{};
5277
0
    };
5278
5279
    // Thread function to resample
5280
0
    const auto JobResampleFunc = [](void *pData)
5281
0
    {
5282
0
        OvrJob *poJob = static_cast<OvrJob *>(pData);
5283
5284
0
        if (poJob->bUseGenericResampleFn)
5285
0
        {
5286
0
            poJob->eErr = poJob->pfnResampleFn(poJob->args, poJob->pChunk,
5287
0
                                               &(poJob->pDstBuffer),
5288
0
                                               &(poJob->eDstBufferDataType));
5289
0
        }
5290
0
        else
5291
0
        {
5292
0
            poJob->eErr = GDALResampleChunkC32R(
5293
0
                poJob->nSrcWidth, poJob->nSrcHeight,
5294
0
                static_cast<const float *>(poJob->pChunk),
5295
0
                poJob->args.nChunkYOff, poJob->args.nChunkYSize,
5296
0
                poJob->args.nDstYOff, poJob->args.nDstYOff2,
5297
0
                poJob->args.nOvrXSize, poJob->args.nOvrYSize,
5298
0
                &(poJob->pDstBuffer), &(poJob->eDstBufferDataType),
5299
0
                poJob->args.pszResampling);
5300
0
        }
5301
5302
0
        auto pDstBuffer = poJob->pDstBuffer;
5303
0
        poJob->oDstBufferHolder = std::make_unique<PointerHolder>(pDstBuffer);
5304
5305
0
        poJob->NotifyFinished();
5306
0
    };
5307
5308
    // Function to write resample data to target band
5309
0
    const auto WriteJobData = [](const OvrJob *poJob)
5310
0
    {
5311
0
        return poJob->poDstBand->RasterIO(
5312
0
            GF_Write, 0, poJob->args.nDstYOff, poJob->nDstWidth,
5313
0
            poJob->args.nDstYOff2 - poJob->args.nDstYOff, poJob->pDstBuffer,
5314
0
            poJob->nDstWidth, poJob->args.nDstYOff2 - poJob->args.nDstYOff,
5315
0
            poJob->eDstBufferDataType, 0, 0, nullptr);
5316
0
    };
5317
5318
    // Wait for completion of oldest job and serialize it
5319
0
    const auto WaitAndFinalizeOldestJob =
5320
0
        [WriteJobData](std::list<std::unique_ptr<OvrJob>> &jobList)
5321
0
    {
5322
0
        auto poOldestJob = jobList.front().get();
5323
0
        poOldestJob->WaitFinished();
5324
0
        CPLErr l_eErr = poOldestJob->eErr;
5325
0
        if (l_eErr == CE_None)
5326
0
        {
5327
0
            l_eErr = WriteJobData(poOldestJob);
5328
0
        }
5329
5330
0
        jobList.pop_front();
5331
0
        return l_eErr;
5332
0
    };
5333
5334
    // Queue of jobs
5335
0
    std::list<std::unique_ptr<OvrJob>> jobList;
5336
5337
0
    GByte *pabyChunkNodataMask = nullptr;
5338
0
    void *pChunk = nullptr;
5339
5340
0
    const int nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
5341
0
                                           /* bDefaultToAllCPUs=*/false);
5342
0
    auto poThreadPool =
5343
0
        nThreads > 1 ? GDALGetGlobalThreadPool(nThreads) : nullptr;
5344
0
    auto poJobQueue = poThreadPool ? poThreadPool->CreateJobQueue()
5345
0
                                   : std::unique_ptr<CPLJobQueue>(nullptr);
5346
5347
    /* -------------------------------------------------------------------- */
5348
    /*      Loop over image operating on chunks.                            */
5349
    /* -------------------------------------------------------------------- */
5350
0
    int nChunkYOff = 0;
5351
0
    CPLErr eErr = CE_None;
5352
5353
0
    for (nChunkYOff = 0; nChunkYOff < nHeight && eErr == CE_None;
5354
0
         nChunkYOff += nFullResYChunk)
5355
0
    {
5356
0
        if (!pfnProgress(nChunkYOff / static_cast<double>(nHeight), nullptr,
5357
0
                         pProgressData))
5358
0
        {
5359
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
5360
0
            eErr = CE_Failure;
5361
0
        }
5362
5363
0
        if (nFullResYChunk + nChunkYOff > nHeight)
5364
0
            nFullResYChunk = nHeight - nChunkYOff;
5365
5366
0
        int nChunkYOffQueried = nChunkYOff - nKernelRadius * nMaxOvrFactor;
5367
0
        int nChunkYSizeQueried =
5368
0
            nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor;
5369
0
        if (nChunkYOffQueried < 0)
5370
0
        {
5371
0
            nChunkYSizeQueried += nChunkYOffQueried;
5372
0
            nChunkYOffQueried = 0;
5373
0
        }
5374
0
        if (nChunkYOffQueried + nChunkYSizeQueried > nHeight)
5375
0
            nChunkYSizeQueried = nHeight - nChunkYOffQueried;
5376
5377
        // Avoid accumulating too many tasks and exhaust RAM
5378
        // Try to complete already finished jobs
5379
0
        while (eErr == CE_None && !jobList.empty())
5380
0
        {
5381
0
            auto poOldestJob = jobList.front().get();
5382
0
            if (!poOldestJob->IsFinished())
5383
0
                break;
5384
0
            eErr = poOldestJob->eErr;
5385
0
            if (eErr == CE_None)
5386
0
            {
5387
0
                eErr = WriteJobData(poOldestJob);
5388
0
            }
5389
5390
0
            jobList.pop_front();
5391
0
        }
5392
5393
        // And in case we have saturated the number of threads,
5394
        // wait for completion of tasks to go below the threshold.
5395
0
        while (eErr == CE_None &&
5396
0
               jobList.size() >= static_cast<size_t>(nThreads))
5397
0
        {
5398
0
            eErr = WaitAndFinalizeOldestJob(jobList);
5399
0
        }
5400
5401
        // (Re)allocate buffers if needed
5402
0
        if (pChunk == nullptr)
5403
0
        {
5404
0
            pChunk = VSI_MALLOC3_VERBOSE(GDALGetDataTypeSizeBytes(eWrkDataType),
5405
0
                                         nMaxChunkYSizeQueried, nWidth);
5406
0
        }
5407
0
        if (bUseNoDataMask && pabyChunkNodataMask == nullptr)
5408
0
        {
5409
0
            pabyChunkNodataMask = static_cast<GByte *>(
5410
0
                VSI_MALLOC2_VERBOSE(nMaxChunkYSizeQueried, nWidth));
5411
0
        }
5412
5413
0
        if (pChunk == nullptr ||
5414
0
            (bUseNoDataMask && pabyChunkNodataMask == nullptr))
5415
0
        {
5416
0
            CPLFree(pChunk);
5417
0
            CPLFree(pabyChunkNodataMask);
5418
0
            return CE_Failure;
5419
0
        }
5420
5421
        // Read chunk.
5422
0
        if (eErr == CE_None)
5423
0
            eErr = poSrcBand->RasterIO(GF_Read, 0, nChunkYOffQueried, nWidth,
5424
0
                                       nChunkYSizeQueried, pChunk, nWidth,
5425
0
                                       nChunkYSizeQueried, eWrkDataType, 0, 0,
5426
0
                                       nullptr);
5427
0
        if (eErr == CE_None && bUseNoDataMask)
5428
0
            eErr = poMaskBand->RasterIO(GF_Read, 0, nChunkYOffQueried, nWidth,
5429
0
                                        nChunkYSizeQueried, pabyChunkNodataMask,
5430
0
                                        nWidth, nChunkYSizeQueried, GDT_UInt8,
5431
0
                                        0, 0, nullptr);
5432
5433
        // Special case to promote 1bit data to 8bit 0/255 values.
5434
0
        if (EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE"))
5435
0
        {
5436
0
            if (eWrkDataType == GDT_Float32)
5437
0
            {
5438
0
                float *pafChunk = static_cast<float *>(pChunk);
5439
0
                for (size_t i = 0;
5440
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5441
0
                {
5442
0
                    if (pafChunk[i] == 1.0f)
5443
0
                        pafChunk[i] = 255.0f;
5444
0
                }
5445
0
            }
5446
0
            else if (eWrkDataType == GDT_UInt8)
5447
0
            {
5448
0
                GByte *pabyChunk = static_cast<GByte *>(pChunk);
5449
0
                for (size_t i = 0;
5450
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5451
0
                {
5452
0
                    if (pabyChunk[i] == 1)
5453
0
                        pabyChunk[i] = 255;
5454
0
                }
5455
0
            }
5456
0
            else if (eWrkDataType == GDT_UInt16)
5457
0
            {
5458
0
                GUInt16 *pasChunk = static_cast<GUInt16 *>(pChunk);
5459
0
                for (size_t i = 0;
5460
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5461
0
                {
5462
0
                    if (pasChunk[i] == 1)
5463
0
                        pasChunk[i] = 255;
5464
0
                }
5465
0
            }
5466
0
            else if (eWrkDataType == GDT_Float64)
5467
0
            {
5468
0
                double *padfChunk = static_cast<double *>(pChunk);
5469
0
                for (size_t i = 0;
5470
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5471
0
                {
5472
0
                    if (padfChunk[i] == 1.0)
5473
0
                        padfChunk[i] = 255.0;
5474
0
                }
5475
0
            }
5476
0
            else
5477
0
            {
5478
0
                CPLAssert(false);
5479
0
            }
5480
0
        }
5481
0
        else if (EQUAL(pszResampling, "AVERAGE_BIT2GRAYSCALE_MINISWHITE"))
5482
0
        {
5483
0
            if (eWrkDataType == GDT_Float32)
5484
0
            {
5485
0
                float *pafChunk = static_cast<float *>(pChunk);
5486
0
                for (size_t i = 0;
5487
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5488
0
                {
5489
0
                    if (pafChunk[i] == 1.0f)
5490
0
                        pafChunk[i] = 0.0f;
5491
0
                    else if (pafChunk[i] == 0.0f)
5492
0
                        pafChunk[i] = 255.0f;
5493
0
                }
5494
0
            }
5495
0
            else if (eWrkDataType == GDT_UInt8)
5496
0
            {
5497
0
                GByte *pabyChunk = static_cast<GByte *>(pChunk);
5498
0
                for (size_t i = 0;
5499
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5500
0
                {
5501
0
                    if (pabyChunk[i] == 1)
5502
0
                        pabyChunk[i] = 0;
5503
0
                    else if (pabyChunk[i] == 0)
5504
0
                        pabyChunk[i] = 255;
5505
0
                }
5506
0
            }
5507
0
            else if (eWrkDataType == GDT_UInt16)
5508
0
            {
5509
0
                GUInt16 *pasChunk = static_cast<GUInt16 *>(pChunk);
5510
0
                for (size_t i = 0;
5511
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5512
0
                {
5513
0
                    if (pasChunk[i] == 1)
5514
0
                        pasChunk[i] = 0;
5515
0
                    else if (pasChunk[i] == 0)
5516
0
                        pasChunk[i] = 255;
5517
0
                }
5518
0
            }
5519
0
            else if (eWrkDataType == GDT_Float64)
5520
0
            {
5521
0
                double *padfChunk = static_cast<double *>(pChunk);
5522
0
                for (size_t i = 0;
5523
0
                     i < static_cast<size_t>(nChunkYSizeQueried) * nWidth; i++)
5524
0
                {
5525
0
                    if (padfChunk[i] == 1.0)
5526
0
                        padfChunk[i] = 0.0;
5527
0
                    else if (padfChunk[i] == 0.0)
5528
0
                        padfChunk[i] = 255.0;
5529
0
                }
5530
0
            }
5531
0
            else
5532
0
            {
5533
0
                CPLAssert(false);
5534
0
            }
5535
0
        }
5536
5537
0
        auto pChunkRaw = pChunk;
5538
0
        auto pabyChunkNodataMaskRaw = pabyChunkNodataMask;
5539
0
        std::shared_ptr<PointerHolder> oSrcBufferHolder;
5540
0
        std::shared_ptr<PointerHolder> oSrcMaskBufferHolder;
5541
0
        if (poJobQueue)
5542
0
        {
5543
0
            oSrcBufferHolder = std::make_shared<PointerHolder>(pChunk);
5544
0
            oSrcMaskBufferHolder =
5545
0
                std::make_shared<PointerHolder>(pabyChunkNodataMask);
5546
0
        }
5547
5548
0
        for (int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None;
5549
0
             ++iOverview)
5550
0
        {
5551
0
            GDALRasterBand *poDstBand = papoOvrBands[iOverview];
5552
0
            const int nDstWidth = poDstBand->GetXSize();
5553
0
            const int nDstHeight = poDstBand->GetYSize();
5554
5555
0
            const double dfXRatioDstToSrc =
5556
0
                static_cast<double>(nWidth) / nDstWidth;
5557
0
            const double dfYRatioDstToSrc =
5558
0
                static_cast<double>(nHeight) / nDstHeight;
5559
5560
            /* --------------------------------------------------------------------
5561
             */
5562
            /*      Figure out the line to start writing to, and the first line
5563
             */
5564
            /*      to not write to.  In theory this approach should ensure that
5565
             */
5566
            /*      every output line will be written if all input chunks are */
5567
            /*      processed. */
5568
            /* --------------------------------------------------------------------
5569
             */
5570
0
            int nDstYOff =
5571
0
                static_cast<int>(0.5 + nChunkYOff / dfYRatioDstToSrc);
5572
0
            if (nDstYOff == nDstHeight)
5573
0
                continue;
5574
0
            int nDstYOff2 = static_cast<int>(
5575
0
                0.5 + (nChunkYOff + nFullResYChunk) / dfYRatioDstToSrc);
5576
5577
0
            if (nChunkYOff + nFullResYChunk == nHeight)
5578
0
                nDstYOff2 = nDstHeight;
5579
#if DEBUG_VERBOSE
5580
            CPLDebug("GDAL",
5581
                     "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)", 0,
5582
                     nChunkYOffQueried, nWidth, nChunkYSizeQueried, 0, nDstYOff,
5583
                     nDstWidth, nDstYOff2 - nDstYOff);
5584
#endif
5585
5586
0
            auto poJob = std::make_unique<OvrJob>();
5587
0
            poJob->pfnResampleFn = pfnResampleFn;
5588
0
            poJob->bUseGenericResampleFn = bUseGenericResampleFn;
5589
0
            poJob->args.eOvrDataType = poDstBand->GetRasterDataType();
5590
0
            poJob->args.nOvrXSize = poDstBand->GetXSize();
5591
0
            poJob->args.nOvrYSize = poDstBand->GetYSize();
5592
0
            const char *pszNBITS = poDstBand->GetMetadataItem(
5593
0
                GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
5594
0
            poJob->args.nOvrNBITS = pszNBITS ? atoi(pszNBITS) : 0;
5595
0
            poJob->args.dfXRatioDstToSrc = dfXRatioDstToSrc;
5596
0
            poJob->args.dfYRatioDstToSrc = dfYRatioDstToSrc;
5597
0
            poJob->args.eWrkDataType = eWrkDataType;
5598
0
            poJob->pChunk = pChunkRaw;
5599
0
            poJob->args.pabyChunkNodataMask = pabyChunkNodataMaskRaw;
5600
0
            poJob->nSrcWidth = nWidth;
5601
0
            poJob->nSrcHeight = nHeight;
5602
0
            poJob->args.nChunkXOff = 0;
5603
0
            poJob->args.nChunkXSize = nWidth;
5604
0
            poJob->args.nChunkYOff = nChunkYOffQueried;
5605
0
            poJob->args.nChunkYSize = nChunkYSizeQueried;
5606
0
            poJob->nDstWidth = nDstWidth;
5607
0
            poJob->args.nDstXOff = 0;
5608
0
            poJob->args.nDstXOff2 = nDstWidth;
5609
0
            poJob->args.nDstYOff = nDstYOff;
5610
0
            poJob->args.nDstYOff2 = nDstYOff2;
5611
0
            poJob->poDstBand = poDstBand;
5612
0
            poJob->args.pszResampling = pszResampling;
5613
0
            poJob->args.bHasNoData = bHasNoData;
5614
0
            poJob->args.dfNoDataValue = dfNoDataValue;
5615
0
            poJob->args.poColorTable = poColorTable;
5616
0
            poJob->args.eSrcDataType = eSrcDataType;
5617
0
            poJob->args.bPropagateNoData = bPropagateNoData;
5618
5619
0
            if (poJobQueue)
5620
0
            {
5621
0
                poJob->SetSrcMaskBufferHolder(oSrcMaskBufferHolder);
5622
0
                poJob->SetSrcBufferHolder(oSrcBufferHolder);
5623
0
                poJobQueue->SubmitJob(JobResampleFunc, poJob.get());
5624
0
                jobList.emplace_back(std::move(poJob));
5625
0
            }
5626
0
            else
5627
0
            {
5628
0
                JobResampleFunc(poJob.get());
5629
0
                eErr = poJob->eErr;
5630
0
                if (eErr == CE_None)
5631
0
                {
5632
0
                    eErr = WriteJobData(poJob.get());
5633
0
                }
5634
0
            }
5635
0
        }
5636
0
    }
5637
5638
0
    VSIFree(pChunk);
5639
0
    VSIFree(pabyChunkNodataMask);
5640
5641
    // Wait for all pending jobs to complete
5642
0
    while (!jobList.empty())
5643
0
    {
5644
0
        const auto l_eErr = WaitAndFinalizeOldestJob(jobList);
5645
0
        if (l_eErr != CE_None && eErr == CE_None)
5646
0
            eErr = l_eErr;
5647
0
    }
5648
5649
    /* -------------------------------------------------------------------- */
5650
    /*      Renormalized overview mean / stddev if needed.                  */
5651
    /* -------------------------------------------------------------------- */
5652
0
    if (eErr == CE_None && EQUAL(pszResampling, "AVERAGE_MP"))
5653
0
    {
5654
0
        GDALOverviewMagnitudeCorrection(
5655
0
            poSrcBand, nOverviewCount,
5656
0
            reinterpret_cast<GDALRasterBandH *>(papoOvrBands),
5657
0
            GDALDummyProgress, nullptr);
5658
0
    }
5659
5660
    /* -------------------------------------------------------------------- */
5661
    /*      It can be important to flush out data to overviews.             */
5662
    /* -------------------------------------------------------------------- */
5663
0
    for (int iOverview = 0; eErr == CE_None && iOverview < nOverviewCount;
5664
0
         ++iOverview)
5665
0
    {
5666
0
        eErr = papoOvrBands[iOverview]->FlushCache(false);
5667
0
    }
5668
5669
0
    if (eErr == CE_None)
5670
0
        pfnProgress(1.0, nullptr, pProgressData);
5671
5672
0
    return eErr;
5673
0
}
5674
5675
/************************************************************************/
5676
/*                  GDALRegenerateOverviewsMultiBand()                  */
5677
/************************************************************************/
5678
5679
/**
5680
 * \brief Variant of GDALRegenerateOverviews, specially dedicated for generating
5681
 * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
5682
 *
5683
 * This function will generate one or more overview images from a base
5684
 * image using the requested downsampling algorithm.  Its primary use
5685
 * is for generating overviews via GDALDataset::BuildOverviews(), but it
5686
 * can also be used to generate downsampled images in one file from another
5687
 * outside the overview architecture.
5688
 *
5689
 * The output bands need to exist in advance and share the same characteristics
5690
 * (type, dimensions)
5691
 *
5692
 * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE",
5693
 * "RMS", "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" and "BILINEAR"
5694
 *
5695
 * It does not support color tables or complex data types.
5696
 *
5697
 * The pseudo-algorithm used by the function is :
5698
 *    for each overview
5699
 *       iterate on lines of the source by a step of deltay
5700
 *           iterate on columns of the source  by a step of deltax
5701
 *               read the source data of size deltax * deltay for all the bands
5702
 *               generate the corresponding overview block for all the bands
5703
 *
5704
 * This function will honour properly NODATA_VALUES tuples (special dataset
5705
 * metadata) so that only a given RGB triplet (in case of a RGB image) will be
5706
 * considered as the nodata value and not each value of the triplet
5707
 * independently per band.
5708
 *
5709
 * Starting with GDAL 3.2, the GDAL_NUM_THREADS configuration option can be set
5710
 * to "ALL_CPUS" or a integer value to specify the number of threads to use for
5711
 * overview computation.
5712
 *
5713
 * @param nBands the number of bands, size of papoSrcBands and size of
5714
 *               first dimension of papapoOverviewBands
5715
 * @param papoSrcBands the list of source bands to downsample
5716
 * @param nOverviews the number of downsampled overview levels being generated.
5717
 * @param papapoOverviewBands bidimension array of bands. First dimension is
5718
 *                            indexed by nBands. Second dimension is indexed by
5719
 *                            nOverviews.
5720
 * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE", "RMS",
5721
 * "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" or "BILINEAR").
5722
 * @param pfnProgress progress report function.
5723
 * @param pProgressData progress function callback data.
5724
 * @param papszOptions (GDAL >= 3.6) NULL terminated list of options as
5725
 *                     key=value pairs, or NULL
5726
 *                     Starting with GDAL 3.8, the XOFF, YOFF, XSIZE and YSIZE
5727
 *                     options can be specified to express that overviews should
5728
 *                     be regenerated only in the specified subset of the source
5729
 *                     dataset.
5730
 * @return CE_None on success or CE_Failure on failure.
5731
 */
5732
5733
CPLErr GDALRegenerateOverviewsMultiBand(
5734
    int nBands, GDALRasterBand *const *papoSrcBands, int nOverviews,
5735
    GDALRasterBand *const *const *papapoOverviewBands,
5736
    const char *pszResampling, GDALProgressFunc pfnProgress,
5737
    void *pProgressData, CSLConstList papszOptions)
5738
0
{
5739
0
    CPL_IGNORE_RET_VAL(papszOptions);
5740
5741
0
    if (pfnProgress == nullptr)
5742
0
        pfnProgress = GDALDummyProgress;
5743
5744
0
    if (EQUAL(pszResampling, "NONE") || nBands == 0 || nOverviews == 0)
5745
0
        return CE_None;
5746
5747
    // Sanity checks.
5748
0
    if (!STARTS_WITH_CI(pszResampling, "NEAR") &&
5749
0
        !EQUAL(pszResampling, "RMS") && !EQUAL(pszResampling, "AVERAGE") &&
5750
0
        !EQUAL(pszResampling, "GAUSS") && !EQUAL(pszResampling, "CUBIC") &&
5751
0
        !EQUAL(pszResampling, "CUBICSPLINE") &&
5752
0
        !EQUAL(pszResampling, "LANCZOS") && !EQUAL(pszResampling, "BILINEAR") &&
5753
0
        !EQUAL(pszResampling, "MODE"))
5754
0
    {
5755
0
        CPLError(CE_Failure, CPLE_NotSupported,
5756
0
                 "GDALRegenerateOverviewsMultiBand: pszResampling='%s' "
5757
0
                 "not supported",
5758
0
                 pszResampling);
5759
0
        return CE_Failure;
5760
0
    }
5761
5762
0
    int nKernelRadius = 0;
5763
0
    GDALResampleFunction pfnResampleFn =
5764
0
        GDALGetResampleFunction(pszResampling, &nKernelRadius);
5765
0
    if (pfnResampleFn == nullptr)
5766
0
        return CE_Failure;
5767
5768
0
    const int nToplevelSrcWidth = papoSrcBands[0]->GetXSize();
5769
0
    const int nToplevelSrcHeight = papoSrcBands[0]->GetYSize();
5770
0
    if (nToplevelSrcWidth <= 0 || nToplevelSrcHeight <= 0)
5771
0
        return CE_None;
5772
0
    GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType();
5773
0
    for (int iBand = 1; iBand < nBands; ++iBand)
5774
0
    {
5775
0
        if (papoSrcBands[iBand]->GetXSize() != nToplevelSrcWidth ||
5776
0
            papoSrcBands[iBand]->GetYSize() != nToplevelSrcHeight)
5777
0
        {
5778
0
            CPLError(
5779
0
                CE_Failure, CPLE_NotSupported,
5780
0
                "GDALRegenerateOverviewsMultiBand: all the source bands must "
5781
0
                "have the same dimensions");
5782
0
            return CE_Failure;
5783
0
        }
5784
0
        if (papoSrcBands[iBand]->GetRasterDataType() != eDataType)
5785
0
        {
5786
0
            CPLError(
5787
0
                CE_Failure, CPLE_NotSupported,
5788
0
                "GDALRegenerateOverviewsMultiBand: all the source bands must "
5789
0
                "have the same data type");
5790
0
            return CE_Failure;
5791
0
        }
5792
0
    }
5793
5794
0
    for (int iOverview = 0; iOverview < nOverviews; ++iOverview)
5795
0
    {
5796
0
        const auto poOvrFirstBand = papapoOverviewBands[0][iOverview];
5797
0
        const int nDstWidth = poOvrFirstBand->GetXSize();
5798
0
        const int nDstHeight = poOvrFirstBand->GetYSize();
5799
0
        for (int iBand = 1; iBand < nBands; ++iBand)
5800
0
        {
5801
0
            const auto poOvrBand = papapoOverviewBands[iBand][iOverview];
5802
0
            if (poOvrBand->GetXSize() != nDstWidth ||
5803
0
                poOvrBand->GetYSize() != nDstHeight)
5804
0
            {
5805
0
                CPLError(
5806
0
                    CE_Failure, CPLE_NotSupported,
5807
0
                    "GDALRegenerateOverviewsMultiBand: all the overviews bands "
5808
0
                    "of the same level must have the same dimensions");
5809
0
                return CE_Failure;
5810
0
            }
5811
0
            if (poOvrBand->GetRasterDataType() != eDataType)
5812
0
            {
5813
0
                CPLError(
5814
0
                    CE_Failure, CPLE_NotSupported,
5815
0
                    "GDALRegenerateOverviewsMultiBand: all the overviews bands "
5816
0
                    "must have the same data type as the source bands");
5817
0
                return CE_Failure;
5818
0
            }
5819
0
        }
5820
0
    }
5821
5822
    // First pass to compute the total number of pixels to write.
5823
0
    double dfTotalPixelCount = 0;
5824
0
    const int nSrcXOff = atoi(CSLFetchNameValueDef(papszOptions, "XOFF", "0"));
5825
0
    const int nSrcYOff = atoi(CSLFetchNameValueDef(papszOptions, "YOFF", "0"));
5826
0
    const int nSrcXSize = atoi(CSLFetchNameValueDef(
5827
0
        papszOptions, "XSIZE", CPLSPrintf("%d", nToplevelSrcWidth)));
5828
0
    const int nSrcYSize = atoi(CSLFetchNameValueDef(
5829
0
        papszOptions, "YSIZE", CPLSPrintf("%d", nToplevelSrcHeight)));
5830
0
    for (int iOverview = 0; iOverview < nOverviews; ++iOverview)
5831
0
    {
5832
0
        dfTotalPixelCount +=
5833
0
            static_cast<double>(nSrcXSize) / nToplevelSrcWidth *
5834
0
            papapoOverviewBands[0][iOverview]->GetXSize() *
5835
0
            static_cast<double>(nSrcYSize) / nToplevelSrcHeight *
5836
0
            papapoOverviewBands[0][iOverview]->GetYSize();
5837
0
    }
5838
5839
0
    const GDALDataType eWrkDataType =
5840
0
        GDALGetOvrWorkDataType(pszResampling, eDataType);
5841
0
    const int nWrkDataTypeSize =
5842
0
        std::max(1, GDALGetDataTypeSizeBytes(eWrkDataType));
5843
5844
0
    const bool bIsMask = papoSrcBands[0]->IsMaskBand();
5845
5846
    // If we have a nodata mask and we are doing something more complicated
5847
    // than nearest neighbouring, we have to fetch to nodata mask.
5848
0
    const bool bUseNoDataMask =
5849
0
        !STARTS_WITH_CI(pszResampling, "NEAR") &&
5850
0
        (bIsMask || (papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0);
5851
5852
0
    std::vector<bool> abHasNoData(nBands);
5853
0
    std::vector<double> adfNoDataValue(nBands);
5854
5855
0
    for (int iBand = 0; iBand < nBands; ++iBand)
5856
0
    {
5857
0
        int nHasNoData = 0;
5858
0
        adfNoDataValue[iBand] =
5859
0
            papoSrcBands[iBand]->GetNoDataValue(&nHasNoData);
5860
0
        abHasNoData[iBand] = CPL_TO_BOOL(nHasNoData);
5861
0
    }
5862
5863
0
    std::string osDetailMessage;
5864
0
    if (bUseNoDataMask &&
5865
0
        papoSrcBands[0]->HasConflictingMaskSources(&osDetailMessage, false))
5866
0
    {
5867
0
        CPLError(CE_Warning, CPLE_AppDefined, "%s%s", osDetailMessage.c_str(),
5868
0
                 abHasNoData[0]
5869
0
                     ? "Only the nodata value will be taken into account."
5870
0
                     : "Only the first listed one will be taken into account.");
5871
0
    }
5872
5873
0
    const bool bPropagateNoData =
5874
0
        CPLTestBool(CPLGetConfigOption("GDAL_OVR_PROPAGATE_NODATA", "NO"));
5875
5876
0
    const int nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
5877
0
                                           /* bDefaultToAllCPUs=*/false);
5878
0
    auto poThreadPool =
5879
0
        nThreads > 1 ? GDALGetGlobalThreadPool(nThreads) : nullptr;
5880
0
    auto poJobQueue = poThreadPool ? poThreadPool->CreateJobQueue()
5881
0
                                   : std::unique_ptr<CPLJobQueue>(nullptr);
5882
5883
    // Only configurable for debug / testing
5884
0
    const GIntBig nChunkMaxSize = []() -> GIntBig
5885
0
    {
5886
0
        const char *pszVal =
5887
0
            CPLGetConfigOption("GDAL_OVR_CHUNK_MAX_SIZE", nullptr);
5888
0
        if (pszVal)
5889
0
        {
5890
0
            GIntBig nRet = 0;
5891
0
            CPLParseMemorySize(pszVal, &nRet, nullptr);
5892
0
            return std::max<GIntBig>(100, nRet);
5893
0
        }
5894
0
        return 10 * 1024 * 1024;
5895
0
    }();
5896
5897
    // Only configurable for debug / testing
5898
0
    const GIntBig nChunkMaxSizeForTempFile = []() -> GIntBig
5899
0
    {
5900
0
        const char *pszVal = CPLGetConfigOption(
5901
0
            "GDAL_OVR_CHUNK_MAX_SIZE_FOR_TEMP_FILE", nullptr);
5902
0
        if (pszVal)
5903
0
        {
5904
0
            GIntBig nRet = 0;
5905
0
            CPLParseMemorySize(pszVal, &nRet, nullptr);
5906
0
            return std::max<GIntBig>(100, nRet);
5907
0
        }
5908
0
        const auto nUsableRAM = CPLGetUsablePhysicalRAM();
5909
0
        if (nUsableRAM > 0)
5910
0
            return nUsableRAM / 10;
5911
        // Select a value to be able to at least downsample by 2 for a RGB
5912
        // 1024x1024 tiled output: (2 * 1024 + 2) * (2 * 1024 + 2) * 3 = 12 MB
5913
0
        return 100 * 1024 * 1024;
5914
0
    }();
5915
5916
    // Second pass to do the real job.
5917
0
    double dfCurPixelCount = 0;
5918
0
    CPLErr eErr = CE_None;
5919
0
    for (int iOverview = 0; iOverview < nOverviews && eErr == CE_None;
5920
0
         ++iOverview)
5921
0
    {
5922
0
        int iSrcOverview = -1;  // -1 means the source bands.
5923
5924
0
        const int nDstTotalWidth =
5925
0
            papapoOverviewBands[0][iOverview]->GetXSize();
5926
0
        const int nDstTotalHeight =
5927
0
            papapoOverviewBands[0][iOverview]->GetYSize();
5928
5929
        // Compute the coordinates of the target region to refresh
5930
0
        constexpr double EPS = 1e-8;
5931
0
        const int nDstXOffStart = static_cast<int>(
5932
0
            static_cast<double>(nSrcXOff) / nToplevelSrcWidth * nDstTotalWidth +
5933
0
            EPS);
5934
0
        const int nDstXOffEnd =
5935
0
            std::min(static_cast<int>(
5936
0
                         std::ceil(static_cast<double>(nSrcXOff + nSrcXSize) /
5937
0
                                       nToplevelSrcWidth * nDstTotalWidth -
5938
0
                                   EPS)),
5939
0
                     nDstTotalWidth);
5940
0
        const int nDstWidth = nDstXOffEnd - nDstXOffStart;
5941
0
        const int nDstYOffStart =
5942
0
            static_cast<int>(static_cast<double>(nSrcYOff) /
5943
0
                                 nToplevelSrcHeight * nDstTotalHeight +
5944
0
                             EPS);
5945
0
        const int nDstYOffEnd =
5946
0
            std::min(static_cast<int>(
5947
0
                         std::ceil(static_cast<double>(nSrcYOff + nSrcYSize) /
5948
0
                                       nToplevelSrcHeight * nDstTotalHeight -
5949
0
                                   EPS)),
5950
0
                     nDstTotalHeight);
5951
0
        const int nDstHeight = nDstYOffEnd - nDstYOffStart;
5952
5953
        // Try to use previous level of overview as the source to compute
5954
        // the next level.
5955
0
        int nSrcWidth = nToplevelSrcWidth;
5956
0
        int nSrcHeight = nToplevelSrcHeight;
5957
0
        if (iOverview > 0 &&
5958
0
            papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstTotalWidth)
5959
0
        {
5960
0
            nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
5961
0
            nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
5962
0
            iSrcOverview = iOverview - 1;
5963
0
        }
5964
5965
0
        const double dfXRatioDstToSrc =
5966
0
            static_cast<double>(nSrcWidth) / nDstTotalWidth;
5967
0
        const double dfYRatioDstToSrc =
5968
0
            static_cast<double>(nSrcHeight) / nDstTotalHeight;
5969
5970
0
        const int nOvrFactor =
5971
0
            std::max(1, std::max(static_cast<int>(0.5 + dfXRatioDstToSrc),
5972
0
                                 static_cast<int>(0.5 + dfYRatioDstToSrc)));
5973
5974
0
        int nDstChunkXSize = 0;
5975
0
        int nDstChunkYSize = 0;
5976
0
        papapoOverviewBands[0][iOverview]->GetBlockSize(&nDstChunkXSize,
5977
0
                                                        &nDstChunkYSize);
5978
5979
0
        constexpr int PIXEL_MARGIN = 2;
5980
        // Try to extend the chunk size so that the memory needed to acquire
5981
        // source pixels goes up to 10 MB.
5982
        // This can help for drivers that support multi-threaded reading
5983
0
        const int nFullResYChunk = static_cast<int>(std::min<double>(
5984
0
            nSrcHeight, PIXEL_MARGIN + nDstChunkYSize * dfYRatioDstToSrc));
5985
0
        const int nFullResYChunkQueried = static_cast<int>(std::min<int64_t>(
5986
0
            nSrcHeight,
5987
0
            nFullResYChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
5988
0
                                 nKernelRadius * nOvrFactor));
5989
0
        while (nDstChunkXSize < nDstWidth)
5990
0
        {
5991
0
            constexpr int INCREASE_FACTOR = 2;
5992
5993
0
            const int nFullResXChunk = static_cast<int>(std::min<double>(
5994
0
                nSrcWidth, PIXEL_MARGIN + INCREASE_FACTOR * nDstChunkXSize *
5995
0
                                              dfXRatioDstToSrc));
5996
5997
0
            const int nFullResXChunkQueried =
5998
0
                static_cast<int>(std::min<int64_t>(
5999
0
                    nSrcWidth,
6000
0
                    nFullResXChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
6001
0
                                         nKernelRadius * nOvrFactor));
6002
6003
0
            if (nBands > nChunkMaxSize / nFullResXChunkQueried /
6004
0
                             nFullResYChunkQueried / nWrkDataTypeSize)
6005
0
            {
6006
0
                break;
6007
0
            }
6008
6009
0
            nDstChunkXSize *= INCREASE_FACTOR;
6010
0
        }
6011
0
        nDstChunkXSize = std::min(nDstChunkXSize, nDstWidth);
6012
6013
0
        const int nFullResXChunk = static_cast<int>(std::min<double>(
6014
0
            nSrcWidth, PIXEL_MARGIN + nDstChunkXSize * dfXRatioDstToSrc));
6015
0
        const int nFullResXChunkQueried = static_cast<int>(std::min<int64_t>(
6016
0
            nSrcWidth,
6017
0
            nFullResXChunk + static_cast<int64_t>(RADIUS_TO_DIAMETER) *
6018
0
                                 nKernelRadius * nOvrFactor));
6019
6020
        // Make sure that the RAM requirements to acquire the source data does
6021
        // not exceed nChunkMaxSizeForTempFile
6022
        // If so, reduce the destination chunk size, generate overviews in a
6023
        // temporary dataset, and copy that temporary dataset over the target
6024
        // overview bands (to avoid issues with lossy compression)
6025
0
        const bool bOverflowFullResXChunkYChunkQueried =
6026
0
            nBands > std::numeric_limits<int64_t>::max() /
6027
0
                         nFullResXChunkQueried / nFullResYChunkQueried /
6028
0
                         nWrkDataTypeSize;
6029
6030
0
        const auto nMemRequirement =
6031
0
            bOverflowFullResXChunkYChunkQueried
6032
0
                ? 0
6033
0
                : static_cast<GIntBig>(nFullResXChunkQueried) *
6034
0
                      nFullResYChunkQueried * nBands * nWrkDataTypeSize;
6035
        // Use a temporary dataset with a smaller destination chunk size
6036
0
        const auto nOverShootFactor =
6037
0
            nMemRequirement / nChunkMaxSizeForTempFile;
6038
6039
0
        constexpr int MIN_OVERSHOOT_FACTOR = 4;
6040
0
        const auto nSqrtOverShootFactor = std::max<GIntBig>(
6041
0
            MIN_OVERSHOOT_FACTOR, static_cast<GIntBig>(std::ceil(std::sqrt(
6042
0
                                      static_cast<double>(nOverShootFactor)))));
6043
0
        constexpr int DEFAULT_CHUNK_SIZE = 256;
6044
0
        constexpr int GTIFF_BLOCK_SIZE_MULTIPLE = 16;
6045
0
        const int nReducedDstChunkXSize =
6046
0
            bOverflowFullResXChunkYChunkQueried
6047
0
                ? DEFAULT_CHUNK_SIZE
6048
0
                : std::max(1, static_cast<int>(nDstChunkXSize /
6049
0
                                               nSqrtOverShootFactor) &
6050
0
                                  ~(GTIFF_BLOCK_SIZE_MULTIPLE - 1));
6051
0
        const int nReducedDstChunkYSize =
6052
0
            bOverflowFullResXChunkYChunkQueried
6053
0
                ? DEFAULT_CHUNK_SIZE
6054
0
                : std::max(1, static_cast<int>(nDstChunkYSize /
6055
0
                                               nSqrtOverShootFactor) &
6056
0
                                  ~(GTIFF_BLOCK_SIZE_MULTIPLE - 1));
6057
6058
0
        if (bOverflowFullResXChunkYChunkQueried ||
6059
0
            nMemRequirement > nChunkMaxSizeForTempFile)
6060
0
        {
6061
0
            const auto nDTSize =
6062
0
                std::max(1, GDALGetDataTypeSizeBytes(eDataType));
6063
0
            const bool bTmpDSMemRequirementOverflow =
6064
0
                nBands > std::numeric_limits<int64_t>::max() / nDstWidth /
6065
0
                             nDstHeight / nDTSize;
6066
0
            const auto nTmpDSMemRequirement =
6067
0
                bTmpDSMemRequirementOverflow
6068
0
                    ? 0
6069
0
                    : static_cast<GIntBig>(nDstWidth) * nDstHeight * nBands *
6070
0
                          nDTSize;
6071
6072
            // make sure that one band buffer doesn't overflow size_t
6073
0
            const bool bChunkSizeOverflow =
6074
0
                static_cast<size_t>(nDTSize) >
6075
0
                std::numeric_limits<size_t>::max() / nDstWidth / nDstHeight;
6076
0
            const size_t nChunkSize =
6077
0
                bChunkSizeOverflow
6078
0
                    ? 0
6079
0
                    : static_cast<size_t>(nDstWidth) * nDstHeight * nDTSize;
6080
6081
0
            const auto CreateVRT =
6082
0
                [nBands, nSrcWidth, nSrcHeight, nDstTotalWidth, nDstTotalHeight,
6083
0
                 pszResampling, eWrkDataType, papoSrcBands, papapoOverviewBands,
6084
0
                 iSrcOverview, &abHasNoData,
6085
0
                 &adfNoDataValue](int nVRTBlockXSize, int nVRTBlockYSize)
6086
0
            {
6087
0
                auto poVRTDS = std::make_unique<VRTDataset>(
6088
0
                    nDstTotalWidth, nDstTotalHeight, nVRTBlockXSize,
6089
0
                    nVRTBlockYSize);
6090
6091
0
                for (int iBand = 0; iBand < nBands; ++iBand)
6092
0
                {
6093
0
                    auto poVRTSrc = std::make_unique<VRTSimpleSource>();
6094
0
                    poVRTSrc->SetResampling(pszResampling);
6095
0
                    poVRTDS->AddBand(eWrkDataType);
6096
0
                    auto poVRTBand = static_cast<VRTSourcedRasterBand *>(
6097
0
                        poVRTDS->GetRasterBand(iBand + 1));
6098
6099
0
                    auto poSrcBand = papoSrcBands[iBand];
6100
0
                    if (iSrcOverview != -1)
6101
0
                        poSrcBand = papapoOverviewBands[iBand][iSrcOverview];
6102
0
                    poVRTBand->ConfigureSource(
6103
0
                        poVRTSrc.get(), poSrcBand, false, 0, 0, nSrcWidth,
6104
0
                        nSrcHeight, 0, 0, nDstTotalWidth, nDstTotalHeight);
6105
                    // Add the source to the band
6106
0
                    poVRTBand->AddSource(poVRTSrc.release());
6107
0
                    if (abHasNoData[iBand])
6108
0
                        poVRTBand->SetNoDataValue(adfNoDataValue[iBand]);
6109
0
                }
6110
6111
0
                if (papoSrcBands[0]->GetMaskFlags() == GMF_PER_DATASET &&
6112
0
                    poVRTDS->CreateMaskBand(GMF_PER_DATASET) == CE_None)
6113
0
                {
6114
0
                    VRTSourcedRasterBand *poMaskVRTBand =
6115
0
                        cpl::down_cast<VRTSourcedRasterBand *>(
6116
0
                            poVRTDS->GetRasterBand(1)->GetMaskBand());
6117
0
                    auto poSrcBand = papoSrcBands[0];
6118
0
                    if (iSrcOverview != -1)
6119
0
                        poSrcBand = papapoOverviewBands[0][iSrcOverview];
6120
0
                    poMaskVRTBand->AddMaskBandSource(
6121
0
                        poSrcBand->GetMaskBand(), 0, 0, nSrcWidth, nSrcHeight,
6122
0
                        0, 0, nDstTotalWidth, nDstTotalHeight);
6123
0
                }
6124
6125
0
                return poVRTDS;
6126
0
            };
6127
6128
            // If the overview accommodates chunking, do so and recurse
6129
            // to avoid generating full size temporary files
6130
0
            if (!bOverflowFullResXChunkYChunkQueried &&
6131
0
                !bTmpDSMemRequirementOverflow && !bChunkSizeOverflow &&
6132
0
                (nDstChunkXSize < nDstWidth || nDstChunkYSize < nDstHeight))
6133
0
            {
6134
                // Create a VRT with the smaller chunk to do the scaling
6135
0
                auto poVRTDS =
6136
0
                    CreateVRT(nReducedDstChunkXSize, nReducedDstChunkYSize);
6137
6138
0
                std::vector<GDALRasterBand *> apoVRTBand(nBands);
6139
0
                std::vector<GDALRasterBand *> apoDstBand(nBands);
6140
0
                for (int iBand = 0; iBand < nBands; ++iBand)
6141
0
                {
6142
0
                    apoDstBand[iBand] = papapoOverviewBands[iBand][iOverview];
6143
0
                    apoVRTBand[iBand] = poVRTDS->GetRasterBand(iBand + 1);
6144
0
                }
6145
6146
                // Use a flag to avoid reading from the overview being built
6147
0
                GDALRasterIOExtraArg sExtraArg;
6148
0
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6149
0
                if (iSrcOverview == -1)
6150
0
                    sExtraArg.bUseOnlyThisScale = true;
6151
6152
                // A single band buffer for data transfer to the overview
6153
0
                std::vector<GByte> abyChunk;
6154
0
                try
6155
0
                {
6156
0
                    abyChunk.resize(nChunkSize);
6157
0
                }
6158
0
                catch (const std::exception &)
6159
0
                {
6160
0
                    CPLError(CE_Failure, CPLE_OutOfMemory,
6161
0
                             "Out of memory allocating temporary buffer");
6162
0
                    return CE_Failure;
6163
0
                }
6164
6165
                // Loop over output height, in chunks
6166
0
                for (int nDstYOff = nDstYOffStart;
6167
0
                     nDstYOff < nDstYOffEnd && eErr == CE_None;
6168
0
                     /* */)
6169
0
                {
6170
0
                    const int nDstYCount =
6171
0
                        std::min(nDstChunkYSize, nDstYOffEnd - nDstYOff);
6172
                    // Loop over output width, in output chunks
6173
0
                    for (int nDstXOff = nDstXOffStart;
6174
0
                         nDstXOff < nDstXOffEnd && eErr == CE_None;
6175
0
                         /* */)
6176
0
                    {
6177
0
                        const int nDstXCount =
6178
0
                            std::min(nDstChunkXSize, nDstXOffEnd - nDstXOff);
6179
                        // Read and transfer the chunk to the overview
6180
0
                        for (int iBand = 0; iBand < nBands && eErr == CE_None;
6181
0
                             ++iBand)
6182
0
                        {
6183
0
                            eErr = apoVRTBand[iBand]->RasterIO(
6184
0
                                GF_Read, nDstXOff, nDstYOff, nDstXCount,
6185
0
                                nDstYCount, abyChunk.data(), nDstXCount,
6186
0
                                nDstYCount, eDataType, 0, 0, &sExtraArg);
6187
0
                            if (eErr == CE_None)
6188
0
                            {
6189
0
                                eErr = apoDstBand[iBand]->RasterIO(
6190
0
                                    GF_Write, nDstXOff, nDstYOff, nDstXCount,
6191
0
                                    nDstYCount, abyChunk.data(), nDstXCount,
6192
0
                                    nDstYCount, eDataType, 0, 0, nullptr);
6193
0
                            }
6194
0
                        }
6195
6196
0
                        dfCurPixelCount +=
6197
0
                            static_cast<double>(nDstXCount) * nDstYCount;
6198
6199
0
                        nDstXOff += nDstXCount;
6200
0
                    }  // width
6201
6202
0
                    if (!pfnProgress(dfCurPixelCount / dfTotalPixelCount,
6203
0
                                     nullptr, pProgressData))
6204
0
                    {
6205
0
                        CPLError(CE_Failure, CPLE_UserInterrupt,
6206
0
                                 "User terminated");
6207
0
                        eErr = CE_Failure;
6208
0
                    }
6209
6210
0
                    nDstYOff += nDstYCount;
6211
0
                }  // height
6212
6213
0
                if (CE_None != eErr)
6214
0
                {
6215
0
                    CPLError(CE_Failure, CPLE_AppDefined,
6216
0
                             "Error while writing overview");
6217
0
                    return CE_Failure;
6218
0
                }
6219
6220
0
                pfnProgress(1.0, nullptr, pProgressData);
6221
                // Flush the overviews we just generated
6222
0
                for (int iBand = 0; iBand < nBands; ++iBand)
6223
0
                    apoDstBand[iBand]->FlushCache(false);
6224
6225
0
                continue;  // Next overview
6226
0
            }  // chunking via temporary dataset
6227
6228
0
            std::unique_ptr<GDALDataset> poTmpDS;
6229
            // Config option mostly/only for autotest purposes
6230
0
            const char *pszGDAL_OVR_TEMP_DRIVER =
6231
0
                CPLGetConfigOption("GDAL_OVR_TEMP_DRIVER", "");
6232
0
            if ((!bTmpDSMemRequirementOverflow &&
6233
0
                 nTmpDSMemRequirement <= nChunkMaxSizeForTempFile &&
6234
0
                 !EQUAL(pszGDAL_OVR_TEMP_DRIVER, "GTIFF")) ||
6235
0
                EQUAL(pszGDAL_OVR_TEMP_DRIVER, "MEM"))
6236
0
            {
6237
0
                auto poTmpDrv = GetGDALDriverManager()->GetDriverByName("MEM");
6238
0
                if (!poTmpDrv)
6239
0
                {
6240
0
                    eErr = CE_Failure;
6241
0
                    break;
6242
0
                }
6243
0
                poTmpDS.reset(poTmpDrv->Create("", nDstTotalWidth,
6244
0
                                               nDstTotalHeight, nBands,
6245
0
                                               eDataType, nullptr));
6246
0
            }
6247
0
            else
6248
0
            {
6249
                // Create a temporary file for the overview
6250
0
                auto poTmpDrv =
6251
0
                    GetGDALDriverManager()->GetDriverByName("GTiff");
6252
0
                if (!poTmpDrv)
6253
0
                {
6254
0
                    eErr = CE_Failure;
6255
0
                    break;
6256
0
                }
6257
0
                std::string osTmpFilename;
6258
0
                auto poDstDS = papapoOverviewBands[0][0]->GetDataset();
6259
0
                if (poDstDS)
6260
0
                {
6261
0
                    osTmpFilename = poDstDS->GetDescription();
6262
0
                    VSIStatBufL sStatBuf;
6263
0
                    if (!osTmpFilename.empty() &&
6264
0
                        VSIStatL(osTmpFilename.c_str(), &sStatBuf) == 0)
6265
0
                        osTmpFilename += "_tmp_ovr.tif";
6266
0
                }
6267
0
                if (osTmpFilename.empty())
6268
0
                {
6269
0
                    osTmpFilename = CPLGenerateTempFilenameSafe(nullptr);
6270
0
                    osTmpFilename += ".tif";
6271
0
                }
6272
0
                CPLDebug("GDAL", "Creating temporary file %s of %d x %d x %d",
6273
0
                         osTmpFilename.c_str(), nDstWidth, nDstHeight, nBands);
6274
0
                CPLStringList aosCO;
6275
0
                if (0 == ((nReducedDstChunkXSize % GTIFF_BLOCK_SIZE_MULTIPLE) |
6276
0
                          (nReducedDstChunkYSize % GTIFF_BLOCK_SIZE_MULTIPLE)))
6277
0
                {
6278
0
                    aosCO.SetNameValue("TILED", "YES");
6279
0
                    aosCO.SetNameValue("BLOCKXSIZE",
6280
0
                                       CPLSPrintf("%d", nReducedDstChunkXSize));
6281
0
                    aosCO.SetNameValue("BLOCKYSIZE",
6282
0
                                       CPLSPrintf("%d", nReducedDstChunkYSize));
6283
0
                }
6284
0
                if (const char *pszCOList =
6285
0
                        poTmpDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST))
6286
0
                {
6287
0
                    aosCO.SetNameValue(
6288
0
                        "COMPRESS", strstr(pszCOList, "ZSTD") ? "ZSTD" : "LZW");
6289
0
                }
6290
0
                poTmpDS.reset(poTmpDrv->Create(osTmpFilename.c_str(), nDstWidth,
6291
0
                                               nDstHeight, nBands, eDataType,
6292
0
                                               aosCO.List()));
6293
0
                if (poTmpDS)
6294
0
                {
6295
0
                    poTmpDS->MarkSuppressOnClose();
6296
0
                    VSIUnlink(osTmpFilename.c_str());
6297
0
                }
6298
0
            }
6299
0
            if (!poTmpDS)
6300
0
            {
6301
0
                eErr = CE_Failure;
6302
0
                break;
6303
0
            }
6304
6305
            // Create a full size VRT to do the resampling without edge effects
6306
0
            auto poVRTDS =
6307
0
                CreateVRT(nReducedDstChunkXSize, nReducedDstChunkYSize);
6308
6309
            // Allocate a band buffer with the overview chunk size
6310
0
            std::unique_ptr<void, VSIFreeReleaser> pDstBuffer(
6311
0
                VSI_MALLOC3_VERBOSE(size_t(nWrkDataTypeSize), nDstChunkXSize,
6312
0
                                    nDstChunkYSize));
6313
0
            if (pDstBuffer == nullptr)
6314
0
            {
6315
0
                eErr = CE_Failure;
6316
0
                break;
6317
0
            }
6318
6319
            // Use a flag to avoid reading the overview being built
6320
0
            GDALRasterIOExtraArg sExtraArg;
6321
0
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6322
0
            if (iSrcOverview == -1)
6323
0
                sExtraArg.bUseOnlyThisScale = true;
6324
6325
            // Scale and copy data from the VRT to the temp file
6326
0
            for (int nDstYOff = nDstYOffStart;
6327
0
                 nDstYOff < nDstYOffEnd && eErr == CE_None;
6328
0
                 /* */)
6329
0
            {
6330
0
                const int nDstYCount =
6331
0
                    std::min(nReducedDstChunkYSize, nDstYOffEnd - nDstYOff);
6332
0
                for (int nDstXOff = nDstXOffStart;
6333
0
                     nDstXOff < nDstXOffEnd && eErr == CE_None;
6334
0
                     /* */)
6335
0
                {
6336
0
                    const int nDstXCount =
6337
0
                        std::min(nReducedDstChunkXSize, nDstXOffEnd - nDstXOff);
6338
0
                    for (int iBand = 0; iBand < nBands && eErr == CE_None;
6339
0
                         ++iBand)
6340
0
                    {
6341
0
                        auto poSrcBand = poVRTDS->GetRasterBand(iBand + 1);
6342
0
                        eErr = poSrcBand->RasterIO(
6343
0
                            GF_Read, nDstXOff, nDstYOff, nDstXCount, nDstYCount,
6344
0
                            pDstBuffer.get(), nDstXCount, nDstYCount,
6345
0
                            eWrkDataType, 0, 0, &sExtraArg);
6346
0
                        if (eErr == CE_None)
6347
0
                        {
6348
                            // Write to the temporary dataset, shifted
6349
0
                            auto poOvrBand = poTmpDS->GetRasterBand(iBand + 1);
6350
0
                            eErr = poOvrBand->RasterIO(
6351
0
                                GF_Write, nDstXOff - nDstXOffStart,
6352
0
                                nDstYOff - nDstYOffStart, nDstXCount,
6353
0
                                nDstYCount, pDstBuffer.get(), nDstXCount,
6354
0
                                nDstYCount, eWrkDataType, 0, 0, nullptr);
6355
0
                        }
6356
0
                    }
6357
0
                    nDstXOff += nDstXCount;
6358
0
                }
6359
0
                nDstYOff += nDstYCount;
6360
0
            }
6361
6362
            // Copy from the temporary to the overview
6363
0
            for (int nDstYOff = nDstYOffStart;
6364
0
                 nDstYOff < nDstYOffEnd && eErr == CE_None;
6365
0
                 /* */)
6366
0
            {
6367
0
                const int nDstYCount =
6368
0
                    std::min(nDstChunkYSize, nDstYOffEnd - nDstYOff);
6369
0
                for (int nDstXOff = nDstXOffStart;
6370
0
                     nDstXOff < nDstXOffEnd && eErr == CE_None;
6371
0
                     /* */)
6372
0
                {
6373
0
                    const int nDstXCount =
6374
0
                        std::min(nDstChunkXSize, nDstXOffEnd - nDstXOff);
6375
0
                    for (int iBand = 0; iBand < nBands && eErr == CE_None;
6376
0
                         ++iBand)
6377
0
                    {
6378
0
                        auto poSrcBand = poTmpDS->GetRasterBand(iBand + 1);
6379
0
                        eErr = poSrcBand->RasterIO(
6380
0
                            GF_Read, nDstXOff - nDstXOffStart,
6381
0
                            nDstYOff - nDstYOffStart, nDstXCount, nDstYCount,
6382
0
                            pDstBuffer.get(), nDstXCount, nDstYCount,
6383
0
                            eWrkDataType, 0, 0, nullptr);
6384
0
                        if (eErr == CE_None)
6385
0
                        {
6386
                            // Write to the destination overview bands
6387
0
                            auto poOvrBand =
6388
0
                                papapoOverviewBands[iBand][iOverview];
6389
0
                            eErr = poOvrBand->RasterIO(
6390
0
                                GF_Write, nDstXOff, nDstYOff, nDstXCount,
6391
0
                                nDstYCount, pDstBuffer.get(), nDstXCount,
6392
0
                                nDstYCount, eWrkDataType, 0, 0, nullptr);
6393
0
                        }
6394
0
                    }
6395
0
                    nDstXOff += nDstXCount;
6396
0
                }
6397
0
                nDstYOff += nDstYCount;
6398
0
            }
6399
6400
0
            if (eErr != CE_None)
6401
0
            {
6402
0
                CPLError(CE_Failure, CPLE_AppDefined,
6403
0
                         "Failed to write overview %d", iOverview);
6404
0
                return eErr;
6405
0
            }
6406
6407
            // Flush the data to overviews.
6408
0
            for (int iBand = 0; iBand < nBands; ++iBand)
6409
0
                papapoOverviewBands[iBand][iOverview]->FlushCache(false);
6410
6411
0
            continue;
6412
0
        }
6413
6414
        // Structure describing a resampling job
6415
0
        struct OvrJob
6416
0
        {
6417
            // Buffers to free when job is finished
6418
0
            std::unique_ptr<PointerHolder> oSrcMaskBufferHolder{};
6419
0
            std::unique_ptr<PointerHolder> oSrcBufferHolder{};
6420
0
            std::unique_ptr<PointerHolder> oDstBufferHolder{};
6421
6422
0
            GDALRasterBand *poDstBand = nullptr;
6423
6424
            // Input parameters of pfnResampleFn
6425
0
            GDALResampleFunction pfnResampleFn = nullptr;
6426
0
            GDALOverviewResampleArgs args{};
6427
0
            const void *pChunk = nullptr;
6428
6429
            // Output values of resampling function
6430
0
            CPLErr eErr = CE_Failure;
6431
0
            void *pDstBuffer = nullptr;
6432
0
            GDALDataType eDstBufferDataType = GDT_Unknown;
6433
6434
0
            void NotifyFinished()
6435
0
            {
6436
0
                std::lock_guard guard(mutex);
6437
0
                bFinished = true;
6438
0
                cv.notify_one();
6439
0
            }
6440
6441
0
            bool IsFinished()
6442
0
            {
6443
0
                std::lock_guard guard(mutex);
6444
0
                return bFinished;
6445
0
            }
6446
6447
0
            void WaitFinished()
6448
0
            {
6449
0
                std::unique_lock oGuard(mutex);
6450
0
                while (!bFinished)
6451
0
                {
6452
0
                    cv.wait(oGuard);
6453
0
                }
6454
0
            }
6455
6456
0
          private:
6457
            // Synchronization
6458
0
            bool bFinished = false;
6459
0
            std::mutex mutex{};
6460
0
            std::condition_variable cv{};
6461
0
        };
6462
6463
        // Thread function to resample
6464
0
        const auto JobResampleFunc = [](void *pData)
6465
0
        {
6466
0
            OvrJob *poJob = static_cast<OvrJob *>(pData);
6467
6468
0
            poJob->eErr = poJob->pfnResampleFn(poJob->args, poJob->pChunk,
6469
0
                                               &(poJob->pDstBuffer),
6470
0
                                               &(poJob->eDstBufferDataType));
6471
6472
0
            auto pDstBuffer = poJob->pDstBuffer;
6473
0
            poJob->oDstBufferHolder =
6474
0
                std::make_unique<PointerHolder>(pDstBuffer);
6475
6476
0
            poJob->NotifyFinished();
6477
0
        };
6478
6479
        // Function to write resample data to target band
6480
0
        const auto WriteJobData = [](const OvrJob *poJob)
6481
0
        {
6482
0
            return poJob->poDstBand->RasterIO(
6483
0
                GF_Write, poJob->args.nDstXOff, poJob->args.nDstYOff,
6484
0
                poJob->args.nDstXOff2 - poJob->args.nDstXOff,
6485
0
                poJob->args.nDstYOff2 - poJob->args.nDstYOff, poJob->pDstBuffer,
6486
0
                poJob->args.nDstXOff2 - poJob->args.nDstXOff,
6487
0
                poJob->args.nDstYOff2 - poJob->args.nDstYOff,
6488
0
                poJob->eDstBufferDataType, 0, 0, nullptr);
6489
0
        };
6490
6491
        // Wait for completion of oldest job and serialize it
6492
0
        const auto WaitAndFinalizeOldestJob =
6493
0
            [WriteJobData](std::list<std::unique_ptr<OvrJob>> &jobList)
6494
0
        {
6495
0
            auto poOldestJob = jobList.front().get();
6496
0
            poOldestJob->WaitFinished();
6497
0
            CPLErr l_eErr = poOldestJob->eErr;
6498
0
            if (l_eErr == CE_None)
6499
0
            {
6500
0
                l_eErr = WriteJobData(poOldestJob);
6501
0
            }
6502
6503
0
            jobList.pop_front();
6504
0
            return l_eErr;
6505
0
        };
6506
6507
        // Queue of jobs
6508
0
        std::list<std::unique_ptr<OvrJob>> jobList;
6509
6510
0
        std::vector<std::unique_ptr<void, VSIFreeReleaser>> apaChunk(nBands);
6511
0
        std::vector<std::unique_ptr<GByte, VSIFreeReleaser>>
6512
0
            apabyChunkNoDataMask(nBands);
6513
6514
        // Iterate on destination overview, block by block.
6515
0
        for (int nDstYOff = nDstYOffStart;
6516
0
             nDstYOff < nDstYOffEnd && eErr == CE_None;
6517
0
             nDstYOff += nDstChunkYSize)
6518
0
        {
6519
0
            int nDstYCount;
6520
0
            if (nDstYOff + nDstChunkYSize <= nDstYOffEnd)
6521
0
                nDstYCount = nDstChunkYSize;
6522
0
            else
6523
0
                nDstYCount = nDstYOffEnd - nDstYOff;
6524
6525
0
            int nChunkYOff = static_cast<int>(nDstYOff * dfYRatioDstToSrc);
6526
0
            int nChunkYOff2 = static_cast<int>(
6527
0
                ceil((nDstYOff + nDstYCount) * dfYRatioDstToSrc));
6528
0
            if (nChunkYOff2 > nSrcHeight ||
6529
0
                nDstYOff + nDstYCount == nDstTotalHeight)
6530
0
                nChunkYOff2 = nSrcHeight;
6531
0
            int nYCount = nChunkYOff2 - nChunkYOff;
6532
0
            CPLAssert(nYCount <= nFullResYChunk);
6533
6534
0
            int nChunkYOffQueried = nChunkYOff - nKernelRadius * nOvrFactor;
6535
0
            int nChunkYSizeQueried =
6536
0
                nYCount + RADIUS_TO_DIAMETER * nKernelRadius * nOvrFactor;
6537
0
            if (nChunkYOffQueried < 0)
6538
0
            {
6539
0
                nChunkYSizeQueried += nChunkYOffQueried;
6540
0
                nChunkYOffQueried = 0;
6541
0
            }
6542
0
            if (nChunkYSizeQueried + nChunkYOffQueried > nSrcHeight)
6543
0
                nChunkYSizeQueried = nSrcHeight - nChunkYOffQueried;
6544
0
            CPLAssert(nChunkYSizeQueried <= nFullResYChunkQueried);
6545
6546
0
            if (!pfnProgress(std::min(1.0, dfCurPixelCount / dfTotalPixelCount),
6547
0
                             nullptr, pProgressData))
6548
0
            {
6549
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6550
0
                eErr = CE_Failure;
6551
0
            }
6552
6553
            // Iterate on destination overview, block by block.
6554
0
            for (int nDstXOff = nDstXOffStart;
6555
0
                 nDstXOff < nDstXOffEnd && eErr == CE_None;
6556
0
                 nDstXOff += nDstChunkXSize)
6557
0
            {
6558
0
                int nDstXCount = 0;
6559
0
                if (nDstXOff + nDstChunkXSize <= nDstXOffEnd)
6560
0
                    nDstXCount = nDstChunkXSize;
6561
0
                else
6562
0
                    nDstXCount = nDstXOffEnd - nDstXOff;
6563
6564
0
                dfCurPixelCount += static_cast<double>(nDstXCount) * nDstYCount;
6565
6566
0
                int nChunkXOff = static_cast<int>(nDstXOff * dfXRatioDstToSrc);
6567
0
                int nChunkXOff2 = static_cast<int>(
6568
0
                    ceil((nDstXOff + nDstXCount) * dfXRatioDstToSrc));
6569
0
                if (nChunkXOff2 > nSrcWidth ||
6570
0
                    nDstXOff + nDstXCount == nDstTotalWidth)
6571
0
                    nChunkXOff2 = nSrcWidth;
6572
0
                const int nXCount = nChunkXOff2 - nChunkXOff;
6573
0
                CPLAssert(nXCount <= nFullResXChunk);
6574
6575
0
                int nChunkXOffQueried = nChunkXOff - nKernelRadius * nOvrFactor;
6576
0
                int nChunkXSizeQueried =
6577
0
                    nXCount + RADIUS_TO_DIAMETER * nKernelRadius * nOvrFactor;
6578
0
                if (nChunkXOffQueried < 0)
6579
0
                {
6580
0
                    nChunkXSizeQueried += nChunkXOffQueried;
6581
0
                    nChunkXOffQueried = 0;
6582
0
                }
6583
0
                if (nChunkXSizeQueried + nChunkXOffQueried > nSrcWidth)
6584
0
                    nChunkXSizeQueried = nSrcWidth - nChunkXOffQueried;
6585
0
                CPLAssert(nChunkXSizeQueried <= nFullResXChunkQueried);
6586
#if DEBUG_VERBOSE
6587
                CPLDebug("GDAL",
6588
                         "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)",
6589
                         nChunkXOffQueried, nChunkYOffQueried,
6590
                         nChunkXSizeQueried, nChunkYSizeQueried, nDstXOff,
6591
                         nDstYOff, nDstXCount, nDstYCount);
6592
#endif
6593
6594
                // Avoid accumulating too many tasks and exhaust RAM
6595
6596
                // Try to complete already finished jobs
6597
0
                while (eErr == CE_None && !jobList.empty())
6598
0
                {
6599
0
                    auto poOldestJob = jobList.front().get();
6600
0
                    if (!poOldestJob->IsFinished())
6601
0
                        break;
6602
0
                    eErr = poOldestJob->eErr;
6603
0
                    if (eErr == CE_None)
6604
0
                    {
6605
0
                        eErr = WriteJobData(poOldestJob);
6606
0
                    }
6607
6608
0
                    jobList.pop_front();
6609
0
                }
6610
6611
                // And in case we have saturated the number of threads,
6612
                // wait for completion of tasks to go below the threshold.
6613
0
                while (eErr == CE_None &&
6614
0
                       jobList.size() >= static_cast<size_t>(nThreads))
6615
0
                {
6616
0
                    eErr = WaitAndFinalizeOldestJob(jobList);
6617
0
                }
6618
6619
                // Read the source buffers for all the bands.
6620
0
                for (int iBand = 0; iBand < nBands && eErr == CE_None; ++iBand)
6621
0
                {
6622
                    // (Re)allocate buffers if needed
6623
0
                    if (apaChunk[iBand] == nullptr)
6624
0
                    {
6625
0
                        apaChunk[iBand].reset(VSI_MALLOC3_VERBOSE(
6626
0
                            nFullResXChunkQueried, nFullResYChunkQueried,
6627
0
                            nWrkDataTypeSize));
6628
0
                        if (apaChunk[iBand] == nullptr)
6629
0
                        {
6630
0
                            eErr = CE_Failure;
6631
0
                        }
6632
0
                    }
6633
0
                    if (bUseNoDataMask &&
6634
0
                        apabyChunkNoDataMask[iBand] == nullptr)
6635
0
                    {
6636
0
                        apabyChunkNoDataMask[iBand].reset(
6637
0
                            static_cast<GByte *>(VSI_MALLOC2_VERBOSE(
6638
0
                                nFullResXChunkQueried, nFullResYChunkQueried)));
6639
0
                        if (apabyChunkNoDataMask[iBand] == nullptr)
6640
0
                        {
6641
0
                            eErr = CE_Failure;
6642
0
                        }
6643
0
                    }
6644
6645
0
                    if (eErr == CE_None)
6646
0
                    {
6647
0
                        GDALRasterBand *poSrcBand = nullptr;
6648
0
                        if (iSrcOverview == -1)
6649
0
                            poSrcBand = papoSrcBands[iBand];
6650
0
                        else
6651
0
                            poSrcBand =
6652
0
                                papapoOverviewBands[iBand][iSrcOverview];
6653
0
                        eErr = poSrcBand->RasterIO(
6654
0
                            GF_Read, nChunkXOffQueried, nChunkYOffQueried,
6655
0
                            nChunkXSizeQueried, nChunkYSizeQueried,
6656
0
                            apaChunk[iBand].get(), nChunkXSizeQueried,
6657
0
                            nChunkYSizeQueried, eWrkDataType, 0, 0, nullptr);
6658
6659
0
                        if (bUseNoDataMask && eErr == CE_None)
6660
0
                        {
6661
0
                            auto poMaskBand = poSrcBand->IsMaskBand()
6662
0
                                                  ? poSrcBand
6663
0
                                                  : poSrcBand->GetMaskBand();
6664
0
                            eErr = poMaskBand->RasterIO(
6665
0
                                GF_Read, nChunkXOffQueried, nChunkYOffQueried,
6666
0
                                nChunkXSizeQueried, nChunkYSizeQueried,
6667
0
                                apabyChunkNoDataMask[iBand].get(),
6668
0
                                nChunkXSizeQueried, nChunkYSizeQueried,
6669
0
                                GDT_UInt8, 0, 0, nullptr);
6670
0
                        }
6671
0
                    }
6672
0
                }
6673
6674
                // Compute the resulting overview block.
6675
0
                for (int iBand = 0; iBand < nBands && eErr == CE_None; ++iBand)
6676
0
                {
6677
0
                    auto poJob = std::make_unique<OvrJob>();
6678
0
                    poJob->pfnResampleFn = pfnResampleFn;
6679
0
                    poJob->poDstBand = papapoOverviewBands[iBand][iOverview];
6680
0
                    poJob->args.eOvrDataType =
6681
0
                        poJob->poDstBand->GetRasterDataType();
6682
0
                    poJob->args.nOvrXSize = poJob->poDstBand->GetXSize();
6683
0
                    poJob->args.nOvrYSize = poJob->poDstBand->GetYSize();
6684
0
                    const char *pszNBITS = poJob->poDstBand->GetMetadataItem(
6685
0
                        GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
6686
0
                    poJob->args.nOvrNBITS = pszNBITS ? atoi(pszNBITS) : 0;
6687
0
                    poJob->args.dfXRatioDstToSrc = dfXRatioDstToSrc;
6688
0
                    poJob->args.dfYRatioDstToSrc = dfYRatioDstToSrc;
6689
0
                    poJob->args.eWrkDataType = eWrkDataType;
6690
0
                    poJob->pChunk = apaChunk[iBand].get();
6691
0
                    poJob->args.pabyChunkNodataMask =
6692
0
                        apabyChunkNoDataMask[iBand].get();
6693
0
                    poJob->args.nChunkXOff = nChunkXOffQueried;
6694
0
                    poJob->args.nChunkXSize = nChunkXSizeQueried;
6695
0
                    poJob->args.nChunkYOff = nChunkYOffQueried;
6696
0
                    poJob->args.nChunkYSize = nChunkYSizeQueried;
6697
0
                    poJob->args.nDstXOff = nDstXOff;
6698
0
                    poJob->args.nDstXOff2 = nDstXOff + nDstXCount;
6699
0
                    poJob->args.nDstYOff = nDstYOff;
6700
0
                    poJob->args.nDstYOff2 = nDstYOff + nDstYCount;
6701
0
                    poJob->args.pszResampling = pszResampling;
6702
0
                    poJob->args.bHasNoData = abHasNoData[iBand];
6703
0
                    poJob->args.dfNoDataValue = adfNoDataValue[iBand];
6704
0
                    poJob->args.eSrcDataType = eDataType;
6705
0
                    poJob->args.bPropagateNoData = bPropagateNoData;
6706
6707
0
                    if (poJobQueue)
6708
0
                    {
6709
0
                        poJob->oSrcMaskBufferHolder =
6710
0
                            std::make_unique<PointerHolder>(
6711
0
                                std::move(apabyChunkNoDataMask[iBand]));
6712
6713
0
                        poJob->oSrcBufferHolder =
6714
0
                            std::make_unique<PointerHolder>(
6715
0
                                std::move(apaChunk[iBand]));
6716
6717
0
                        poJobQueue->SubmitJob(JobResampleFunc, poJob.get());
6718
0
                        jobList.emplace_back(std::move(poJob));
6719
0
                    }
6720
0
                    else
6721
0
                    {
6722
0
                        JobResampleFunc(poJob.get());
6723
0
                        eErr = poJob->eErr;
6724
0
                        if (eErr == CE_None)
6725
0
                        {
6726
0
                            eErr = WriteJobData(poJob.get());
6727
0
                        }
6728
0
                    }
6729
0
                }
6730
0
            }
6731
0
        }
6732
6733
        // Wait for all pending jobs to complete
6734
0
        while (!jobList.empty())
6735
0
        {
6736
0
            const auto l_eErr = WaitAndFinalizeOldestJob(jobList);
6737
0
            if (l_eErr != CE_None && eErr == CE_None)
6738
0
                eErr = l_eErr;
6739
0
        }
6740
6741
        // Flush the data to overviews.
6742
0
        for (int iBand = 0; iBand < nBands; ++iBand)
6743
0
        {
6744
0
            if (papapoOverviewBands[iBand][iOverview]->FlushCache(false) !=
6745
0
                CE_None)
6746
0
                eErr = CE_Failure;
6747
0
        }
6748
0
    }
6749
6750
0
    if (eErr == CE_None)
6751
0
        pfnProgress(1.0, nullptr, pProgressData);
6752
6753
0
    return eErr;
6754
0
}
6755
6756
/************************************************************************/
6757
/*                  GDALRegenerateOverviewsMultiBand()                  */
6758
/************************************************************************/
6759
6760
/**
6761
 * \brief Variant of GDALRegenerateOverviews, specially dedicated for generating
6762
 * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
6763
 *
6764
 * This function will generate one or more overview images from a base
6765
 * image using the requested downsampling algorithm.  Its primary use
6766
 * is for generating overviews via GDALDataset::BuildOverviews(), but it
6767
 * can also be used to generate downsampled images in one file from another
6768
 * outside the overview architecture.
6769
 *
6770
 * The output bands need to exist in advance and share the same characteristics
6771
 * (type, dimensions)
6772
 *
6773
 * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE",
6774
 * "RMS", "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" and "BILINEAR"
6775
 *
6776
 * It does not support color tables or complex data types.
6777
 *
6778
 * The pseudo-algorithm used by the function is :
6779
 *    for each overview
6780
 *       iterate on lines of the source by a step of deltay
6781
 *           iterate on columns of the source  by a step of deltax
6782
 *               read the source data of size deltax * deltay for all the bands
6783
 *               generate the corresponding overview block for all the bands
6784
 *
6785
 * This function will honour properly NODATA_VALUES tuples (special dataset
6786
 * metadata) so that only a given RGB triplet (in case of a RGB image) will be
6787
 * considered as the nodata value and not each value of the triplet
6788
 * independently per band.
6789
 *
6790
 * The GDAL_NUM_THREADS configuration option can be set
6791
 * to "ALL_CPUS" or a integer value to specify the number of threads to use for
6792
 * overview computation.
6793
 *
6794
 * @param apoSrcBands the list of source bands to downsample
6795
 * @param aapoOverviewBands bidimension array of bands. First dimension is
6796
 *                          indexed by bands. Second dimension is indexed by
6797
 *                          overview levels. All aapoOverviewBands[i] arrays
6798
 *                          must have the same size (i.e. same number of
6799
 *                          overviews)
6800
 * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE", "RMS",
6801
 * "GAUSS", "CUBIC", "CUBICSPLINE", "LANCZOS" or "BILINEAR").
6802
 * @param pfnProgress progress report function.
6803
 * @param pProgressData progress function callback data.
6804
 * @param papszOptions NULL terminated list of options as
6805
 *                     key=value pairs, or NULL
6806
 *                     The XOFF, YOFF, XSIZE and YSIZE
6807
 *                     options can be specified to express that overviews should
6808
 *                     be regenerated only in the specified subset of the source
6809
 *                     dataset.
6810
 * @return CE_None on success or CE_Failure on failure.
6811
 * @since 3.10
6812
 */
6813
6814
CPLErr GDALRegenerateOverviewsMultiBand(
6815
    const std::vector<GDALRasterBand *> &apoSrcBands,
6816
    const std::vector<std::vector<GDALRasterBand *>> &aapoOverviewBands,
6817
    const char *pszResampling, GDALProgressFunc pfnProgress,
6818
    void *pProgressData, CSLConstList papszOptions)
6819
0
{
6820
0
    CPLAssert(apoSrcBands.size() == aapoOverviewBands.size());
6821
0
    for (size_t i = 1; i < aapoOverviewBands.size(); ++i)
6822
0
    {
6823
0
        CPLAssert(aapoOverviewBands[i].size() == aapoOverviewBands[0].size());
6824
0
    }
6825
6826
0
    if (aapoOverviewBands.empty())
6827
0
        return CE_None;
6828
6829
0
    std::vector<GDALRasterBand **> apapoOverviewBands;
6830
0
    for (auto &apoOverviewBands : aapoOverviewBands)
6831
0
    {
6832
0
        auto papoOverviewBands = static_cast<GDALRasterBand **>(
6833
0
            CPLMalloc(apoOverviewBands.size() * sizeof(GDALRasterBand *)));
6834
0
        for (size_t i = 0; i < apoOverviewBands.size(); ++i)
6835
0
        {
6836
0
            papoOverviewBands[i] = apoOverviewBands[i];
6837
0
        }
6838
0
        apapoOverviewBands.push_back(papoOverviewBands);
6839
0
    }
6840
0
    const CPLErr eErr = GDALRegenerateOverviewsMultiBand(
6841
0
        static_cast<int>(apoSrcBands.size()), apoSrcBands.data(),
6842
0
        static_cast<int>(aapoOverviewBands[0].size()),
6843
0
        apapoOverviewBands.data(), pszResampling, pfnProgress, pProgressData,
6844
0
        papszOptions);
6845
0
    for (GDALRasterBand **papoOverviewBands : apapoOverviewBands)
6846
0
        CPLFree(papoOverviewBands);
6847
0
    return eErr;
6848
0
}
6849
6850
/************************************************************************/
6851
/*                        GDALComputeBandStats()                        */
6852
/************************************************************************/
6853
6854
/** Undocumented
6855
 * @param hSrcBand undocumented.
6856
 * @param nSampleStep Step between scanlines used to compute statistics.
6857
 *                    When nSampleStep is equal to 1, all scanlines will
6858
 *                    be processed.
6859
 * @param pdfMean undocumented.
6860
 * @param pdfStdDev undocumented.
6861
 * @param pfnProgress undocumented.
6862
 * @param pProgressData undocumented.
6863
 * @return undocumented
6864
 */
6865
CPLErr CPL_STDCALL GDALComputeBandStats(GDALRasterBandH hSrcBand,
6866
                                        int nSampleStep, double *pdfMean,
6867
                                        double *pdfStdDev,
6868
                                        GDALProgressFunc pfnProgress,
6869
                                        void *pProgressData)
6870
6871
0
{
6872
0
    VALIDATE_POINTER1(hSrcBand, "GDALComputeBandStats", CE_Failure);
6873
6874
0
    GDALRasterBand *poSrcBand = GDALRasterBand::FromHandle(hSrcBand);
6875
6876
0
    if (pfnProgress == nullptr)
6877
0
        pfnProgress = GDALDummyProgress;
6878
6879
0
    const int nWidth = poSrcBand->GetXSize();
6880
0
    const int nHeight = poSrcBand->GetYSize();
6881
6882
0
    if (nSampleStep >= nHeight || nSampleStep < 1)
6883
0
        nSampleStep = 1;
6884
6885
0
    GDALDataType eWrkType = GDT_Unknown;
6886
0
    float *pafData = nullptr;
6887
0
    GDALDataType eType = poSrcBand->GetRasterDataType();
6888
0
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType));
6889
0
    if (bComplex)
6890
0
    {
6891
0
        pafData = static_cast<float *>(
6892
0
            VSI_MALLOC2_VERBOSE(nWidth, 2 * sizeof(float)));
6893
0
        eWrkType = GDT_CFloat32;
6894
0
    }
6895
0
    else
6896
0
    {
6897
0
        pafData =
6898
0
            static_cast<float *>(VSI_MALLOC2_VERBOSE(nWidth, sizeof(float)));
6899
0
        eWrkType = GDT_Float32;
6900
0
    }
6901
6902
0
    if (nWidth == 0 || pafData == nullptr)
6903
0
    {
6904
0
        VSIFree(pafData);
6905
0
        return CE_Failure;
6906
0
    }
6907
6908
    /* -------------------------------------------------------------------- */
6909
    /*      Loop over all sample lines.                                     */
6910
    /* -------------------------------------------------------------------- */
6911
0
    double dfSum = 0.0;
6912
0
    double dfSum2 = 0.0;
6913
0
    int iLine = 0;
6914
0
    GIntBig nSamples = 0;
6915
6916
0
    do
6917
0
    {
6918
0
        if (!pfnProgress(iLine / static_cast<double>(nHeight), nullptr,
6919
0
                         pProgressData))
6920
0
        {
6921
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6922
0
            CPLFree(pafData);
6923
0
            return CE_Failure;
6924
0
        }
6925
6926
0
        const CPLErr eErr =
6927
0
            poSrcBand->RasterIO(GF_Read, 0, iLine, nWidth, 1, pafData, nWidth,
6928
0
                                1, eWrkType, 0, 0, nullptr);
6929
0
        if (eErr != CE_None)
6930
0
        {
6931
0
            CPLFree(pafData);
6932
0
            return eErr;
6933
0
        }
6934
6935
0
        for (int iPixel = 0; iPixel < nWidth; ++iPixel)
6936
0
        {
6937
0
            float fValue = 0.0f;
6938
6939
0
            if (bComplex)
6940
0
            {
6941
                // Compute the magnitude of the complex value.
6942
0
                fValue =
6943
0
                    std::hypot(pafData[static_cast<size_t>(iPixel) * 2],
6944
0
                               pafData[static_cast<size_t>(iPixel) * 2 + 1]);
6945
0
            }
6946
0
            else
6947
0
            {
6948
0
                fValue = pafData[iPixel];
6949
0
            }
6950
6951
0
            dfSum += static_cast<double>(fValue);
6952
0
            dfSum2 += static_cast<double>(fValue) * static_cast<double>(fValue);
6953
0
        }
6954
6955
0
        nSamples += nWidth;
6956
0
        iLine += nSampleStep;
6957
0
    } while (iLine < nHeight);
6958
6959
0
    if (!pfnProgress(1.0, nullptr, pProgressData))
6960
0
    {
6961
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
6962
0
        CPLFree(pafData);
6963
0
        return CE_Failure;
6964
0
    }
6965
6966
    /* -------------------------------------------------------------------- */
6967
    /*      Produce the result values.                                      */
6968
    /* -------------------------------------------------------------------- */
6969
0
    if (pdfMean != nullptr)
6970
0
        *pdfMean = dfSum / nSamples;
6971
6972
0
    if (pdfStdDev != nullptr)
6973
0
    {
6974
0
        const double dfMean = dfSum / nSamples;
6975
6976
0
        *pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean));
6977
0
    }
6978
6979
0
    CPLFree(pafData);
6980
6981
0
    return CE_None;
6982
0
}
6983
6984
/************************************************************************/
6985
/*                  GDALOverviewMagnitudeCorrection()                   */
6986
/*                                                                      */
6987
/*      Correct the mean and standard deviation of the overviews of     */
6988
/*      the given band to match the base layer approximately.           */
6989
/************************************************************************/
6990
6991
/** Undocumented
6992
 * @param hBaseBand undocumented.
6993
 * @param nOverviewCount undocumented.
6994
 * @param pahOverviews undocumented.
6995
 * @param pfnProgress undocumented.
6996
 * @param pProgressData undocumented.
6997
 * @return undocumented
6998
 */
6999
CPLErr GDALOverviewMagnitudeCorrection(GDALRasterBandH hBaseBand,
7000
                                       int nOverviewCount,
7001
                                       GDALRasterBandH *pahOverviews,
7002
                                       GDALProgressFunc pfnProgress,
7003
                                       void *pProgressData)
7004
7005
0
{
7006
0
    VALIDATE_POINTER1(hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure);
7007
7008
    /* -------------------------------------------------------------------- */
7009
    /*      Compute mean/stddev for source raster.                          */
7010
    /* -------------------------------------------------------------------- */
7011
0
    double dfOrigMean = 0.0;
7012
0
    double dfOrigStdDev = 0.0;
7013
0
    {
7014
0
        const CPLErr eErr =
7015
0
            GDALComputeBandStats(hBaseBand, 2, &dfOrigMean, &dfOrigStdDev,
7016
0
                                 pfnProgress, pProgressData);
7017
7018
0
        if (eErr != CE_None)
7019
0
            return eErr;
7020
0
    }
7021
7022
    /* -------------------------------------------------------------------- */
7023
    /*      Loop on overview bands.                                         */
7024
    /* -------------------------------------------------------------------- */
7025
0
    for (int iOverview = 0; iOverview < nOverviewCount; ++iOverview)
7026
0
    {
7027
0
        GDALRasterBand *poOverview =
7028
0
            GDALRasterBand::FromHandle(pahOverviews[iOverview]);
7029
0
        double dfOverviewMean, dfOverviewStdDev;
7030
7031
0
        const CPLErr eErr =
7032
0
            GDALComputeBandStats(pahOverviews[iOverview], 1, &dfOverviewMean,
7033
0
                                 &dfOverviewStdDev, pfnProgress, pProgressData);
7034
7035
0
        if (eErr != CE_None)
7036
0
            return eErr;
7037
7038
0
        double dfGain = 1.0;
7039
0
        if (dfOrigStdDev >= 0.0001)
7040
0
            dfGain = dfOrigStdDev / dfOverviewStdDev;
7041
7042
        /* --------------------------------------------------------------------
7043
         */
7044
        /*      Apply gain and offset. */
7045
        /* --------------------------------------------------------------------
7046
         */
7047
0
        const int nWidth = poOverview->GetXSize();
7048
0
        const int nHeight = poOverview->GetYSize();
7049
7050
0
        GDALDataType eWrkType = GDT_Unknown;
7051
0
        float *pafData = nullptr;
7052
0
        const GDALDataType eType = poOverview->GetRasterDataType();
7053
0
        const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType));
7054
0
        if (bComplex)
7055
0
        {
7056
0
            pafData = static_cast<float *>(
7057
0
                VSI_MALLOC2_VERBOSE(nWidth, 2 * sizeof(float)));
7058
0
            eWrkType = GDT_CFloat32;
7059
0
        }
7060
0
        else
7061
0
        {
7062
0
            pafData = static_cast<float *>(
7063
0
                VSI_MALLOC2_VERBOSE(nWidth, sizeof(float)));
7064
0
            eWrkType = GDT_Float32;
7065
0
        }
7066
7067
0
        if (pafData == nullptr)
7068
0
        {
7069
0
            return CE_Failure;
7070
0
        }
7071
7072
0
        for (int iLine = 0; iLine < nHeight; ++iLine)
7073
0
        {
7074
0
            if (!pfnProgress(iLine / static_cast<double>(nHeight), nullptr,
7075
0
                             pProgressData))
7076
0
            {
7077
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
7078
0
                CPLFree(pafData);
7079
0
                return CE_Failure;
7080
0
            }
7081
7082
0
            if (poOverview->RasterIO(GF_Read, 0, iLine, nWidth, 1, pafData,
7083
0
                                     nWidth, 1, eWrkType, 0, 0,
7084
0
                                     nullptr) != CE_None)
7085
0
            {
7086
0
                CPLFree(pafData);
7087
0
                return CE_Failure;
7088
0
            }
7089
7090
0
            for (int iPixel = 0; iPixel < nWidth; ++iPixel)
7091
0
            {
7092
0
                if (bComplex)
7093
0
                {
7094
0
                    pafData[static_cast<size_t>(iPixel) * 2] *=
7095
0
                        static_cast<float>(dfGain);
7096
0
                    pafData[static_cast<size_t>(iPixel) * 2 + 1] *=
7097
0
                        static_cast<float>(dfGain);
7098
0
                }
7099
0
                else
7100
0
                {
7101
0
                    pafData[iPixel] = static_cast<float>(
7102
0
                        (double(pafData[iPixel]) - dfOverviewMean) * dfGain +
7103
0
                        dfOrigMean);
7104
0
                }
7105
0
            }
7106
7107
0
            if (poOverview->RasterIO(GF_Write, 0, iLine, nWidth, 1, pafData,
7108
0
                                     nWidth, 1, eWrkType, 0, 0,
7109
0
                                     nullptr) != CE_None)
7110
0
            {
7111
0
                CPLFree(pafData);
7112
0
                return CE_Failure;
7113
0
            }
7114
0
        }
7115
7116
0
        if (!pfnProgress(1.0, nullptr, pProgressData))
7117
0
        {
7118
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
7119
0
            CPLFree(pafData);
7120
0
            return CE_Failure;
7121
0
        }
7122
7123
0
        CPLFree(pafData);
7124
0
    }
7125
7126
0
    return CE_None;
7127
0
}