• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.58
/alg/gdalpansharpen.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Pansharpening module
4
 * Purpose:  Implementation of pansharpening.
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
9
 * Copyright (c) 2015, Airbus DS Geo SA (weighted Brovey algorithm)
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "cpl_worker_thread_pool.h"
16
#include "gdalpansharpen.h"
17

18
#include <algorithm>
19
#include <array>
20
#include <cstddef>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <limits>
25
#include <new>
26

27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_float.h"
30
#include "cpl_multiproc.h"
31
#include "cpl_vsi.h"
32
#include "../frmts/mem/memdataset.h"
33
#include "../frmts/vrt/vrtdataset.h"
34
#include "gdal_priv.h"
35
#include "gdal_priv_templates.hpp"
36
// #include "gdalsse_priv.h"
37

38
// Limit types to practical use cases.
39
#define LIMIT_TYPES 1
40

41
/************************************************************************/
42
/*                     GDALCreatePansharpenOptions()                    */
43
/************************************************************************/
44

45
/** Create pansharpening options.
46
 *
47
 * @return a newly allocated pansharpening option structure that must be freed
48
 * with GDALDestroyPansharpenOptions().
49
 *
50
 * @since GDAL 2.1
51
 */
52

53
GDALPansharpenOptions *GDALCreatePansharpenOptions()
139✔
54
{
55
    GDALPansharpenOptions *psOptions = static_cast<GDALPansharpenOptions *>(
56
        CPLCalloc(1, sizeof(GDALPansharpenOptions)));
139✔
57
    psOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
139✔
58
    psOptions->eResampleAlg = GRIORA_Cubic;
139✔
59
    return psOptions;
139✔
60
}
61

62
/************************************************************************/
63
/*                     GDALDestroyPansharpenOptions()                   */
64
/************************************************************************/
65

66
/** Destroy pansharpening options.
67
 *
68
 * @param psOptions a pansharpening option structure allocated with
69
 * GDALCreatePansharpenOptions()
70
 *
71
 * @since GDAL 2.1
72
 */
73

74
void GDALDestroyPansharpenOptions(GDALPansharpenOptions *psOptions)
140✔
75
{
76
    if (psOptions == nullptr)
140✔
77
        return;
1✔
78
    CPLFree(psOptions->padfWeights);
139✔
79
    CPLFree(psOptions->pahInputSpectralBands);
139✔
80
    CPLFree(psOptions->panOutPansharpenedBands);
139✔
81
    CPLFree(psOptions);
139✔
82
}
83

84
/************************************************************************/
85
/*                      GDALClonePansharpenOptions()                    */
86
/************************************************************************/
87

88
/** Clone pansharpening options.
89
 *
90
 * @param psOptions a pansharpening option structure allocated with
91
 * GDALCreatePansharpenOptions()
92
 * @return a newly allocated pansharpening option structure that must be freed
93
 * with GDALDestroyPansharpenOptions().
94
 *
95
 * @since GDAL 2.1
96
 */
97

98
GDALPansharpenOptions *
99
GDALClonePansharpenOptions(const GDALPansharpenOptions *psOptions)
72✔
100
{
101
    GDALPansharpenOptions *psNewOptions = GDALCreatePansharpenOptions();
72✔
102
    psNewOptions->ePansharpenAlg = psOptions->ePansharpenAlg;
72✔
103
    psNewOptions->eResampleAlg = psOptions->eResampleAlg;
72✔
104
    psNewOptions->nBitDepth = psOptions->nBitDepth;
72✔
105
    psNewOptions->nWeightCount = psOptions->nWeightCount;
72✔
106
    if (psOptions->padfWeights)
72✔
107
    {
108
        psNewOptions->padfWeights = static_cast<double *>(
72✔
109
            CPLMalloc(sizeof(double) * psOptions->nWeightCount));
72✔
110
        memcpy(psNewOptions->padfWeights, psOptions->padfWeights,
72✔
111
               sizeof(double) * psOptions->nWeightCount);
72✔
112
    }
113
    psNewOptions->hPanchroBand = psOptions->hPanchroBand;
72✔
114
    psNewOptions->nInputSpectralBands = psOptions->nInputSpectralBands;
72✔
115
    if (psOptions->pahInputSpectralBands)
72✔
116
    {
117
        const size_t nSize =
72✔
118
            sizeof(GDALRasterBandH) * psOptions->nInputSpectralBands;
72✔
119
        psNewOptions->pahInputSpectralBands =
72✔
120
            static_cast<GDALRasterBandH *>(CPLMalloc(nSize));
72✔
121
        memcpy(psNewOptions->pahInputSpectralBands,
72✔
122
               psOptions->pahInputSpectralBands, nSize);
72✔
123
    }
124
    psNewOptions->nOutPansharpenedBands = psOptions->nOutPansharpenedBands;
72✔
125
    if (psOptions->panOutPansharpenedBands)
72✔
126
    {
127
        psNewOptions->panOutPansharpenedBands = static_cast<int *>(
71✔
128
            CPLMalloc(sizeof(int) * psOptions->nOutPansharpenedBands));
71✔
129
        memcpy(psNewOptions->panOutPansharpenedBands,
71✔
130
               psOptions->panOutPansharpenedBands,
71✔
131
               sizeof(int) * psOptions->nOutPansharpenedBands);
71✔
132
    }
133
    psNewOptions->bHasNoData = psOptions->bHasNoData;
72✔
134
    psNewOptions->dfNoData = psOptions->dfNoData;
72✔
135
    psNewOptions->nThreads = psOptions->nThreads;
72✔
136
    return psNewOptions;
72✔
137
}
138

139
/************************************************************************/
140
/*                        GDALPansharpenOperation()                     */
141
/************************************************************************/
142

143
/** Pansharpening operation constructor.
144
 *
145
 * The object is ready to be used after Initialize() has been called.
146
 */
147
GDALPansharpenOperation::GDALPansharpenOperation() = default;
148

149
/************************************************************************/
150
/*                       ~GDALPansharpenOperation()                     */
151
/************************************************************************/
152

153
/** Pansharpening operation destructor.
154
 */
155

156
GDALPansharpenOperation::~GDALPansharpenOperation()
70✔
157
{
158
    GDALDestroyPansharpenOptions(psOptions);
70✔
159
    for (size_t i = 0; i < aVDS.size(); i++)
79✔
160
        delete aVDS[i];
9✔
161
    delete poThreadPool;
70✔
162
}
70✔
163

164
/************************************************************************/
165
/*                              Initialize()                            */
166
/************************************************************************/
167

168
/** Initialize the pansharpening operation.
169
 *
170
 * @param psOptionsIn pansharpening options. Must not be NULL.
171
 *
172
 * @return CE_None in case of success, CE_Failure in case of failure.
173
 */
174
CPLErr
175
GDALPansharpenOperation::Initialize(const GDALPansharpenOptions *psOptionsIn)
70✔
176
{
177
    if (psOptionsIn->hPanchroBand == nullptr)
70✔
178
    {
179
        CPLError(CE_Failure, CPLE_AppDefined, "hPanchroBand not set");
×
180
        return CE_Failure;
×
181
    }
182
    if (psOptionsIn->nInputSpectralBands <= 0)
70✔
183
    {
184
        CPLError(CE_Failure, CPLE_AppDefined,
×
185
                 "No input spectral bands defined");
186
        return CE_Failure;
×
187
    }
188
    if (psOptionsIn->padfWeights == nullptr ||
70✔
189
        psOptionsIn->nWeightCount != psOptionsIn->nInputSpectralBands)
70✔
190
    {
191
        CPLError(CE_Failure, CPLE_AppDefined,
×
192
                 "No weights defined, or not the same number as input "
193
                 "spectral bands");
194
        return CE_Failure;
×
195
    }
196

197
    auto poPanchroBand = GDALRasterBand::FromHandle(psOptionsIn->hPanchroBand);
70✔
198
    auto poPanchroDS = poPanchroBand->GetDataset();
70✔
199
    if (poPanchroDS == nullptr)
70✔
200
    {
201
        CPLError(CE_Failure, CPLE_AppDefined,
×
202
                 "Cannot retrieve dataset associated with hPanchroBand");
203
        return CE_Failure;
×
204
    }
205
    // Make sure that the band is really a first level child of the owning dataset
206
    if (poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != poPanchroBand)
70✔
207
    {
208
        CPLError(CE_Failure, CPLE_AppDefined,
×
209
                 "poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != "
210
                 "poPanchroBand");
211
        return CE_Failure;
×
212
    }
213
    GDALGeoTransform panchroGT;
70✔
214
    if (poPanchroDS->GetGeoTransform(panchroGT) != CE_None)
70✔
215
    {
216
        CPLError(CE_Failure, CPLE_AppDefined,
×
217
                 "Panchromatic band has no associated geotransform");
218
        return CE_Failure;
×
219
    }
220

221
    GDALRasterBandH hRefBand = psOptionsIn->pahInputSpectralBands[0];
70✔
222
    auto poRefBand = GDALRasterBand::FromHandle(hRefBand);
70✔
223
    auto poRefBandDS = poRefBand->GetDataset();
70✔
224
    if (poRefBandDS == nullptr)
70✔
225
    {
226
        CPLError(
×
227
            CE_Failure, CPLE_AppDefined,
228
            "Cannot retrieve dataset associated with ahInputSpectralBands[0]");
229
        return CE_Failure;
×
230
    }
231
    // Make sure that the band is really a first level child of the owning dataset
232
    if (poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand)
70✔
233
    {
234
        CPLError(
×
235
            CE_Failure, CPLE_AppDefined,
236
            "poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand");
237
        return CE_Failure;
×
238
    }
239

240
    GDALGeoTransform refMSGT;
70✔
241
    if (poRefBandDS->GetGeoTransform(refMSGT) != CE_None)
70✔
242
    {
243
        CPLError(CE_Failure, CPLE_AppDefined,
×
244
                 "ahInputSpectralBands[0] band has no associated geotransform");
245
        return CE_Failure;
×
246
    }
247

248
    GDALGeoTransform invMSGT;
70✔
249
    if (!refMSGT.GetInverse(invMSGT))
70✔
250
    {
251
        CPLError(CE_Failure, CPLE_AppDefined,
×
252
                 "ahInputSpectralBands[0] geotransform is not invertible");
253
        return CE_Failure;
×
254
    }
255

256
    // Do InvMSGT * PanchroGT multiplication
257
    m_panToMSGT[1] = invMSGT[1] * panchroGT[1] + invMSGT[2] * panchroGT[4];
70✔
258
    m_panToMSGT[2] = invMSGT[1] * panchroGT[2] + invMSGT[2] * panchroGT[5];
70✔
259
    m_panToMSGT[0] =
140✔
260
        invMSGT[1] * panchroGT[0] + invMSGT[2] * panchroGT[3] + invMSGT[0];
70✔
261
    m_panToMSGT[4] = invMSGT[4] * panchroGT[1] + invMSGT[5] * panchroGT[4];
70✔
262
    m_panToMSGT[5] = invMSGT[4] * panchroGT[2] + invMSGT[5] * panchroGT[5];
70✔
263
    m_panToMSGT[3] =
140✔
264
        invMSGT[4] * panchroGT[0] + invMSGT[5] * panchroGT[3] + invMSGT[3];
70✔
265
#if 0
266
    CPLDebug("GDAL", "m_panToMSGT[] = %g %g %g %g %g %g",
267
           m_panToMSGT[0], m_panToMSGT[1], m_panToMSGT[2],
268
           m_panToMSGT[3], m_panToMSGT[4], m_panToMSGT[5]);
269
#endif
270
    if (std::fabs(m_panToMSGT[2]) > 1e-10 || std::fabs(m_panToMSGT[4]) > 1e-10)
70✔
271
    {
272
        CPLError(CE_Failure, CPLE_NotSupported,
×
273
                 "Composition of panchromatic and multispectral geotransform "
274
                 "has rotational terms");
275
        return CE_Failure;
×
276
    }
277

278
    bool bSameDataset = psOptionsIn->nInputSpectralBands > 1;
70✔
279
    if (bSameDataset)
70✔
280
        anInputBands.push_back(GDALGetBandNumber(hRefBand));
55✔
281
    for (int i = 1; i < psOptionsIn->nInputSpectralBands; i++)
174✔
282
    {
283
        GDALRasterBandH hBand = psOptionsIn->pahInputSpectralBands[i];
105✔
284
        if (GDALGetRasterBandXSize(hBand) != GDALGetRasterBandXSize(hRefBand) ||
209✔
285
            GDALGetRasterBandYSize(hBand) != GDALGetRasterBandYSize(hRefBand))
104✔
286
        {
287
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
288
                     "Dimensions of input spectral band %d different from "
289
                     "first spectral band",
290
                     i + 1);
291
            return CE_Failure;
1✔
292
        }
293

294
        auto poBand = GDALRasterBand::FromHandle(hBand);
104✔
295
        auto poBandDS = poBand->GetDataset();
104✔
296
        if (poBandDS == nullptr)
104✔
297
        {
298
            CPLError(CE_Failure, CPLE_AppDefined,
×
299
                     "Cannot retrieve dataset associated with "
300
                     "input spectral band %d",
301
                     i + 1);
302
            return CE_Failure;
×
303
        }
304
        // Make sure that the band is really a first level child of the owning dataset
305
        if (poBandDS->GetRasterBand(poBand->GetBand()) != poBand)
104✔
306
        {
307
            CPLError(CE_Failure, CPLE_AppDefined,
×
308
                     "poBandDS->GetRasterBand(poBand->GetBand()) != poBand");
309
            return CE_Failure;
×
310
        }
311

312
        GDALGeoTransform MSGT;
104✔
313
        if (poBandDS->GetGeoTransform(MSGT) != CE_None)
104✔
314
        {
315
            CPLError(
×
316
                CE_Failure, CPLE_AppDefined,
317
                "input spectral band %d band has no associated geotransform",
318
                i + 1);
319
            return CE_Failure;
×
320
        }
321
        if (MSGT != refMSGT)
104✔
322
        {
323
            CPLError(CE_Failure, CPLE_AppDefined,
×
324
                     "input spectral band %d has a different "
325
                     "geotransform than the first spectral band",
326
                     i + 1);
327
            return CE_Failure;
×
328
        }
329

330
        if (bSameDataset)
104✔
331
        {
332
            if (GDALGetBandDataset(hBand) != GDALGetBandDataset(hRefBand))
101✔
333
            {
334
                anInputBands.resize(0);
5✔
335
                bSameDataset = false;
5✔
336
            }
337
            else
338
            {
339
                anInputBands.push_back(GDALGetBandNumber(hBand));
96✔
340
            }
341
        }
342
    }
343
    if (psOptionsIn->nOutPansharpenedBands == 0)
69✔
344
    {
345
        CPLError(CE_Warning, CPLE_AppDefined,
1✔
346
                 "No output pansharpened band defined");
347
    }
348
    for (int i = 0; i < psOptionsIn->nOutPansharpenedBands; i++)
236✔
349
    {
350
        if (psOptionsIn->panOutPansharpenedBands[i] < 0 ||
167✔
351
            psOptionsIn->panOutPansharpenedBands[i] >=
167✔
352
                psOptionsIn->nInputSpectralBands)
167✔
353
        {
354
            CPLError(CE_Failure, CPLE_AppDefined,
×
355
                     "Invalid value panOutPansharpenedBands[%d] = %d", i,
356
                     psOptionsIn->panOutPansharpenedBands[i]);
×
357
            return CE_Failure;
×
358
        }
359
    }
360

361
    GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
69✔
362
    if (psOptionsIn->nBitDepth)
69✔
363
    {
364
        if (psOptionsIn->nBitDepth < 0 || psOptionsIn->nBitDepth > 31 ||
13✔
365
            (eWorkDataType == GDT_Byte && psOptionsIn->nBitDepth > 8) ||
13✔
366
            (eWorkDataType == GDT_UInt16 && psOptionsIn->nBitDepth > 16))
3✔
367
        {
368
            CPLError(CE_Failure, CPLE_AppDefined,
×
369
                     "Invalid value nBitDepth = %d for type %s",
370
                     psOptionsIn->nBitDepth,
×
371
                     GDALGetDataTypeName(eWorkDataType));
372
            return CE_Failure;
×
373
        }
374
    }
375

376
    psOptions = GDALClonePansharpenOptions(psOptionsIn);
69✔
377
    if (psOptions->nBitDepth == GDALGetDataTypeSizeBits(eWorkDataType))
69✔
378
        psOptions->nBitDepth = 0;
7✔
379
    if (psOptions->nBitDepth &&
69✔
380
        !(eWorkDataType == GDT_Byte || eWorkDataType == GDT_UInt16 ||
3✔
381
          eWorkDataType == GDT_UInt32 || eWorkDataType == GDT_UInt64))
382
    {
383
        CPLError(CE_Warning, CPLE_AppDefined,
×
384
                 "Ignoring nBitDepth = %d for type %s", psOptions->nBitDepth,
×
385
                 GDALGetDataTypeName(eWorkDataType));
386
        psOptions->nBitDepth = 0;
×
387
    }
388

389
    // Detect negative weights.
390
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
242✔
391
    {
392
        if (psOptions->padfWeights[i] < 0.0)
173✔
393
        {
394
            bPositiveWeights = FALSE;
×
395
            break;
×
396
        }
397
    }
398

399
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
242✔
400
    {
401
        aMSBands.push_back(
346✔
402
            GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]));
173✔
403
    }
404

405
    if (psOptions->bHasNoData)
69✔
406
    {
407
        bool bNeedToWrapInVRT = false;
9✔
408
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
29✔
409
        {
410
            GDALRasterBand *poBand =
411
                GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
20✔
412
            int bHasNoData = FALSE;
20✔
413
            double dfNoData = poBand->GetNoDataValue(&bHasNoData);
20✔
414
            if (!bHasNoData || dfNoData != psOptions->dfNoData)
20✔
415
                bNeedToWrapInVRT = true;
17✔
416
        }
417

418
        if (bNeedToWrapInVRT)
9✔
419
        {
420
            // Wrap spectral bands in a VRT if they don't have the nodata value.
421
            VRTDataset *poVDS = nullptr;
8✔
422
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
25✔
423
            {
424
                GDALRasterBand *poSrcBand = aMSBands[i];
17✔
425
                int iVRTBand = 1;
17✔
426
                if (anInputBands.empty() || i == 0)
17✔
427
                {
428
                    poVDS = new VRTDataset(poSrcBand->GetXSize(),
9✔
429
                                           poSrcBand->GetYSize());
9✔
430
                    aVDS.push_back(poVDS);
9✔
431
                }
432
                if (!anInputBands.empty())
17✔
433
                {
434
                    anInputBands[i] = i + 1;
13✔
435
                    iVRTBand = i + 1;
13✔
436
                }
437
                poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
17✔
438
                VRTSourcedRasterBand *poVRTBand =
439
                    dynamic_cast<VRTSourcedRasterBand *>(
×
440
                        poVDS->GetRasterBand(iVRTBand));
17✔
441
                if (poVRTBand == nullptr)
17✔
442
                    return CE_Failure;
×
443
                aMSBands[i] = poVRTBand;
17✔
444
                poVRTBand->SetNoDataValue(psOptions->dfNoData);
17✔
445
                const char *pszNBITS =
446
                    poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
17✔
447
                if (pszNBITS)
17✔
448
                    poVRTBand->SetMetadataItem("NBITS", pszNBITS,
×
449
                                               "IMAGE_STRUCTURE");
×
450

451
                VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
17✔
452
                poVRTBand->ConfigureSource(
68✔
453
                    poSimpleSource, poSrcBand, FALSE, 0, 0,
454
                    poSrcBand->GetXSize(), poSrcBand->GetYSize(), 0, 0,
17✔
455
                    poSrcBand->GetXSize(), poSrcBand->GetYSize());
17✔
456
                poVRTBand->AddSource(poSimpleSource);
17✔
457
            }
458
        }
459
    }
460

461
    // Setup thread pool.
462
    int nThreads = psOptions->nThreads;
69✔
463
    if (nThreads == -1)
69✔
464
        nThreads = CPLGetNumCPUs();
23✔
465
    else if (nThreads == 0)
46✔
466
    {
467
        const char *pszNumThreads =
468
            CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
46✔
469
        if (pszNumThreads)
46✔
470
        {
471
            if (EQUAL(pszNumThreads, "ALL_CPUS"))
×
472
                nThreads = CPLGetNumCPUs();
×
473
            else
474
                nThreads = std::max(0, std::min(128, atoi(pszNumThreads)));
×
475
        }
476
    }
477
    if (nThreads > 1)
69✔
478
    {
479
        CPLDebug("PANSHARPEN", "Using %d threads", nThreads);
23✔
480
        poThreadPool = new (std::nothrow) CPLWorkerThreadPool();
46✔
481
        // coverity[tainted_data]
482
        if (poThreadPool == nullptr ||
46✔
483
            !poThreadPool->Setup(nThreads, nullptr, nullptr))
23✔
484
        {
485
            delete poThreadPool;
×
486
            poThreadPool = nullptr;
×
487
        }
488
    }
489

490
    GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
69✔
491
    if (eResampleAlg != GRIORA_NearestNeighbour)
69✔
492
    {
493
        const char *pszResampling =
69✔
494
            (eResampleAlg == GRIORA_Bilinear)      ? "BILINEAR"
138✔
495
            : (eResampleAlg == GRIORA_Cubic)       ? "CUBIC"
69✔
496
            : (eResampleAlg == GRIORA_CubicSpline) ? "CUBICSPLINE"
×
497
            : (eResampleAlg == GRIORA_Lanczos)     ? "LANCZOS"
×
498
            : (eResampleAlg == GRIORA_Average)     ? "AVERAGE"
×
499
            : (eResampleAlg == GRIORA_RMS)         ? "RMS"
×
500
            : (eResampleAlg == GRIORA_Mode)        ? "MODE"
×
501
            : (eResampleAlg == GRIORA_Gauss)       ? "GAUSS"
×
502
                                                   : "UNKNOWN";
503

504
        GDALGetResampleFunction(pszResampling, &nKernelRadius);
69✔
505
    }
506

507
    return CE_None;
69✔
508
}
509

510
/************************************************************************/
511
/*                    WeightedBroveyWithNoData()                        */
512
/************************************************************************/
513

514
template <class WorkDataType, class OutDataType>
515
void GDALPansharpenOperation::WeightedBroveyWithNoData(
8✔
516
    const WorkDataType *pPanBuffer,
517
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
518
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
519
{
520
    WorkDataType noData, validValue;
521
    GDALCopyWord(psOptions->dfNoData, noData);
8✔
522

523
    if constexpr (!(std::numeric_limits<WorkDataType>::is_integer))
524
        validValue = static_cast<WorkDataType>(noData + 1e-5);
×
525
    else if (noData == std::numeric_limits<WorkDataType>::min())
8✔
526
        validValue = std::numeric_limits<WorkDataType>::min() + 1;
4✔
527
    else
528
        validValue = noData - 1;
4✔
529

530
    for (size_t j = 0; j < nValues; j++)
1,586,280✔
531
    {
532
        double dfPseudoPanchro = 0.0;
1,586,270✔
533
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
5,704,270✔
534
        {
535
            WorkDataType nSpectralVal =
4,118,000✔
536
                pUpsampledSpectralBuffer[i * nBandValues + j];
4,118,000✔
537
            if (nSpectralVal == noData)
4,118,000✔
538
            {
539
                dfPseudoPanchro = 0.0;
×
540
                break;
×
541
            }
542
            dfPseudoPanchro += psOptions->padfWeights[i] * nSpectralVal;
4,118,000✔
543
        }
544
        if (dfPseudoPanchro != 0.0 && pPanBuffer[j] != noData)
1,586,270✔
545
        {
546
            const double dfFactor = pPanBuffer[j] / dfPseudoPanchro;
1,592,120✔
547
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
5,632,720✔
548
            {
549
                WorkDataType nRawValue = pUpsampledSpectralBuffer
4,040,560✔
550
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
4,040,560✔
551
                WorkDataType nPansharpenedValue;
552
                GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
4,040,560✔
553
                if (nMaxValue != 0 && nPansharpenedValue > nMaxValue)
4,003,280✔
554
                    nPansharpenedValue = nMaxValue;
×
555
                // We don't want a valid value to be mapped to NoData.
556
                if (nPansharpenedValue == noData)
4,003,280✔
557
                    nPansharpenedValue = validValue;
5,170✔
558
                GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
4,003,280✔
559
            }
1,592,160✔
560
        }
561
        else
562
        {
563
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
518✔
564
            {
565
                GDALCopyWord(noData, pDataBuf[i * nBandValues + j]);
6,404✔
566
            }
567
        }
568
    }
569
}
8✔
570

571
/************************************************************************/
572
/*                           ComputeFactor()                            */
573
/************************************************************************/
574

575
template <class T>
576
static inline double ComputeFactor(T panValue, double dfPseudoPanchro)
22,564,570✔
577
{
578
    if (dfPseudoPanchro == 0.0)
22,564,570✔
579
        return 0.0;
4,163✔
580

581
    return panValue / dfPseudoPanchro;
22,560,400✔
582
}
583

584
/************************************************************************/
585
/*                           ClampAndRound()                            */
586
/************************************************************************/
587

588
template <class T> static inline T ClampAndRound(double dfVal, T nMaxValue)
1,081,328✔
589
{
590
    if (dfVal > nMaxValue)
1,081,328✔
591
        return nMaxValue;
50✔
592
    else
593
        return static_cast<T>(dfVal + 0.5);
1,081,284✔
594
}
595

596
/************************************************************************/
597
/*                         WeightedBrovey()                             */
598
/************************************************************************/
599

600
template <class WorkDataType, class OutDataType, int bHasBitDepth>
601
void GDALPansharpenOperation::WeightedBrovey3(
158✔
602
    const WorkDataType *pPanBuffer,
603
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
604
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
605
{
606
    if (psOptions->bHasNoData)
158✔
607
    {
608
        WeightedBroveyWithNoData<WorkDataType, OutDataType>(
8✔
609
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
610
            nBandValues, nMaxValue);
611
        return;
8✔
612
    }
613

614
    for (size_t j = 0; j < nValues; j++)
21,675,552✔
615
    {
616
        double dfFactor = 0.0;
21,792,730✔
617
        // if( pPanBuffer[j] == 0 )
618
        //     dfFactor = 1.0;
619
        // else
620
        {
621
            double dfPseudoPanchro = 0.0;
21,792,730✔
622
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
93,455,100✔
623
                dfPseudoPanchro +=
71,662,400✔
624
                    psOptions->padfWeights[i] *
71,662,400✔
625
                    pUpsampledSpectralBuffer[i * nBandValues + j];
71,662,400✔
626
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
21,792,730✔
627
        }
628

629
        for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
84,784,800✔
630
        {
631
            WorkDataType nRawValue =
63,109,500✔
632
                pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
63,109,500✔
633
                                             nBandValues +
63,109,500✔
634
                                         j];
635
            WorkDataType nPansharpenedValue;
636
            GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
63,109,500✔
637
            if constexpr (bHasBitDepth)
638
            {
639
                if (nPansharpenedValue > nMaxValue)
×
640
                    nPansharpenedValue = nMaxValue;
×
641
            }
642
            GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
59,689,000✔
643
        }
644
    }
645
}
646

647
/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
648
/* Could possibly be used too on 32bit, but we would need to check at runtime */
649
#if defined(__x86_64) || defined(_M_X64) || defined(USE_NEON_OPTIMIZATIONS)
650

651
#define USE_SSE2
652
#include "gdalsse_priv.h"
653

654
template <class T, int NINPUT, int NOUTPUT>
655
size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
39✔
656
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
657
    size_t nValues, size_t nBandValues, T nMaxValue) const
658
{
659
    static_assert(NINPUT == 3 || NINPUT == 4);
660
    static_assert(NOUTPUT == 3 || NOUTPUT == 4);
661
    const XMMReg4Double w0 =
39✔
662
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 0);
39✔
663
    const XMMReg4Double w1 =
41✔
664
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 1);
41✔
665
    const XMMReg4Double w2 =
39✔
666
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 2);
39✔
667
    [[maybe_unused]] const XMMReg4Double w3 =
40✔
668
        (NINPUT == 3)
669
            ? XMMReg4Double::Zero()
670
            : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 3);
8✔
671

672
    const XMMReg4Double zero = XMMReg4Double::Zero();
39✔
673
    double dfMaxValue = nMaxValue;
41✔
674
    const XMMReg4Double maxValue =
41✔
675
        XMMReg4Double::Load1ValHighAndLow(&dfMaxValue);
676

677
    size_t j = 0;  // Used after for.
876✔
678
    for (; j + 3 < nValues; j += 4)
1,328,784✔
679
    {
680
        XMMReg4Double pseudoPanchro = zero;
1,328,743✔
681

682
        XMMReg4Double val0 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,314,530✔
683
                                                     0 * nBandValues + j);
758,837✔
684
        XMMReg4Double val1 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,325,619✔
685
                                                     1 * nBandValues + j);
1,325,619✔
686
        XMMReg4Double val2 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,325,287✔
687
                                                     2 * nBandValues + j);
1,325,287✔
688
        XMMReg4Double val3;
1,328,294✔
689
        if constexpr (NINPUT == 4 || NOUTPUT == 4)
690
        {
691
            val3 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
512,912✔
692
                                           3 * nBandValues + j);
508,047✔
693
        }
694

695
        pseudoPanchro += w0 * val0;
1,320,507✔
696
        pseudoPanchro += w1 * val1;
1,319,146✔
697
        pseudoPanchro += w2 * val2;
1,322,489✔
698
        if constexpr (NINPUT == 4)
699
            pseudoPanchro += w3 * val3;
510,893✔
700

701
        /* Little trick to avoid use of ternary operator due to one of the
702
         * branch being zero */
703
        XMMReg4Double factor = XMMReg4Double::And(
1,325,251✔
704
            XMMReg4Double::NotEquals(pseudoPanchro, zero),
705
            XMMReg4Double::Load4Val(pPanBuffer + j) / pseudoPanchro);
766,135✔
706

707
        val0 = XMMReg4Double::Min(val0 * factor, maxValue);
1,315,049✔
708
        val1 = XMMReg4Double::Min(val1 * factor, maxValue);
1,320,425✔
709
        val2 = XMMReg4Double::Min(val2 * factor, maxValue);
1,321,267✔
710
        if constexpr (NOUTPUT == 4)
711
        {
712
            val3 = XMMReg4Double::Min(val3 * factor, maxValue);
255,170✔
713
        }
714
        val0.Store4Val(pDataBuf + 0 * nBandValues + j);
1,323,732✔
715
        val1.Store4Val(pDataBuf + 1 * nBandValues + j);
1,326,676✔
716
        val2.Store4Val(pDataBuf + 2 * nBandValues + j);
1,324,801✔
717
        if constexpr (NOUTPUT == 4)
718
        {
719
            val3.Store4Val(pDataBuf + 3 * nBandValues + j);
255,653✔
720
        }
721
    }
722
    return j;
41✔
723
}
724

725
#else
726

727
template <class T, int NINPUT, int NOUTPUT>
728
size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
729
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
730
    size_t nValues, size_t nBandValues, T nMaxValue) const
731
{
732
    static_assert(NINPUT == 3 || NINPUT == 4);
733
    const double dfw0 = psOptions->padfWeights[0];
734
    const double dfw1 = psOptions->padfWeights[1];
735
    const double dfw2 = psOptions->padfWeights[2];
736
    // cppcheck-suppress knownConditionTrueFalse
737
    [[maybe_unused]] const double dfw3 =
738
        (NINPUT == 3) ? 0 : psOptions->padfWeights[3];
739
    size_t j = 0;  // Used after for.
740
    for (; j + 1 < nValues; j += 2)
741
    {
742
        double dfFactor = 0.0;
743
        double dfFactor2 = 0.0;
744
        double dfPseudoPanchro = 0.0;
745
        double dfPseudoPanchro2 = 0.0;
746

747
        dfPseudoPanchro += dfw0 * pUpsampledSpectralBuffer[j];
748
        dfPseudoPanchro2 += dfw0 * pUpsampledSpectralBuffer[j + 1];
749

750
        dfPseudoPanchro += dfw1 * pUpsampledSpectralBuffer[nBandValues + j];
751
        dfPseudoPanchro2 +=
752
            dfw1 * pUpsampledSpectralBuffer[nBandValues + j + 1];
753

754
        dfPseudoPanchro += dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j];
755
        dfPseudoPanchro2 +=
756
            dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j + 1];
757

758
        if constexpr (NINPUT == 4)
759
        {
760
            dfPseudoPanchro +=
761
                dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j];
762
            dfPseudoPanchro2 +=
763
                dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j + 1];
764
        }
765

766
        dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
767
        dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
768

769
        for (int i = 0; i < NOUTPUT; i++)
770
        {
771
            T nRawValue = pUpsampledSpectralBuffer[i * nBandValues + j];
772
            double dfTmp = nRawValue * dfFactor;
773
            pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
774

775
            T nRawValue2 = pUpsampledSpectralBuffer[i * nBandValues + j + 1];
776
            double dfTmp2 = nRawValue2 * dfFactor2;
777
            pDataBuf[i * nBandValues + j + 1] =
778
                ClampAndRound(dfTmp2, nMaxValue);
779
        }
780
    }
781
    return j;
782
}
783
#endif
784

785
template <class T>
786
void GDALPansharpenOperation::WeightedBroveyPositiveWeights(
63✔
787
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
788
    size_t nValues, size_t nBandValues, T nMaxValue) const
789
{
790
    if (psOptions->bHasNoData)
63✔
791
    {
792
        WeightedBroveyWithNoData<T, T>(pPanBuffer, pUpsampledSpectralBuffer,
×
793
                                       pDataBuf, nValues, nBandValues,
794
                                       nMaxValue);
795
        return;
×
796
    }
797

798
    if (nMaxValue == 0)
63✔
799
        nMaxValue = cpl::NumericLimits<T>::max();
55✔
800
    size_t j;
801
    if (psOptions->nInputSpectralBands == 3 &&
59✔
802
        psOptions->nOutPansharpenedBands == 3 &&
33✔
803
        psOptions->panOutPansharpenedBands[0] == 0 &&
33✔
804
        psOptions->panOutPansharpenedBands[1] == 1 &&
33✔
805
        psOptions->panOutPansharpenedBands[2] == 2)
33✔
806
    {
807
        j = WeightedBroveyPositiveWeightsInternal<T, 3, 3>(
32✔
808
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
809
            nBandValues, nMaxValue);
810
    }
811
    else if (psOptions->nInputSpectralBands == 4 &&
27✔
812
             psOptions->nOutPansharpenedBands == 4 &&
8✔
813
             psOptions->panOutPansharpenedBands[0] == 0 &&
3✔
814
             psOptions->panOutPansharpenedBands[1] == 1 &&
3✔
815
             psOptions->panOutPansharpenedBands[2] == 2 &&
3✔
816
             psOptions->panOutPansharpenedBands[3] == 3)
3✔
817
    {
818
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 4>(
3✔
819
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
820
            nBandValues, nMaxValue);
821
    }
822
    else if (psOptions->nInputSpectralBands == 4 &&
24✔
823
             psOptions->nOutPansharpenedBands == 3 &&
3✔
824
             psOptions->panOutPansharpenedBands[0] == 0 &&
3✔
825
             psOptions->panOutPansharpenedBands[1] == 1 &&
3✔
826
             psOptions->panOutPansharpenedBands[2] == 2)
3✔
827
    {
828
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 3>(
3✔
829
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
830
            nBandValues, nMaxValue);
831
    }
832
    else
833
    {
834
        for (j = 0; j + 1 < nValues; j += 2)
402,052✔
835
        {
836
            double dfFactor = 0.0;
404,921✔
837
            double dfFactor2 = 0.0;
404,921✔
838
            double dfPseudoPanchro = 0.0;
404,921✔
839
            double dfPseudoPanchro2 = 0.0;
404,921✔
840
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1,092,458✔
841
            {
842
                dfPseudoPanchro +=
687,534✔
843
                    psOptions->padfWeights[i] *
687,534✔
844
                    pUpsampledSpectralBuffer[i * nBandValues + j];
687,534✔
845
                dfPseudoPanchro2 +=
687,534✔
846
                    psOptions->padfWeights[i] *
687,534✔
847
                    pUpsampledSpectralBuffer[i * nBandValues + j + 1];
687,534✔
848
            }
849

850
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
404,921✔
851
            dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
397,348✔
852

853
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
1,047,448✔
854
            {
855
                const T nRawValue = pUpsampledSpectralBuffer
645,419✔
856
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
645,419✔
857
                const double dfTmp = nRawValue * dfFactor;
645,419✔
858
                pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
645,419✔
859

860
                const T nRawValue2 = pUpsampledSpectralBuffer
580,537✔
861
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j +
580,537✔
862
                     1];
863
                const double dfTmp2 = nRawValue2 * dfFactor2;
580,537✔
864
                pDataBuf[i * nBandValues + j + 1] =
657,616✔
865
                    ClampAndRound(dfTmp2, nMaxValue);
580,537✔
866
            }
867
        }
868
    }
869
    for (; j < nValues; j++)
18✔
870
    {
871
        double dfFactor = 0.0;
13✔
872
        double dfPseudoPanchro = 0.0;
13✔
873
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
54✔
874
            dfPseudoPanchro += psOptions->padfWeights[i] *
41✔
875
                               pUpsampledSpectralBuffer[i * nBandValues + j];
41✔
876
        dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
13✔
877

878
        for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
53✔
879
        {
880
            T nRawValue =
40✔
881
                pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
40✔
882
                                             nBandValues +
40✔
883
                                         j];
884
            double dfTmp = nRawValue * dfFactor;
40✔
885
            pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
40✔
886
        }
887
    }
888
}
889

890
template <class WorkDataType, class OutDataType>
891
void GDALPansharpenOperation::WeightedBrovey(
152✔
892
    const WorkDataType *pPanBuffer,
893
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
894
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
895
{
896
    if (nMaxValue == 0)
152✔
897
        WeightedBrovey3<WorkDataType, OutDataType, FALSE>(
152✔
898
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
899
            nBandValues, 0);
900
    else
901
    {
UNCOV
902
        WeightedBrovey3<WorkDataType, OutDataType, TRUE>(
×
903
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
904
            nBandValues, nMaxValue);
905
    }
906
}
154✔
907

908
template <class T>
909
void GDALPansharpenOperation::WeightedBroveyGByteOrUInt16(
62✔
910
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
911
    size_t nValues, size_t nBandValues, T nMaxValue) const
912
{
913
    if (bPositiveWeights)
62✔
914
    {
915
        WeightedBroveyPositiveWeights(pPanBuffer, pUpsampledSpectralBuffer,
61✔
916
                                      pDataBuf, nValues, nBandValues,
917
                                      nMaxValue);
918
    }
919
    else if (nMaxValue == 0)
1✔
920
    {
921
        WeightedBrovey3<T, T, FALSE>(pPanBuffer, pUpsampledSpectralBuffer,
×
922
                                     pDataBuf, nValues, nBandValues, 0);
923
    }
924
    else
925
    {
926
        WeightedBrovey3<T, T, TRUE>(pPanBuffer, pUpsampledSpectralBuffer,
1✔
927
                                    pDataBuf, nValues, nBandValues, nMaxValue);
928
    }
929
}
63✔
930

931
template <>
932
void GDALPansharpenOperation::WeightedBrovey<GByte, GByte>(
48✔
933
    const GByte *pPanBuffer, const GByte *pUpsampledSpectralBuffer,
934
    GByte *pDataBuf, size_t nValues, size_t nBandValues, GByte nMaxValue) const
935
{
936
    WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
48✔
937
                                nValues, nBandValues, nMaxValue);
938
}
48✔
939

940
template <>
941
void GDALPansharpenOperation::WeightedBrovey<GUInt16, GUInt16>(
14✔
942
    const GUInt16 *pPanBuffer, const GUInt16 *pUpsampledSpectralBuffer,
943
    GUInt16 *pDataBuf, size_t nValues, size_t nBandValues,
944
    GUInt16 nMaxValue) const
945
{
946
    WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
14✔
947
                                nValues, nBandValues, nMaxValue);
948
}
15✔
949

950
template <class WorkDataType>
951
CPLErr GDALPansharpenOperation::WeightedBrovey(
215✔
952
    const WorkDataType *pPanBuffer,
953
    const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
954
    GDALDataType eBufDataType, size_t nValues, size_t nBandValues,
955
    WorkDataType nMaxValue) const
956
{
957
    switch (eBufDataType)
215✔
958
    {
959
        case GDT_Byte:
49✔
960
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
49✔
961
                           static_cast<GByte *>(pDataBuf), nValues, nBandValues,
962
                           nMaxValue);
963
            break;
49✔
964

965
        case GDT_UInt16:
16✔
966
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
16✔
967
                           static_cast<GUInt16 *>(pDataBuf), nValues,
968
                           nBandValues, nMaxValue);
969
            break;
16✔
970

971
#ifndef LIMIT_TYPES
972
        case GDT_Int8:
973
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
974
                           static_cast<GInt8 *>(pDataBuf), nValues, nBandValues,
975
                           nMaxValue);
976
            break;
977

978
        case GDT_Int16:
979
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
980
                           static_cast<GInt16 *>(pDataBuf), nValues,
981
                           nBandValues, nMaxValue);
982
            break;
983

984
        case GDT_UInt32:
985
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
986
                           static_cast<GUInt32 *>(pDataBuf), nValues,
987
                           nBandValues, nMaxValue);
988
            break;
989

990
        case GDT_Int32:
991
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
992
                           static_cast<GInt32 *>(pDataBuf), nValues,
993
                           nBandValues, nMaxValue);
994
            break;
995

996
        case GDT_UInt64:
997
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
998
                           static_cast<std::uint64_t *>(pDataBuf), nValues,
999
                           nBandValues, nMaxValue);
1000
            break;
1001

1002
        case GDT_Int64:
1003
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1004
                           static_cast<std::int64_t *>(pDataBuf), nValues,
1005
                           nBandValues, nMaxValue);
1006
            break;
1007

1008
        case GDT_Float16:
1009
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1010
                           static_cast<GFloat16 *>(pDataBuf), nValues,
1011
                           nBandValues, nMaxValue);
1012
            break;
1013

1014
        case GDT_Float32:
1015
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1016
                           static_cast<float *>(pDataBuf), nValues, nBandValues,
1017
                           nMaxValue);
1018
            break;
1019
#endif
1020

1021
        case GDT_Float64:
150✔
1022
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
150✔
1023
                           static_cast<double *>(pDataBuf), nValues,
1024
                           nBandValues, nMaxValue);
1025
            break;
152✔
1026

1027
        default:
×
1028
            CPLError(CE_Failure, CPLE_NotSupported,
×
1029
                     "eBufDataType not supported");
1030
            return CE_Failure;
×
1031
            break;
1032
    }
1033

1034
    return CE_None;
217✔
1035
}
1036

1037
template <class WorkDataType>
1038
CPLErr GDALPansharpenOperation::WeightedBrovey(
6✔
1039
    const WorkDataType *pPanBuffer,
1040
    const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
1041
    GDALDataType eBufDataType, size_t nValues, size_t nBandValues) const
1042
{
1043
    switch (eBufDataType)
6✔
1044
    {
1045
        case GDT_Byte:
6✔
1046
            WeightedBrovey3<WorkDataType, GByte, FALSE>(
6✔
1047
                pPanBuffer, pUpsampledSpectralBuffer,
1048
                static_cast<GByte *>(pDataBuf), nValues, nBandValues, 0);
1049
            break;
6✔
1050

1051
        case GDT_UInt16:
×
1052
            WeightedBrovey3<WorkDataType, GUInt16, FALSE>(
×
1053
                pPanBuffer, pUpsampledSpectralBuffer,
1054
                static_cast<GUInt16 *>(pDataBuf), nValues, nBandValues, 0);
1055
            break;
×
1056

1057
#ifndef LIMIT_TYPES
1058
        case GDT_Int8:
1059
            WeightedBrovey3<WorkDataType, GInt8, FALSE>(
1060
                pPanBuffer, pUpsampledSpectralBuffer,
1061
                static_cast<GInt8 *>(pDataBuf), nValues, nBandValues, 0);
1062
            break;
1063

1064
        case GDT_Int16:
1065
            WeightedBrovey3<WorkDataType, GInt16, FALSE>(
1066
                pPanBuffer, pUpsampledSpectralBuffer,
1067
                static_cast<GInt16 *>(pDataBuf), nValues, nBandValues, 0);
1068
            break;
1069

1070
        case GDT_UInt32:
1071
            WeightedBrovey3<WorkDataType, GUInt32, FALSE>(
1072
                pPanBuffer, pUpsampledSpectralBuffer,
1073
                static_cast<GUInt32 *>(pDataBuf), nValues, nBandValues, 0);
1074
            break;
1075

1076
        case GDT_Int32:
1077
            WeightedBrovey3<WorkDataType, GInt32, FALSE>(
1078
                pPanBuffer, pUpsampledSpectralBuffer,
1079
                static_cast<GInt32 *>(pDataBuf), nValues, nBandValues, 0);
1080
            break;
1081

1082
        case GDT_UInt64:
1083
            WeightedBrovey3<WorkDataType, std::uint64_t, FALSE>(
1084
                pPanBuffer, pUpsampledSpectralBuffer,
1085
                static_cast<std::uint64_t *>(pDataBuf), nValues, nBandValues,
1086
                0);
1087
            break;
1088

1089
        case GDT_Int64:
1090
            WeightedBrovey3<WorkDataType, std::int64_t, FALSE>(
1091
                pPanBuffer, pUpsampledSpectralBuffer,
1092
                static_cast<std::int64_t *>(pDataBuf), nValues, nBandValues, 0);
1093
            break;
1094

1095
        case GDT_Float16:
1096
            WeightedBrovey3<WorkDataType, GFloat16, FALSE>(
1097
                pPanBuffer, pUpsampledSpectralBuffer,
1098
                static_cast<GFloat16 *>(pDataBuf), nValues, nBandValues, 0);
1099
            break;
1100

1101
        case GDT_Float32:
1102
            WeightedBrovey3<WorkDataType, float, FALSE>(
1103
                pPanBuffer, pUpsampledSpectralBuffer,
1104
                static_cast<float *>(pDataBuf), nValues, nBandValues, 0);
1105
            break;
1106
#endif
1107

1108
        case GDT_Float64:
×
1109
            WeightedBrovey3<WorkDataType, double, FALSE>(
×
1110
                pPanBuffer, pUpsampledSpectralBuffer,
1111
                static_cast<double *>(pDataBuf), nValues, nBandValues, 0);
1112
            break;
×
1113

1114
        default:
×
1115
            CPLError(CE_Failure, CPLE_NotSupported,
×
1116
                     "eBufDataType not supported");
1117
            return CE_Failure;
×
1118
            break;
1119
    }
1120

1121
    return CE_None;
6✔
1122
}
1123

1124
/************************************************************************/
1125
/*                           ClampValues()                              */
1126
/************************************************************************/
1127

1128
template <class T>
1129
static void ClampValues(T *panBuffer, size_t nValues, T nMaxVal)
2✔
1130
{
1131
    for (size_t i = 0; i < nValues; i++)
34✔
1132
    {
1133
        if (panBuffer[i] > nMaxVal)
32✔
1134
            panBuffer[i] = nMaxVal;
8✔
1135
    }
1136
}
2✔
1137

1138
/************************************************************************/
1139
/*                         ProcessRegion()                              */
1140
/************************************************************************/
1141

1142
/** Executes a pansharpening operation on a rectangular region of the
1143
 * resulting dataset.
1144
 *
1145
 * The window is expressed with respect to the dimensions of the panchromatic
1146
 * band.
1147
 *
1148
 * Spectral bands are upsampled and merged with the panchromatic band according
1149
 * to the select algorithm and options.
1150
 *
1151
 * @param nXOff pixel offset.
1152
 * @param nYOff pixel offset.
1153
 * @param nXSize width of the pansharpened region to compute.
1154
 * @param nYSize height of the pansharpened region to compute.
1155
 * @param pDataBuf output buffer. Must be nXSize * nYSize *
1156
 *                 GDALGetDataTypeSizeBytes(eBufDataType) *
1157
 *                 psOptions->nOutPansharpenedBands large.
1158
 *                 It begins with all values of the first output band, followed
1159
 *                 by values of the second output band, etc...
1160
 * @param eBufDataType data type of the output buffer
1161
 *
1162
 * @return CE_None in case of success, CE_Failure in case of failure.
1163
 *
1164
 * @since GDAL 2.1
1165
 */
1166
CPLErr GDALPansharpenOperation::ProcessRegion(int nXOff, int nYOff, int nXSize,
91✔
1167
                                              int nYSize, void *pDataBuf,
1168
                                              GDALDataType eBufDataType)
1169
{
1170
    if (psOptions == nullptr)
91✔
1171
        return CE_Failure;
×
1172

1173
    // TODO: Avoid allocating buffers each time.
1174
    GDALRasterBand *poPanchroBand =
1175
        GDALRasterBand::FromHandle(psOptions->hPanchroBand);
91✔
1176
    GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
91✔
1177
#ifdef LIMIT_TYPES
1178
    if (eWorkDataType != GDT_Byte && eWorkDataType != GDT_UInt16)
91✔
1179
        eWorkDataType = GDT_Float64;
6✔
1180
#endif
1181
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eWorkDataType);
91✔
1182
    GByte *pUpsampledSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
91✔
1183
        nXSize, nYSize,
1184
        cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1185
    GByte *pPanBuffer = static_cast<GByte *>(
1186
        VSI_MALLOC3_VERBOSE(nXSize, nYSize, nDataTypeSize));
91✔
1187
    if (pUpsampledSpectralBuffer == nullptr || pPanBuffer == nullptr)
91✔
1188
    {
1189
        VSIFree(pUpsampledSpectralBuffer);
×
1190
        VSIFree(pPanBuffer);
×
1191
        return CE_Failure;
×
1192
    }
1193

1194
    CPLErr eErr = poPanchroBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
91✔
1195
                                          pPanBuffer, nXSize, nYSize,
1196
                                          eWorkDataType, 0, 0, nullptr);
1197
    if (eErr != CE_None)
91✔
1198
    {
1199
        VSIFree(pUpsampledSpectralBuffer);
×
1200
        VSIFree(pPanBuffer);
×
1201
        return CE_Failure;
×
1202
    }
1203

1204
    int nTasks = 0;
91✔
1205
    if (poThreadPool)
91✔
1206
    {
1207
        nTasks = poThreadPool->GetThreadCount();
44✔
1208
        if (nTasks > nYSize)
44✔
1209
            nTasks = nYSize;
×
1210
    }
1211

1212
    GDALRasterIOExtraArg sExtraArg;
1213
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
91✔
1214
    const GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
91✔
1215
    // cppcheck-suppress redundantAssignment
1216
    sExtraArg.eResampleAlg = eResampleAlg;
91✔
1217
    sExtraArg.bFloatingPointWindowValidity = TRUE;
91✔
1218
    sExtraArg.dfXOff = m_panToMSGT[0] + nXOff * m_panToMSGT[1];
91✔
1219
    sExtraArg.dfYOff = m_panToMSGT[3] + nYOff * m_panToMSGT[5];
91✔
1220
    sExtraArg.dfXSize = nXSize * m_panToMSGT[1];
91✔
1221
    sExtraArg.dfYSize = nYSize * m_panToMSGT[5];
91✔
1222
    if (sExtraArg.dfXOff + sExtraArg.dfXSize > aMSBands[0]->GetXSize())
91✔
1223
        sExtraArg.dfXSize = aMSBands[0]->GetXSize() - sExtraArg.dfXOff;
2✔
1224
    if (sExtraArg.dfYOff + sExtraArg.dfYSize > aMSBands[0]->GetYSize())
91✔
1225
        sExtraArg.dfYSize = aMSBands[0]->GetYSize() - sExtraArg.dfYOff;
2✔
1226
    int nSpectralXOff = static_cast<int>(sExtraArg.dfXOff);
91✔
1227
    int nSpectralYOff = static_cast<int>(sExtraArg.dfYOff);
91✔
1228
    int nSpectralXSize = static_cast<int>(0.49999 + sExtraArg.dfXSize);
91✔
1229
    int nSpectralYSize = static_cast<int>(0.49999 + sExtraArg.dfYSize);
91✔
1230
    if (nSpectralXOff + nSpectralXSize > aMSBands[0]->GetXSize())
91✔
1231
        nSpectralXSize = aMSBands[0]->GetXSize() - nSpectralXOff;
×
1232
    if (nSpectralYOff + nSpectralYSize > aMSBands[0]->GetYSize())
91✔
1233
        nSpectralYSize = aMSBands[0]->GetYSize() - nSpectralYOff;
×
1234
    if (nSpectralXSize == 0)
91✔
1235
        nSpectralXSize = 1;
10✔
1236
    if (nSpectralYSize == 0)
91✔
1237
        nSpectralYSize = 1;
10✔
1238

1239
    // When upsampling, extract the multispectral data at
1240
    // full resolution in a temp buffer, and then do the upsampling.
1241
    if (nSpectralXSize < nXSize && nSpectralYSize < nYSize &&
91✔
1242
        eResampleAlg != GRIORA_NearestNeighbour && nYSize > 1)
81✔
1243
    {
1244
        // Take some margin to take into account the radius of the
1245
        // resampling kernel.
1246
        int nXOffExtract = nSpectralXOff - nKernelRadius;
81✔
1247
        int nYOffExtract = nSpectralYOff - nKernelRadius;
81✔
1248
        int nXSizeExtract = nSpectralXSize + 1 + 2 * nKernelRadius;
81✔
1249
        int nYSizeExtract = nSpectralYSize + 1 + 2 * nKernelRadius;
81✔
1250
        if (nXOffExtract < 0)
81✔
1251
        {
1252
            nXSizeExtract += nXOffExtract;
74✔
1253
            nXOffExtract = 0;
74✔
1254
        }
1255
        if (nYOffExtract < 0)
81✔
1256
        {
1257
            nYSizeExtract += nYOffExtract;
67✔
1258
            nYOffExtract = 0;
67✔
1259
        }
1260
        if (nXOffExtract + nXSizeExtract > aMSBands[0]->GetXSize())
81✔
1261
            nXSizeExtract = aMSBands[0]->GetXSize() - nXOffExtract;
74✔
1262
        if (nYOffExtract + nYSizeExtract > aMSBands[0]->GetYSize())
81✔
1263
            nYSizeExtract = aMSBands[0]->GetYSize() - nYOffExtract;
67✔
1264

1265
        GByte *pSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
81✔
1266
            nXSizeExtract, nYSizeExtract,
1267
            cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1268
        if (pSpectralBuffer == nullptr)
81✔
1269
        {
1270
            VSIFree(pUpsampledSpectralBuffer);
×
1271
            VSIFree(pPanBuffer);
×
1272
            return CE_Failure;
×
1273
        }
1274

1275
        if (!anInputBands.empty())
81✔
1276
        {
1277
            // Use dataset RasterIO when possible.
1278
            eErr = aMSBands[0]->GetDataset()->RasterIO(
146✔
1279
                GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1280
                nYSizeExtract, pSpectralBuffer, nXSizeExtract, nYSizeExtract,
1281
                eWorkDataType, static_cast<int>(anInputBands.size()),
73✔
1282
                &anInputBands[0], 0, 0, 0, nullptr);
73✔
1283
        }
1284
        else
1285
        {
1286
            for (int i = 0;
8✔
1287
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
20✔
1288
            {
1289
                eErr = aMSBands[i]->RasterIO(
24✔
1290
                    GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1291
                    nYSizeExtract,
1292
                    pSpectralBuffer + static_cast<size_t>(i) * nXSizeExtract *
12✔
1293
                                          nYSizeExtract * nDataTypeSize,
12✔
1294
                    nXSizeExtract, nYSizeExtract, eWorkDataType, 0, 0, nullptr);
1295
            }
1296
        }
1297
        if (eErr != CE_None)
81✔
1298
        {
1299
            VSIFree(pSpectralBuffer);
×
1300
            VSIFree(pUpsampledSpectralBuffer);
×
1301
            VSIFree(pPanBuffer);
×
1302
            return CE_Failure;
×
1303
        }
1304

1305
        // Create a MEM dataset that wraps the input buffer.
1306
        auto poMEMDS = MEMDataset::Create("", nXSizeExtract, nYSizeExtract, 0,
81✔
1307
                                          eWorkDataType, nullptr);
1308

1309
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
322✔
1310
        {
1311
            GByte *pabyBuffer =
241✔
1312
                pSpectralBuffer + static_cast<size_t>(i) * nDataTypeSize *
241✔
1313
                                      nXSizeExtract * nYSizeExtract;
241✔
1314
            GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
241✔
1315
                poMEMDS, i + 1, pabyBuffer, eWorkDataType, 0, 0, false);
1316
            poMEMDS->AddMEMBand(hMEMBand);
241✔
1317

1318
            const char *pszNBITS =
1319
                aMSBands[i]->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
241✔
1320
            if (pszNBITS)
241✔
1321
                poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
4✔
1322
                    "NBITS", pszNBITS, "IMAGE_STRUCTURE");
4✔
1323

1324
            if (psOptions->bHasNoData)
241✔
1325
                poMEMDS->GetRasterBand(i + 1)->SetNoDataValue(
13✔
1326
                    psOptions->dfNoData);
13✔
1327
        }
1328

1329
        if (nTasks <= 1)
81✔
1330
        {
1331
            nSpectralXOff -= nXOffExtract;
37✔
1332
            nSpectralYOff -= nYOffExtract;
37✔
1333
            sExtraArg.dfXOff -= nXOffExtract;
37✔
1334
            sExtraArg.dfYOff -= nYOffExtract;
37✔
1335
            CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
37✔
1336
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1337
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1338
                eWorkDataType, psOptions->nInputSpectralBands, nullptr, 0, 0, 0,
37✔
1339
                &sExtraArg));
1340
        }
1341
        else
1342
        {
1343
            // We are abusing the contract of the GDAL API by using the
1344
            // MEMDataset from several threads. In this case, this is safe. In
1345
            // case, that would no longer be the case we could create as many
1346
            // MEMDataset as threads pointing to the same buffer.
1347

1348
            // To avoid races in threads, we query now the mask flags,
1349
            // so that implicit mask bands are created now.
1350
            for (int i = 0; i < poMEMDS->GetRasterCount(); i++)
190✔
1351
            {
1352
                poMEMDS->GetRasterBand(i + 1)->GetMaskFlags();
146✔
1353
            }
1354

1355
            std::vector<GDALPansharpenResampleJob> asJobs;
44✔
1356
            asJobs.resize(nTasks);
44✔
1357
            GDALPansharpenResampleJob *pasJobs = &(asJobs[0]);
44✔
1358
            {
1359
                std::vector<void *> ahJobData;
44✔
1360
                ahJobData.resize(nTasks);
44✔
1361

1362
#ifdef DEBUG_TIMING
1363
                struct timeval tv;
1364
#endif
1365
                for (int i = 0; i < nTasks; i++)
220✔
1366
                {
1367
                    const size_t iStartLine =
176✔
1368
                        (static_cast<size_t>(i) * nYSize) / nTasks;
176✔
1369
                    const size_t iNextStartLine =
176✔
1370
                        (static_cast<size_t>(i + 1) * nYSize) / nTasks;
176✔
1371
                    pasJobs[i].poMEMDS = poMEMDS;
176✔
1372
                    pasJobs[i].eResampleAlg = eResampleAlg;
176✔
1373
                    pasJobs[i].dfXOff = sExtraArg.dfXOff - nXOffExtract;
176✔
1374
                    pasJobs[i].dfYOff = m_panToMSGT[3] +
176✔
1375
                                        (nYOff + iStartLine) * m_panToMSGT[5] -
176✔
1376
                                        nYOffExtract;
1377
                    pasJobs[i].dfXSize = sExtraArg.dfXSize;
176✔
1378
                    pasJobs[i].dfYSize =
176✔
1379
                        (iNextStartLine - iStartLine) * m_panToMSGT[5];
176✔
1380
                    if (pasJobs[i].dfXOff + pasJobs[i].dfXSize > nXSizeExtract)
176✔
1381
                    {
1382
                        pasJobs[i].dfXSize = nYSizeExtract - pasJobs[i].dfXOff;
×
1383
                    }
1384
                    if (pasJobs[i].dfYOff + pasJobs[i].dfYSize >
352✔
1385
                        aMSBands[0]->GetYSize())
176✔
1386
                    {
1387
                        pasJobs[i].dfYSize =
×
1388
                            aMSBands[0]->GetYSize() - pasJobs[i].dfYOff;
×
1389
                    }
1390
                    pasJobs[i].nXOff = static_cast<int>(pasJobs[i].dfXOff);
176✔
1391
                    pasJobs[i].nYOff = static_cast<int>(pasJobs[i].dfYOff);
176✔
1392
                    pasJobs[i].nXSize =
176✔
1393
                        static_cast<int>(0.4999 + pasJobs[i].dfXSize);
176✔
1394
                    pasJobs[i].nYSize =
176✔
1395
                        static_cast<int>(0.4999 + pasJobs[i].dfYSize);
176✔
1396
                    if (pasJobs[i].nXOff + pasJobs[i].nXSize > nXSizeExtract)
176✔
1397
                    {
1398
                        pasJobs[i].nXSize = nXSizeExtract - pasJobs[i].nXOff;
×
1399
                    }
1400
                    if (pasJobs[i].nYOff + pasJobs[i].nYSize > nYSizeExtract)
176✔
1401
                    {
1402
                        pasJobs[i].nYSize = nYSizeExtract - pasJobs[i].nYOff;
×
1403
                    }
1404
                    if (pasJobs[i].nXSize == 0)
176✔
1405
                        pasJobs[i].nXSize = 1;
×
1406
                    if (pasJobs[i].nYSize == 0)
176✔
1407
                        pasJobs[i].nYSize = 1;
×
1408
                    pasJobs[i].pBuffer = pUpsampledSpectralBuffer +
176✔
1409
                                         static_cast<size_t>(iStartLine) *
176✔
1410
                                             nXSize * nDataTypeSize;
176✔
1411
                    pasJobs[i].eDT = eWorkDataType;
176✔
1412
                    pasJobs[i].nBufXSize = nXSize;
176✔
1413
                    pasJobs[i].nBufYSize =
176✔
1414
                        static_cast<int>(iNextStartLine - iStartLine);
176✔
1415
                    pasJobs[i].nBandCount = psOptions->nInputSpectralBands;
176✔
1416
                    pasJobs[i].nBandSpace =
176✔
1417
                        static_cast<GSpacing>(nXSize) * nYSize * nDataTypeSize;
176✔
1418
#ifdef DEBUG_TIMING
1419
                    pasJobs[i].ptv = &tv;
1420
#endif
1421
                    pasJobs[i].eErr = CE_Failure;
176✔
1422

1423
                    ahJobData[i] = &(pasJobs[i]);
176✔
1424
                }
1425
#ifdef DEBUG_TIMING
1426
                gettimeofday(&tv, nullptr);
1427
#endif
1428
                poThreadPool->SubmitJobs(PansharpenResampleJobThreadFunc,
44✔
1429
                                         ahJobData);
1430
                poThreadPool->WaitCompletion();
44✔
1431

1432
                for (int i = 0; i < nTasks; i++)
220✔
1433
                {
1434
                    if (pasJobs[i].eErr == CE_Failure)
176✔
1435
                    {
1436
                        CPLError(CE_Failure, CPLE_AppDefined, "%s",
×
1437
                                 pasJobs[i].osLastErrorMsg.c_str());
×
1438
                        GDALClose(poMEMDS);
×
1439
                        VSIFree(pSpectralBuffer);
×
1440
                        VSIFree(pUpsampledSpectralBuffer);
×
1441
                        VSIFree(pPanBuffer);
×
1442

1443
                        return CE_Failure;
×
1444
                    }
1445
                }
1446
            }
1447
        }
1448

1449
        GDALClose(poMEMDS);
81✔
1450

1451
        VSIFree(pSpectralBuffer);
81✔
1452
    }
1453
    else
1454
    {
1455
        if (!anInputBands.empty())
10✔
1456
        {
1457
            // Use dataset RasterIO when possible.
1458
            eErr = aMSBands[0]->GetDataset()->RasterIO(
20✔
1459
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1460
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1461
                eWorkDataType, static_cast<int>(anInputBands.size()),
10✔
1462
                &anInputBands[0], 0, 0, 0, &sExtraArg);
10✔
1463
        }
1464
        else
1465
        {
1466
            for (int i = 0;
×
1467
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
×
1468
            {
1469
                eErr = aMSBands[i]->RasterIO(
×
1470
                    GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1471
                    nSpectralYSize,
1472
                    pUpsampledSpectralBuffer + static_cast<size_t>(i) * nXSize *
×
1473
                                                   nYSize * nDataTypeSize,
×
1474
                    nXSize, nYSize, eWorkDataType, 0, 0, &sExtraArg);
1475
            }
1476
        }
1477
        if (eErr != CE_None)
10✔
1478
        {
1479
            VSIFree(pUpsampledSpectralBuffer);
×
1480
            VSIFree(pPanBuffer);
×
1481
            return CE_Failure;
×
1482
        }
1483
    }
1484

1485
    // In case NBITS was not set on the spectral bands, clamp the values
1486
    // if overshoot might have occurred.
1487
    int nBitDepth = psOptions->nBitDepth;
91✔
1488
    if (nBitDepth &&
91✔
1489
        (eResampleAlg == GRIORA_Cubic || eResampleAlg == GRIORA_CubicSpline ||
×
1490
         eResampleAlg == GRIORA_Lanczos))
1491
    {
1492
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
12✔
1493
        {
1494
            GDALRasterBand *poBand = aMSBands[i];
6✔
1495
            int nBandBitDepth = 0;
6✔
1496
            const char *pszNBITS =
1497
                poBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
6✔
1498
            if (pszNBITS)
6✔
1499
                nBandBitDepth = atoi(pszNBITS);
4✔
1500
            if (nBandBitDepth < nBitDepth)
6✔
1501
            {
1502
                if (eWorkDataType == GDT_Byte && nBitDepth >= 0 &&
2✔
1503
                    nBitDepth <= 8)
1504
                {
1505
                    ClampValues(
1✔
1506
                        reinterpret_cast<GByte *>(pUpsampledSpectralBuffer) +
1507
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1508
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1509
                        static_cast<GByte>((1 << nBitDepth) - 1));
1✔
1510
                }
1511
                else if (eWorkDataType == GDT_UInt16 && nBitDepth >= 0 &&
1✔
1512
                         nBitDepth <= 16)
1513
                {
1514
                    ClampValues(
1✔
1515
                        reinterpret_cast<GUInt16 *>(pUpsampledSpectralBuffer) +
1✔
1516
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1517
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1518
                        static_cast<GUInt16>((1 << nBitDepth) - 1));
1✔
1519
                }
1520
#ifndef LIMIT_TYPES
1521
                else if (eWorkDataType == GDT_UInt32)
1522
                {
1523
                    ClampValues(reinterpret_cast<GUInt32*>(pUpsampledSpectralBuffer) +
1524
                                static_cast<size_t>(i) * nXSize * nYSize,
1525
                                static_cast<size_t>(nXSize)*nYSize,
1526
                                (static_cast<GUInt32>((1 << nBitDepth)-1));
1527
                }
1528
#endif
1529
            }
1530
        }
1531
    }
1532

1533
    const GUInt32 nMaxValue = (nBitDepth >= 0 && nBitDepth <= 31)
91✔
1534
                                  ? (1U << nBitDepth) - 1
182✔
1535
                                  : UINT32_MAX;
1536

1537
    double *padfTempBuffer = nullptr;
91✔
1538
    GDALDataType eBufDataTypeOri = eBufDataType;
91✔
1539
    void *pDataBufOri = pDataBuf;
91✔
1540
    // CFloat64 is the query type used by gdallocationinfo...
1541
#ifdef LIMIT_TYPES
1542
    if (eBufDataType != GDT_Byte && eBufDataType != GDT_UInt16)
91✔
1543
#else
1544
    if (eBufDataType == GDT_CFloat64)
1545
#endif
1546
    {
1547
        padfTempBuffer = static_cast<double *>(VSI_MALLOC3_VERBOSE(
50✔
1548
            nXSize, nYSize, psOptions->nOutPansharpenedBands * sizeof(double)));
1549
        if (padfTempBuffer == nullptr)
50✔
1550
        {
1551
            VSIFree(pUpsampledSpectralBuffer);
×
1552
            VSIFree(pPanBuffer);
×
1553
            return CE_Failure;
×
1554
        }
1555
        pDataBuf = padfTempBuffer;
50✔
1556
        eBufDataType = GDT_Float64;
50✔
1557
    }
1558

1559
    if (nTasks > 1)
91✔
1560
    {
1561
        std::vector<GDALPansharpenJob> asJobs;
88✔
1562
        asJobs.resize(nTasks);
44✔
1563
        GDALPansharpenJob *pasJobs = &(asJobs[0]);
44✔
1564
        {
1565
            std::vector<void *> ahJobData;
88✔
1566
            ahJobData.resize(nTasks);
44✔
1567
#ifdef DEBUG_TIMING
1568
            struct timeval tv;
1569
#endif
1570
            for (int i = 0; i < nTasks; i++)
220✔
1571
            {
1572
                const size_t iStartLine =
176✔
1573
                    (static_cast<size_t>(i) * nYSize) / nTasks;
176✔
1574
                const size_t iNextStartLine =
176✔
1575
                    (static_cast<size_t>(i + 1) * nYSize) / nTasks;
176✔
1576
                pasJobs[i].poPansharpenOperation = this;
176✔
1577
                pasJobs[i].eWorkDataType = eWorkDataType;
176✔
1578
                pasJobs[i].eBufDataType = eBufDataType;
176✔
1579
                pasJobs[i].pPanBuffer =
176✔
1580
                    pPanBuffer + iStartLine * nXSize * nDataTypeSize;
176✔
1581
                pasJobs[i].pUpsampledSpectralBuffer =
176✔
1582
                    pUpsampledSpectralBuffer +
176✔
1583
                    iStartLine * nXSize * nDataTypeSize;
176✔
1584
                pasJobs[i].pDataBuf =
176✔
1585
                    static_cast<GByte *>(pDataBuf) +
176✔
1586
                    iStartLine * nXSize *
352✔
1587
                        GDALGetDataTypeSizeBytes(eBufDataType);
176✔
1588
                pasJobs[i].nValues = (iNextStartLine - iStartLine) * nXSize;
176✔
1589
                pasJobs[i].nBandValues = static_cast<size_t>(nXSize) * nYSize;
176✔
1590
                pasJobs[i].nMaxValue = nMaxValue;
176✔
1591
#ifdef DEBUG_TIMING
1592
                pasJobs[i].ptv = &tv;
1593
#endif
1594
                ahJobData[i] = &(pasJobs[i]);
176✔
1595
            }
1596
#ifdef DEBUG_TIMING
1597
            gettimeofday(&tv, nullptr);
1598
#endif
1599
            poThreadPool->SubmitJobs(PansharpenJobThreadFunc, ahJobData);
44✔
1600
            poThreadPool->WaitCompletion();
44✔
1601
        }
1602

1603
        eErr = CE_None;
44✔
1604
        for (int i = 0; i < nTasks; i++)
220✔
1605
        {
1606
            if (pasJobs[i].eErr != CE_None)
176✔
1607
                eErr = CE_Failure;
×
1608
        }
1609
    }
1610
    else
1611
    {
1612
        eErr = PansharpenChunk(eWorkDataType, eBufDataType, pPanBuffer,
47✔
1613
                               pUpsampledSpectralBuffer, pDataBuf,
1614
                               static_cast<size_t>(nXSize) * nYSize,
47✔
1615
                               static_cast<size_t>(nXSize) * nYSize, nMaxValue);
47✔
1616
    }
1617

1618
    if (padfTempBuffer)
91✔
1619
    {
1620
        GDALCopyWords64(padfTempBuffer, GDT_Float64, sizeof(double),
50✔
1621
                        pDataBufOri, eBufDataTypeOri,
1622
                        GDALGetDataTypeSizeBytes(eBufDataTypeOri),
1623
                        static_cast<size_t>(nXSize) * nYSize *
50✔
1624
                            psOptions->nOutPansharpenedBands);
50✔
1625
        VSIFree(padfTempBuffer);
50✔
1626
    }
1627

1628
    VSIFree(pUpsampledSpectralBuffer);
91✔
1629
    VSIFree(pPanBuffer);
91✔
1630

1631
    return eErr;
91✔
1632
}
1633

1634
/************************************************************************/
1635
/*                   PansharpenResampleJobThreadFunc()                  */
1636
/************************************************************************/
1637

1638
void GDALPansharpenOperation::PansharpenResampleJobThreadFunc(void *pUserData)
175✔
1639
{
1640
    GDALPansharpenResampleJob *psJob =
175✔
1641
        static_cast<GDALPansharpenResampleJob *>(pUserData);
1642

1643
#ifdef DEBUG_TIMING
1644
    struct timeval tv;
1645
    gettimeofday(&tv, nullptr);
1646
    const GIntBig launch_time =
1647
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1648
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1649
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1650
                              static_cast<GIntBig>(tv.tv_usec);
1651
#endif
1652

1653
    GDALRasterIOExtraArg sExtraArg;
1654
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
175✔
1655
    // cppcheck-suppress redundantAssignment
1656
    sExtraArg.eResampleAlg = psJob->eResampleAlg;
175✔
1657
    sExtraArg.bFloatingPointWindowValidity = TRUE;
175✔
1658
    sExtraArg.dfXOff = psJob->dfXOff;
175✔
1659
    sExtraArg.dfYOff = psJob->dfYOff;
175✔
1660
    sExtraArg.dfXSize = psJob->dfXSize;
175✔
1661
    sExtraArg.dfYSize = psJob->dfYSize;
175✔
1662

1663
    std::vector<int> anBands;
351✔
1664
    for (int i = 0; i < psJob->nBandCount; ++i)
753✔
1665
        anBands.push_back(i + 1);
578✔
1666
    // This call to RasterIO() in a thread to poMEMDS shared between several
1667
    // threads is really risky, but works given the implementation details...
1668
    // Do not do this at home!
1669
    psJob->eErr = psJob->poMEMDS->RasterIO(
172✔
1670
        GF_Read, psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
1671
        psJob->pBuffer, psJob->nBufXSize, psJob->nBufYSize, psJob->eDT,
1672
        psJob->nBandCount, anBands.data(), 0, 0, psJob->nBandSpace, &sExtraArg);
175✔
1673
    if (CPLGetLastErrorType() == CE_Failure)
176✔
1674
        psJob->osLastErrorMsg = CPLGetLastErrorMsg();
×
1675

1676
#ifdef DEBUG_TIMING
1677
    struct timeval tv_end;
1678
    gettimeofday(&tv_end, nullptr);
1679
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1680
                        static_cast<GIntBig>(tv_end.tv_usec);
1681
    if (start_job - launch_time > 500)
1682
        /*ok*/ printf("Resample: Delay before start=" CPL_FRMT_GIB
1683
                      ", completion time=" CPL_FRMT_GIB "\n",
1684
                      start_job - launch_time, end - start_job);
1685
#endif
1686
}
176✔
1687

1688
/************************************************************************/
1689
/*                      PansharpenJobThreadFunc()                       */
1690
/************************************************************************/
1691

1692
void GDALPansharpenOperation::PansharpenJobThreadFunc(void *pUserData)
175✔
1693
{
1694
    GDALPansharpenJob *psJob = static_cast<GDALPansharpenJob *>(pUserData);
175✔
1695

1696
#ifdef DEBUG_TIMING
1697
    struct timeval tv;
1698
    gettimeofday(&tv, nullptr);
1699
    const GIntBig launch_time =
1700
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1701
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1702
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1703
                              static_cast<GIntBig>(tv.tv_usec);
1704
#endif
1705

1706
#if 0
1707
    for( int i = 0; i < 1000000; i++ )
1708
        acc += i * i;
1709
    psJob->eErr = CE_None;
1710
#else
1711
    psJob->eErr = psJob->poPansharpenOperation->PansharpenChunk(
175✔
1712
        psJob->eWorkDataType, psJob->eBufDataType, psJob->pPanBuffer,
1713
        psJob->pUpsampledSpectralBuffer, psJob->pDataBuf, psJob->nValues,
1714
        psJob->nBandValues, psJob->nMaxValue);
1715
#endif
1716

1717
#ifdef DEBUG_TIMING
1718
    struct timeval tv_end;
1719
    gettimeofday(&tv_end, nullptr);
1720
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1721
                        static_cast<GIntBig>(tv_end.tv_usec);
1722
    if (start_job - launch_time > 500)
1723
        /*ok*/ printf("Pansharpen: Delay before start=" CPL_FRMT_GIB
1724
                      ", completion time=" CPL_FRMT_GIB "\n",
1725
                      start_job - launch_time, end - start_job);
1726
#endif
1727
}
176✔
1728

1729
/************************************************************************/
1730
/*                           PansharpenChunk()                          */
1731
/************************************************************************/
1732

1733
CPLErr GDALPansharpenOperation::PansharpenChunk(
222✔
1734
    GDALDataType eWorkDataType, GDALDataType eBufDataType,
1735
    const void *pPanBuffer, const void *pUpsampledSpectralBuffer,
1736
    void *pDataBuf, size_t nValues, size_t nBandValues, GUInt32 nMaxValue) const
1737
{
1738
    CPLErr eErr = CE_None;
222✔
1739

1740
    switch (eWorkDataType)
222✔
1741
    {
1742
        case GDT_Byte:
109✔
1743
            eErr = WeightedBrovey(
109✔
1744
                static_cast<const GByte *>(pPanBuffer),
1745
                static_cast<const GByte *>(pUpsampledSpectralBuffer), pDataBuf,
1746
                eBufDataType, nValues, nBandValues,
1747
                static_cast<GByte>(nMaxValue));
1748
            break;
109✔
1749

1750
        case GDT_UInt16:
107✔
1751
            eErr = WeightedBrovey(
107✔
1752
                static_cast<const GUInt16 *>(pPanBuffer),
1753
                static_cast<const GUInt16 *>(pUpsampledSpectralBuffer),
1754
                pDataBuf, eBufDataType, nValues, nBandValues,
1755
                static_cast<GUInt16>(nMaxValue));
1756
            break;
108✔
1757

1758
#ifndef LIMIT_TYPES
1759
        case GDT_Int8:
1760
            eErr = WeightedBrovey(
1761
                static_cast<const GInt8 *>(pPanBuffer),
1762
                static_cast<const GInt8 *>(pUpsampledSpectralBuffer), pDataBuf,
1763
                eBufDataType, nValues, nBandValues);
1764
            break;
1765

1766
        case GDT_Int16:
1767
            eErr = WeightedBrovey(
1768
                static_cast<const GInt16 *>(pPanBuffer),
1769
                static_cast<const GInt16 *>(pUpsampledSpectralBuffer), pDataBuf,
1770
                eBufDataType, nValues, nBandValues);
1771
            break;
1772

1773
        case GDT_UInt32:
1774
            eErr = WeightedBrovey(
1775
                static_cast<const GUInt32 *>(pPanBuffer),
1776
                static_cast<const GUInt32 *>(pUpsampledSpectralBuffer),
1777
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1778
            break;
1779

1780
        case GDT_Int32:
1781
            eErr = WeightedBrovey(
1782
                static_cast<const GInt32 *>(pPanBuffer),
1783
                static_cast<const GInt32 *>(pUpsampledSpectralBuffer), pDataBuf,
1784
                eBufDataType, nValues, nBandValues);
1785
            break;
1786

1787
        case GDT_UInt64:
1788
            eErr = WeightedBrovey(
1789
                static_cast<const std::uint64_t *>(pPanBuffer),
1790
                static_cast<const std::uint64_t *>(pUpsampledSpectralBuffer),
1791
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1792
            break;
1793

1794
        case GDT_Int64:
1795
            eErr = WeightedBrovey(
1796
                static_cast<const std::int64_t *>(pPanBuffer),
1797
                static_cast<const std::int64_t *>(pUpsampledSpectralBuffer),
1798
                pDataBuf, eBufDataType, nValues, nBandValues);
1799
            break;
1800

1801
        case GDT_Float16:
1802
            eErr = WeightedBrovey(
1803
                static_cast<const GFloat16 *>(pPanBuffer),
1804
                static_cast<const GFloat16 *>(pUpsampledSpectralBuffer),
1805
                pDataBuf, eBufDataType, nValues, nBandValues);
1806
            break;
1807

1808
        case GDT_Float32:
1809
            eErr = WeightedBrovey(
1810
                static_cast<const float *>(pPanBuffer),
1811
                static_cast<const float *>(pUpsampledSpectralBuffer), pDataBuf,
1812
                eBufDataType, nValues, nBandValues);
1813
            break;
1814
#endif
1815
        case GDT_Float64:
6✔
1816
            eErr = WeightedBrovey(
6✔
1817
                static_cast<const double *>(pPanBuffer),
1818
                static_cast<const double *>(pUpsampledSpectralBuffer), pDataBuf,
1819
                eBufDataType, nValues, nBandValues);
1820
            break;
6✔
1821

UNCOV
1822
        default:
×
UNCOV
1823
            CPLError(CE_Failure, CPLE_NotSupported,
×
1824
                     "eWorkDataType not supported");
1825
            eErr = CE_Failure;
×
1826
            break;
×
1827
    }
1828

1829
    return eErr;
223✔
1830
}
1831

1832
/************************************************************************/
1833
/*                             GetOptions()                             */
1834
/************************************************************************/
1835

1836
/** Return options.
1837
 * @return options.
1838
 */
1839
GDALPansharpenOptions *GDALPansharpenOperation::GetOptions()
151✔
1840
{
1841
    return psOptions;
151✔
1842
}
1843

1844
/************************************************************************/
1845
/*                     GDALCreatePansharpenOperation()                  */
1846
/************************************************************************/
1847

1848
/** Instantiate a pansharpening operation.
1849
 *
1850
 * The passed options are validated.
1851
 *
1852
 * @param psOptions a pansharpening option structure allocated with
1853
 * GDALCreatePansharpenOptions(). It is duplicated by this function.
1854
 * @return a valid pansharpening operation handle, or NULL in case of failure.
1855
 *
1856
 * @since GDAL 2.1
1857
 */
1858

1859
GDALPansharpenOperationH
1860
GDALCreatePansharpenOperation(const GDALPansharpenOptions *psOptions)
×
1861
{
1862
    GDALPansharpenOperation *psOperation = new GDALPansharpenOperation();
×
1863
    if (psOperation->Initialize(psOptions) == CE_None)
×
1864
        return reinterpret_cast<GDALPansharpenOperationH>(psOperation);
×
1865
    delete psOperation;
×
1866
    return nullptr;
×
1867
}
1868

1869
/************************************************************************/
1870
/*                     GDALDestroyPansharpenOperation()                 */
1871
/************************************************************************/
1872

1873
/** Destroy a pansharpening operation.
1874
 *
1875
 * @param hOperation a valid pansharpening operation.
1876
 *
1877
 * @since GDAL 2.1
1878
 */
1879

1880
void GDALDestroyPansharpenOperation(GDALPansharpenOperationH hOperation)
×
1881
{
1882
    delete reinterpret_cast<GDALPansharpenOperation *>(hOperation);
×
1883
}
×
1884

1885
/************************************************************************/
1886
/*                       GDALPansharpenProcessRegion()                  */
1887
/************************************************************************/
1888

1889
/** Executes a pansharpening operation on a rectangular region of the
1890
 * resulting dataset.
1891
 *
1892
 * The window is expressed with respect to the dimensions of the panchromatic
1893
 * band.
1894
 *
1895
 * Spectral bands are upsampled and merged with the panchromatic band according
1896
 * to the select algorithm and options.
1897
 *
1898
 * @param hOperation a valid pansharpening operation.
1899
 * @param nXOff pixel offset.
1900
 * @param nYOff pixel offset.
1901
 * @param nXSize width of the pansharpened region to compute.
1902
 * @param nYSize height of the pansharpened region to compute.
1903
 * @param pDataBuf output buffer. Must be nXSize * nYSize *
1904
 *                 GDALGetDataTypeSizeBytes(eBufDataType) *
1905
 *                 psOptions->nOutPansharpenedBands large.
1906
 *                 It begins with all values of the first output band, followed
1907
 *                 by values of the second output band, etc...
1908
 * @param eBufDataType data type of the output buffer
1909
 *
1910
 * @return CE_None in case of success, CE_Failure in case of failure.
1911
 *
1912
 * @since GDAL 2.1
1913
 */
1914
CPLErr GDALPansharpenProcessRegion(GDALPansharpenOperationH hOperation,
×
1915
                                   int nXOff, int nYOff, int nXSize, int nYSize,
1916
                                   void *pDataBuf, GDALDataType eBufDataType)
1917
{
1918
    return reinterpret_cast<GDALPansharpenOperation *>(hOperation)
1919
        ->ProcessRegion(nXOff, nYOff, nXSize, nYSize, pDataBuf, eBufDataType);
×
1920
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc