Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/vrt/vrtprocesseddatasetfunctions.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTProcessedDataset processing functions
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_float.h"
14
#include "cpl_minixml.h"
15
#include "cpl_string.h"
16
#include "gdal_cpp_functions.h"
17
#include "vrtdataset.h"
18
#include "vrtexpression.h"
19
20
#include <algorithm>
21
#include <functional>
22
#include <limits>
23
#include <map>
24
#include <optional>
25
#include <set>
26
#include <vector>
27
28
/************************************************************************/
29
/*                            GetDstValue()                             */
30
/************************************************************************/
31
32
/** Return a destination value given an initial value, the destination no data
33
 * value and its replacement value
34
 */
35
static inline double GetDstValue(double dfVal, double dfDstNoData,
36
                                 double dfReplacementDstNodata,
37
                                 GDALDataType eIntendedDstDT,
38
                                 bool bDstIntendedDTIsInteger)
39
0
{
40
0
    if (bDstIntendedDTIsInteger && std::round(dfVal) == dfDstNoData)
41
0
    {
42
0
        return dfReplacementDstNodata;
43
0
    }
44
0
    else if (eIntendedDstDT == GDT_Float16 &&
45
0
             static_cast<GFloat16>(dfVal) == static_cast<GFloat16>(dfDstNoData))
46
0
    {
47
0
        return dfReplacementDstNodata;
48
0
    }
49
0
    else if (eIntendedDstDT == GDT_Float32 &&
50
0
             static_cast<float>(dfVal) == static_cast<float>(dfDstNoData))
51
0
    {
52
0
        return dfReplacementDstNodata;
53
0
    }
54
0
    else if (eIntendedDstDT == GDT_Float64 && dfVal == dfDstNoData)
55
0
    {
56
0
        return dfReplacementDstNodata;
57
0
    }
58
0
    else
59
0
    {
60
0
        return dfVal;
61
0
    }
62
0
}
63
64
/************************************************************************/
65
/*                      BandAffineCombinationData                       */
66
/************************************************************************/
67
68
namespace
69
{
70
/** Working structure for 'BandAffineCombination' builtin function. */
71
struct BandAffineCombinationData
72
{
73
    static constexpr const char *const EXPECTED_SIGNATURE =
74
        "BandAffineCombination";
75
    //! Signature (to make sure callback functions are called with the right argument)
76
    const std::string m_osSignature = EXPECTED_SIGNATURE;
77
78
    /** Replacement nodata value */
79
    std::vector<double> m_adfReplacementDstNodata{};
80
81
    /** Intended destination data type. */
82
    GDALDataType m_eIntendedDstDT = GDT_Float64;
83
84
    /** Affine transformation coefficients.
85
     * m_aadfCoefficients[i][0] is the constant term for the i(th) dst band
86
     * m_aadfCoefficients[i][j] is the weight of the j(th) src band for the
87
     * i(th) dst vand.
88
     * Said otherwise dst[i] = m_aadfCoefficients[i][0] +
89
     *      sum(m_aadfCoefficients[i][j + 1] * src[j] for j in 0...nSrcBands-1)
90
     */
91
    std::vector<std::vector<double>> m_aadfCoefficients{};
92
93
    //! Minimum clamping value.
94
    double m_dfClampMin = std::numeric_limits<double>::quiet_NaN();
95
96
    //! Maximum clamping value.
97
    double m_dfClampMax = std::numeric_limits<double>::quiet_NaN();
98
};
99
}  // namespace
100
101
/************************************************************************/
102
/*               SetOutputValuesForInNoDataAndOutNoData()               */
103
/************************************************************************/
104
105
static std::vector<double> SetOutputValuesForInNoDataAndOutNoData(
106
    int nInBands, double *padfInNoData, int *pnOutBands,
107
    double **ppadfOutNoData, bool bSrcNodataSpecified, double dfSrcNoData,
108
    bool bDstNodataSpecified, double dfDstNoData, bool bIsFinalStep)
109
0
{
110
0
    if (bSrcNodataSpecified)
111
0
    {
112
0
        std::vector<double> adfNoData(nInBands, dfSrcNoData);
113
0
        memcpy(padfInNoData, adfNoData.data(),
114
0
               adfNoData.size() * sizeof(double));
115
0
    }
116
117
0
    std::vector<double> adfDstNoData;
118
0
    if (bDstNodataSpecified)
119
0
    {
120
0
        adfDstNoData.resize(*pnOutBands, dfDstNoData);
121
0
    }
122
0
    else if (bIsFinalStep)
123
0
    {
124
0
        adfDstNoData =
125
0
            std::vector<double>(*ppadfOutNoData, *ppadfOutNoData + *pnOutBands);
126
0
    }
127
0
    else
128
0
    {
129
0
        adfDstNoData =
130
0
            std::vector<double>(padfInNoData, padfInNoData + nInBands);
131
0
        adfDstNoData.resize(*pnOutBands, *padfInNoData);
132
0
    }
133
134
0
    if (*ppadfOutNoData == nullptr)
135
0
    {
136
0
        *ppadfOutNoData =
137
0
            static_cast<double *>(CPLMalloc(*pnOutBands * sizeof(double)));
138
0
    }
139
0
    memcpy(*ppadfOutNoData, adfDstNoData.data(), *pnOutBands * sizeof(double));
140
141
0
    return adfDstNoData;
142
0
}
143
144
/************************************************************************/
145
/*                     BandAffineCombinationInit()                      */
146
/************************************************************************/
147
148
/** Init function for 'BandAffineCombination' builtin function. */
149
static CPLErr BandAffineCombinationInit(
150
    const char * /*pszFuncName*/, void * /*pUserData*/,
151
    CSLConstList papszFunctionArgs, int nInBands, GDALDataType eInDT,
152
    double *padfInNoData, int *pnOutBands, GDALDataType *peOutDT,
153
    double **ppadfOutNoData, const char * /* pszVRTPath */,
154
    VRTPDWorkingDataPtr *ppWorkingData)
155
0
{
156
0
    CPLAssert(eInDT == GDT_Float64);
157
158
0
    *peOutDT = eInDT;
159
0
    *ppWorkingData = nullptr;
160
161
0
    auto data = std::make_unique<BandAffineCombinationData>();
162
163
0
    std::map<int, std::vector<double>> oMapCoefficients{};
164
0
    double dfSrcNoData = std::numeric_limits<double>::quiet_NaN();
165
0
    bool bSrcNodataSpecified = false;
166
0
    double dfDstNoData = std::numeric_limits<double>::quiet_NaN();
167
0
    bool bDstNodataSpecified = false;
168
0
    double dfReplacementDstNodata = std::numeric_limits<double>::quiet_NaN();
169
0
    bool bReplacementDstNodataSpecified = false;
170
171
0
    for (const auto &[pszKey, pszValue] :
172
0
         cpl::IterateNameValue(papszFunctionArgs))
173
0
    {
174
0
        if (EQUAL(pszKey, "src_nodata"))
175
0
        {
176
0
            bSrcNodataSpecified = true;
177
0
            dfSrcNoData = CPLAtof(pszValue);
178
0
        }
179
0
        else if (EQUAL(pszKey, "dst_nodata"))
180
0
        {
181
0
            bDstNodataSpecified = true;
182
0
            dfDstNoData = CPLAtof(pszValue);
183
0
        }
184
0
        else if (EQUAL(pszKey, "replacement_nodata"))
185
0
        {
186
0
            bReplacementDstNodataSpecified = true;
187
0
            dfReplacementDstNodata = CPLAtof(pszValue);
188
0
        }
189
0
        else if (EQUAL(pszKey, "dst_intended_datatype"))
190
0
        {
191
0
            for (GDALDataType eDT = GDT_UInt8; eDT < GDT_TypeCount;
192
0
                 eDT = static_cast<GDALDataType>(eDT + 1))
193
0
            {
194
0
                if (EQUAL(GDALGetDataTypeName(eDT), pszValue))
195
0
                {
196
0
                    data->m_eIntendedDstDT = eDT;
197
0
                    break;
198
0
                }
199
0
            }
200
0
        }
201
0
        else if (STARTS_WITH_CI(pszKey, "coefficients_"))
202
0
        {
203
0
            const int nTargetBand = atoi(pszKey + strlen("coefficients_"));
204
0
            if (nTargetBand <= 0 || nTargetBand > 65536)
205
0
            {
206
0
                CPLError(CE_Failure, CPLE_AppDefined,
207
0
                         "Invalid band in argument '%s'", pszKey);
208
0
                return CE_Failure;
209
0
            }
210
0
            const CPLStringList aosTokens(CSLTokenizeString2(pszValue, ",", 0));
211
0
            if (aosTokens.size() != 1 + nInBands)
212
0
            {
213
0
                CPLError(CE_Failure, CPLE_AppDefined,
214
0
                         "Argument %s has %d values, whereas %d are expected",
215
0
                         pszKey, aosTokens.size(), 1 + nInBands);
216
0
                return CE_Failure;
217
0
            }
218
0
            std::vector<double> adfValues;
219
0
            for (int i = 0; i < aosTokens.size(); ++i)
220
0
            {
221
0
                adfValues.push_back(CPLAtof(aosTokens[i]));
222
0
            }
223
0
            oMapCoefficients[nTargetBand - 1] = std::move(adfValues);
224
0
        }
225
0
        else if (EQUAL(pszKey, "min"))
226
0
        {
227
0
            data->m_dfClampMin = CPLAtof(pszValue);
228
0
        }
229
0
        else if (EQUAL(pszKey, "max"))
230
0
        {
231
0
            data->m_dfClampMax = CPLAtof(pszValue);
232
0
        }
233
0
        else
234
0
        {
235
0
            CPLError(CE_Warning, CPLE_AppDefined,
236
0
                     "Unrecognized argument name %s. Ignored", pszKey);
237
0
        }
238
0
    }
239
240
0
    const bool bIsFinalStep = *pnOutBands != 0;
241
0
    if (bIsFinalStep)
242
0
    {
243
0
        if (*pnOutBands != static_cast<int>(oMapCoefficients.size()))
244
0
        {
245
0
            CPLError(CE_Failure, CPLE_AppDefined,
246
0
                     "Final step expect %d bands, but only %d coefficient_XX "
247
0
                     "are provided",
248
0
                     *pnOutBands, static_cast<int>(oMapCoefficients.size()));
249
0
            return CE_Failure;
250
0
        }
251
0
    }
252
0
    else
253
0
    {
254
0
        *pnOutBands = static_cast<int>(oMapCoefficients.size());
255
0
    }
256
257
0
    const std::vector<double> adfDstNoData =
258
0
        SetOutputValuesForInNoDataAndOutNoData(
259
0
            nInBands, padfInNoData, pnOutBands, ppadfOutNoData,
260
0
            bSrcNodataSpecified, dfSrcNoData, bDstNodataSpecified, dfDstNoData,
261
0
            bIsFinalStep);
262
263
0
    if (bReplacementDstNodataSpecified)
264
0
    {
265
0
        data->m_adfReplacementDstNodata.resize(*pnOutBands,
266
0
                                               dfReplacementDstNodata);
267
0
    }
268
0
    else
269
0
    {
270
0
        for (double dfVal : adfDstNoData)
271
0
        {
272
0
            data->m_adfReplacementDstNodata.emplace_back(
273
0
                GDALGetNoDataReplacementValue(data->m_eIntendedDstDT, dfVal));
274
0
        }
275
0
    }
276
277
    // Check we have a set of coefficient for all output bands and
278
    // convert the map to a vector
279
0
    for (auto &oIter : oMapCoefficients)
280
0
    {
281
0
        const int iExpected = static_cast<int>(data->m_aadfCoefficients.size());
282
0
        if (oIter.first != iExpected)
283
0
        {
284
0
            CPLError(CE_Failure, CPLE_AppDefined,
285
0
                     "Argument coefficients_%d is missing", iExpected + 1);
286
0
            return CE_Failure;
287
0
        }
288
0
        data->m_aadfCoefficients.emplace_back(std::move(oIter.second));
289
0
    }
290
0
    *ppWorkingData = data.release();
291
0
    return CE_None;
292
0
}
293
294
/************************************************************************/
295
/*                     BandAffineCombinationFree()                      */
296
/************************************************************************/
297
298
/** Free function for 'BandAffineCombination' builtin function. */
299
static void BandAffineCombinationFree(const char * /*pszFuncName*/,
300
                                      void * /*pUserData*/,
301
                                      VRTPDWorkingDataPtr pWorkingData)
302
0
{
303
0
    BandAffineCombinationData *data =
304
0
        static_cast<BandAffineCombinationData *>(pWorkingData);
305
0
    CPLAssert(data->m_osSignature ==
306
0
              BandAffineCombinationData::EXPECTED_SIGNATURE);
307
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
308
0
    delete data;
309
0
}
310
311
/************************************************************************/
312
/*                    BandAffineCombinationProcess()                    */
313
/************************************************************************/
314
315
/** Processing function for 'BandAffineCombination' builtin function. */
316
static CPLErr BandAffineCombinationProcess(
317
    const char * /*pszFuncName*/, void * /*pUserData*/,
318
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs*/,
319
    int nBufXSize, int nBufYSize, const void *pInBuffer, size_t nInBufferSize,
320
    GDALDataType eInDT, int nInBands, const double *CPL_RESTRICT padfInNoData,
321
    void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT, int nOutBands,
322
    const double *CPL_RESTRICT padfOutNoData, double /*dfSrcXOff*/,
323
    double /*dfSrcYOff*/, double /*dfSrcXSize*/, double /*dfSrcYSize*/,
324
    const double /*adfSrcGT*/[], const char * /* pszVRTPath */,
325
    CSLConstList /*papszExtra*/)
326
0
{
327
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
328
329
0
    CPL_IGNORE_RET_VAL(eInDT);
330
0
    CPLAssert(eInDT == GDT_Float64);
331
0
    CPL_IGNORE_RET_VAL(eOutDT);
332
0
    CPLAssert(eOutDT == GDT_Float64);
333
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
334
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
335
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
336
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
337
338
0
    const BandAffineCombinationData *data =
339
0
        static_cast<BandAffineCombinationData *>(pWorkingData);
340
0
    CPLAssert(data->m_osSignature ==
341
0
              BandAffineCombinationData::EXPECTED_SIGNATURE);
342
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
343
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
344
0
    const bool bDstIntendedDTIsInteger =
345
0
        CPL_TO_BOOL(GDALDataTypeIsInteger(data->m_eIntendedDstDT));
346
0
    const double dfClampMin = data->m_dfClampMin;
347
0
    const double dfClampMax = data->m_dfClampMax;
348
0
    for (size_t i = 0; i < nElts; ++i)
349
0
    {
350
0
        for (int iDst = 0; iDst < nOutBands; ++iDst)
351
0
        {
352
0
            const auto &adfCoefficients = data->m_aadfCoefficients[iDst];
353
0
            double dfVal = adfCoefficients[0];
354
0
            bool bSetNoData = false;
355
0
            for (int iSrc = 0; iSrc < nInBands; ++iSrc)
356
0
            {
357
                // written this way to work with a NaN value
358
0
                if (!(padfSrc[iSrc] != padfInNoData[iSrc]))
359
0
                {
360
0
                    bSetNoData = true;
361
0
                    break;
362
0
                }
363
0
                dfVal += adfCoefficients[iSrc + 1] * padfSrc[iSrc];
364
0
            }
365
0
            if (bSetNoData)
366
0
            {
367
0
                *padfDst = padfOutNoData[iDst];
368
0
            }
369
0
            else
370
0
            {
371
0
                double dfDstVal = GetDstValue(
372
0
                    dfVal, padfOutNoData[iDst],
373
0
                    data->m_adfReplacementDstNodata[iDst],
374
0
                    data->m_eIntendedDstDT, bDstIntendedDTIsInteger);
375
0
                if (dfDstVal < dfClampMin)
376
0
                    dfDstVal = dfClampMin;
377
0
                if (dfDstVal > dfClampMax)
378
0
                    dfDstVal = dfClampMax;
379
0
                *padfDst = dfDstVal;
380
0
            }
381
0
            ++padfDst;
382
0
        }
383
0
        padfSrc += nInBands;
384
0
    }
385
386
0
    return CE_None;
387
0
}
388
389
/************************************************************************/
390
/*                               LUTData                                */
391
/************************************************************************/
392
393
namespace
394
{
395
/** Working structure for 'LUT' builtin function. */
396
struct LUTData
397
{
398
    static constexpr const char *const EXPECTED_SIGNATURE = "LUT";
399
    //! Signature (to make sure callback functions are called with the right argument)
400
    const std::string m_osSignature = EXPECTED_SIGNATURE;
401
402
    //! m_aadfLUTInputs[i][j] is the j(th) input value for that LUT of band i.
403
    std::vector<std::vector<double>> m_aadfLUTInputs{};
404
405
    //! m_aadfLUTOutputs[i][j] is the j(th) output value for that LUT of band i.
406
    std::vector<std::vector<double>> m_aadfLUTOutputs{};
407
408
    /************************************************************************/
409
    /*                              LookupValue()                           */
410
    /************************************************************************/
411
412
    double LookupValue(int iBand, double dfInput) const
413
0
    {
414
0
        const auto &adfInput = m_aadfLUTInputs[iBand];
415
0
        const auto &afdOutput = m_aadfLUTOutputs[iBand];
416
417
        // Find the index of the first element in the LUT input array that
418
        // is not smaller than the input value.
419
0
        int i = static_cast<int>(
420
0
            std::lower_bound(adfInput.data(), adfInput.data() + adfInput.size(),
421
0
                             dfInput) -
422
0
            adfInput.data());
423
424
0
        if (i == 0)
425
0
            return afdOutput[0];
426
427
        // If the index is beyond the end of the LUT input array, the input
428
        // value is larger than all the values in the array.
429
0
        if (i == static_cast<int>(adfInput.size()))
430
0
            return afdOutput.back();
431
432
0
        if (adfInput[i] == dfInput)
433
0
            return afdOutput[i];
434
435
        // Otherwise, interpolate.
436
0
        return afdOutput[i - 1] + (dfInput - adfInput[i - 1]) *
437
0
                                      ((afdOutput[i] - afdOutput[i - 1]) /
438
0
                                       (adfInput[i] - adfInput[i - 1]));
439
0
    }
440
};
441
}  // namespace
442
443
/************************************************************************/
444
/*                              LUTInit()                               */
445
/************************************************************************/
446
447
/** Init function for 'LUT' builtin function. */
448
static CPLErr LUTInit(const char * /*pszFuncName*/, void * /*pUserData*/,
449
                      CSLConstList papszFunctionArgs, int nInBands,
450
                      GDALDataType eInDT, double *padfInNoData, int *pnOutBands,
451
                      GDALDataType *peOutDT, double **ppadfOutNoData,
452
                      const char * /* pszVRTPath */,
453
                      VRTPDWorkingDataPtr *ppWorkingData)
454
0
{
455
0
    CPLAssert(eInDT == GDT_Float64);
456
457
0
    const bool bIsFinalStep = *pnOutBands != 0;
458
0
    *peOutDT = eInDT;
459
0
    *ppWorkingData = nullptr;
460
461
0
    if (bIsFinalStep)
462
0
    {
463
0
        if (*pnOutBands != nInBands)
464
0
        {
465
0
            CPLError(CE_Failure, CPLE_NotSupported,
466
0
                     "LUT step: input band count (%d) is different from output "
467
0
                     "band count (%d)",
468
0
                     nInBands, *pnOutBands);
469
0
            return CE_Failure;
470
0
        }
471
0
    }
472
0
    else
473
0
    {
474
0
        *pnOutBands = nInBands;
475
0
    }
476
477
0
    auto data = std::make_unique<LUTData>();
478
479
0
    double dfSrcNoData = std::numeric_limits<double>::quiet_NaN();
480
0
    bool bSrcNodataSpecified = false;
481
0
    double dfDstNoData = std::numeric_limits<double>::quiet_NaN();
482
0
    bool bDstNodataSpecified = false;
483
484
0
    std::map<int, std::pair<std::vector<double>, std::vector<double>>> oMap{};
485
486
0
    for (const auto &[pszKey, pszValue] :
487
0
         cpl::IterateNameValue(papszFunctionArgs))
488
0
    {
489
0
        if (EQUAL(pszKey, "src_nodata"))
490
0
        {
491
0
            bSrcNodataSpecified = true;
492
0
            dfSrcNoData = CPLAtof(pszValue);
493
0
        }
494
0
        else if (EQUAL(pszKey, "dst_nodata"))
495
0
        {
496
0
            bDstNodataSpecified = true;
497
0
            dfDstNoData = CPLAtof(pszValue);
498
0
        }
499
0
        else if (STARTS_WITH_CI(pszKey, "lut_"))
500
0
        {
501
0
            const int nBand = atoi(pszKey + strlen("lut_"));
502
0
            if (nBand <= 0 || nBand > nInBands)
503
0
            {
504
0
                CPLError(CE_Failure, CPLE_AppDefined,
505
0
                         "Invalid band in argument '%s'", pszKey);
506
0
                return CE_Failure;
507
0
            }
508
0
            const CPLStringList aosTokens(CSLTokenizeString2(pszValue, ",", 0));
509
0
            std::vector<double> adfInputValues;
510
0
            std::vector<double> adfOutputValues;
511
0
            for (int i = 0; i < aosTokens.size(); ++i)
512
0
            {
513
0
                const CPLStringList aosTokens2(
514
0
                    CSLTokenizeString2(aosTokens[i], ":", 0));
515
0
                if (aosTokens2.size() != 2)
516
0
                {
517
0
                    CPLError(CE_Failure, CPLE_AppDefined,
518
0
                             "Invalid value for argument '%s'", pszKey);
519
0
                    return CE_Failure;
520
0
                }
521
0
                adfInputValues.push_back(CPLAtof(aosTokens2[0]));
522
0
                adfOutputValues.push_back(CPLAtof(aosTokens2[1]));
523
0
            }
524
0
            oMap[nBand - 1] = std::pair(std::move(adfInputValues),
525
0
                                        std::move(adfOutputValues));
526
0
        }
527
0
        else
528
0
        {
529
0
            CPLError(CE_Warning, CPLE_AppDefined,
530
0
                     "Unrecognized argument name %s. Ignored", pszKey);
531
0
        }
532
0
    }
533
534
0
    SetOutputValuesForInNoDataAndOutNoData(
535
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bSrcNodataSpecified,
536
0
        dfSrcNoData, bDstNodataSpecified, dfDstNoData, bIsFinalStep);
537
538
0
    int iExpected = 0;
539
    // Check we have values for all bands and convert to vector
540
0
    for (auto &oIter : oMap)
541
0
    {
542
0
        if (oIter.first != iExpected)
543
0
        {
544
0
            CPLError(CE_Failure, CPLE_AppDefined, "Argument lut_%d is missing",
545
0
                     iExpected + 1);
546
0
            return CE_Failure;
547
0
        }
548
0
        ++iExpected;
549
0
        data->m_aadfLUTInputs.emplace_back(std::move(oIter.second.first));
550
0
        data->m_aadfLUTOutputs.emplace_back(std::move(oIter.second.second));
551
0
    }
552
553
0
    if (static_cast<int>(oMap.size()) < *pnOutBands)
554
0
    {
555
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing lut_XX element(s)");
556
0
        return CE_Failure;
557
0
    }
558
559
0
    *ppWorkingData = data.release();
560
0
    return CE_None;
561
0
}
562
563
/************************************************************************/
564
/*                              LUTFree()                               */
565
/************************************************************************/
566
567
/** Free function for 'LUT' builtin function. */
568
static void LUTFree(const char * /*pszFuncName*/, void * /*pUserData*/,
569
                    VRTPDWorkingDataPtr pWorkingData)
570
0
{
571
0
    LUTData *data = static_cast<LUTData *>(pWorkingData);
572
0
    CPLAssert(data->m_osSignature == LUTData::EXPECTED_SIGNATURE);
573
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
574
0
    delete data;
575
0
}
576
577
/************************************************************************/
578
/*                             LUTProcess()                             */
579
/************************************************************************/
580
581
/** Processing function for 'LUT' builtin function. */
582
static CPLErr
583
LUTProcess(const char * /*pszFuncName*/, void * /*pUserData*/,
584
           VRTPDWorkingDataPtr pWorkingData,
585
           CSLConstList /* papszFunctionArgs*/, int nBufXSize, int nBufYSize,
586
           const void *pInBuffer, size_t nInBufferSize, GDALDataType eInDT,
587
           int nInBands, const double *CPL_RESTRICT padfInNoData,
588
           void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT,
589
           int nOutBands, const double *CPL_RESTRICT padfOutNoData,
590
           double /*dfSrcXOff*/, double /*dfSrcYOff*/, double /*dfSrcXSize*/,
591
           double /*dfSrcYSize*/, const double /*adfSrcGT*/[],
592
           const char * /* pszVRTPath */, CSLConstList /*papszExtra*/)
593
0
{
594
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
595
596
0
    CPL_IGNORE_RET_VAL(eInDT);
597
0
    CPLAssert(eInDT == GDT_Float64);
598
0
    CPL_IGNORE_RET_VAL(eOutDT);
599
0
    CPLAssert(eOutDT == GDT_Float64);
600
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
601
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
602
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
603
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
604
0
    CPLAssert(nInBands == nOutBands);
605
0
    CPL_IGNORE_RET_VAL(nOutBands);
606
607
0
    const LUTData *data = static_cast<LUTData *>(pWorkingData);
608
0
    CPLAssert(data->m_osSignature == LUTData::EXPECTED_SIGNATURE);
609
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
610
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
611
0
    for (size_t i = 0; i < nElts; ++i)
612
0
    {
613
0
        for (int iBand = 0; iBand < nInBands; ++iBand)
614
0
        {
615
            // written this way to work with a NaN value
616
0
            if (!(*padfSrc != padfInNoData[iBand]))
617
0
                *padfDst = padfOutNoData[iBand];
618
0
            else
619
0
                *padfDst = data->LookupValue(iBand, *padfSrc);
620
0
            ++padfSrc;
621
0
            ++padfDst;
622
0
        }
623
0
    }
624
625
0
    return CE_None;
626
0
}
627
628
/************************************************************************/
629
/*                         LocalScaleOffsetData                         */
630
/************************************************************************/
631
632
namespace
633
{
634
/** Working structure for 'LocalScaleOffset' builtin function. */
635
struct LocalScaleOffsetData
636
{
637
    static constexpr const char *const EXPECTED_SIGNATURE = "LocalScaleOffset";
638
    //! Signature (to make sure callback functions are called with the right argument)
639
    const std::string m_osSignature = EXPECTED_SIGNATURE;
640
641
    //! Nodata value for gain dataset(s)
642
    double m_dfGainNodata = std::numeric_limits<double>::quiet_NaN();
643
644
    //! Nodata value for offset dataset(s)
645
    double m_dfOffsetNodata = std::numeric_limits<double>::quiet_NaN();
646
647
    //! Minimum clamping value.
648
    double m_dfClampMin = std::numeric_limits<double>::quiet_NaN();
649
650
    //! Maximum clamping value.
651
    double m_dfClampMax = std::numeric_limits<double>::quiet_NaN();
652
653
    //! Map from gain/offset dataset name to datasets
654
    std::map<std::string, std::unique_ptr<GDALDataset>> m_oDatasetMap{};
655
656
    //! Vector of size nInBands that point to the raster band from which to read gains.
657
    std::vector<GDALRasterBand *> m_oGainBands{};
658
659
    //! Vector of size nInBands that point to the raster band from which to read offsets.
660
    std::vector<GDALRasterBand *> m_oOffsetBands{};
661
662
    //! Working buffer that contain gain values.
663
    std::vector<VRTProcessedDataset::NoInitByte> m_abyGainBuffer{};
664
665
    //! Working buffer that contain offset values.
666
    std::vector<VRTProcessedDataset::NoInitByte> m_abyOffsetBuffer{};
667
};
668
}  // namespace
669
670
/************************************************************************/
671
/*                           CheckAllBands()                            */
672
/************************************************************************/
673
674
/** Return true if the key of oMap is the sequence of all integers between
675
 * 0 and nExpectedBandCount-1.
676
 */
677
template <class T>
678
static bool CheckAllBands(const std::map<int, T> &oMap, int nExpectedBandCount)
679
0
{
680
0
    int iExpected = 0;
681
0
    for (const auto &kv : oMap)
682
0
    {
683
0
        if (kv.first != iExpected)
684
0
            return false;
685
0
        ++iExpected;
686
0
    }
687
0
    return iExpected == nExpectedBandCount;
688
0
}
Unexecuted instantiation: vrtprocesseddatasetfunctions.cpp:bool CheckAllBands<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::map<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&, int)
Unexecuted instantiation: vrtprocesseddatasetfunctions.cpp:bool CheckAllBands<int>(std::__1::map<int, int, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, int> > > const&, int)
689
690
/************************************************************************/
691
/*                        LocalScaleOffsetInit()                        */
692
/************************************************************************/
693
694
/** Init function for 'LocalScaleOffset' builtin function. */
695
static CPLErr
696
LocalScaleOffsetInit(const char * /*pszFuncName*/, void * /*pUserData*/,
697
                     CSLConstList papszFunctionArgs, int nInBands,
698
                     GDALDataType eInDT, double *padfInNoData, int *pnOutBands,
699
                     GDALDataType *peOutDT, double **ppadfOutNoData,
700
                     const char *pszVRTPath, VRTPDWorkingDataPtr *ppWorkingData)
701
0
{
702
0
    CPLAssert(eInDT == GDT_Float64);
703
704
0
    const bool bIsFinalStep = *pnOutBands != 0;
705
0
    *peOutDT = eInDT;
706
0
    *ppWorkingData = nullptr;
707
708
0
    if (bIsFinalStep)
709
0
    {
710
0
        if (*pnOutBands != nInBands)
711
0
        {
712
0
            CPLError(CE_Failure, CPLE_NotSupported,
713
0
                     "LocalScaleOffset step: input band count (%d) is "
714
0
                     "different from output band count (%d)",
715
0
                     nInBands, *pnOutBands);
716
0
            return CE_Failure;
717
0
        }
718
0
    }
719
0
    else
720
0
    {
721
0
        *pnOutBands = nInBands;
722
0
    }
723
724
0
    auto data = std::make_unique<LocalScaleOffsetData>();
725
726
0
    bool bNodataSpecified = false;
727
0
    double dfNoData = std::numeric_limits<double>::quiet_NaN();
728
729
0
    bool bGainNodataSpecified = false;
730
0
    bool bOffsetNodataSpecified = false;
731
732
0
    std::map<int, std::string> oGainDatasetNameMap;
733
0
    std::map<int, int> oGainDatasetBandMap;
734
735
0
    std::map<int, std::string> oOffsetDatasetNameMap;
736
0
    std::map<int, int> oOffsetDatasetBandMap;
737
738
0
    bool bRelativeToVRT = false;
739
740
0
    for (const auto &[pszKey, pszValue] :
741
0
         cpl::IterateNameValue(papszFunctionArgs))
742
0
    {
743
0
        if (EQUAL(pszKey, "relativeToVRT"))
744
0
        {
745
0
            bRelativeToVRT = CPLTestBool(pszValue);
746
0
        }
747
0
        else if (EQUAL(pszKey, "nodata"))
748
0
        {
749
0
            bNodataSpecified = true;
750
0
            dfNoData = CPLAtof(pszValue);
751
0
        }
752
0
        else if (EQUAL(pszKey, "gain_nodata"))
753
0
        {
754
0
            bGainNodataSpecified = true;
755
0
            data->m_dfGainNodata = CPLAtof(pszValue);
756
0
        }
757
0
        else if (EQUAL(pszKey, "offset_nodata"))
758
0
        {
759
0
            bOffsetNodataSpecified = true;
760
0
            data->m_dfOffsetNodata = CPLAtof(pszValue);
761
0
        }
762
0
        else if (STARTS_WITH_CI(pszKey, "gain_dataset_filename_"))
763
0
        {
764
0
            const int nBand = atoi(pszKey + strlen("gain_dataset_filename_"));
765
0
            if (nBand <= 0 || nBand > nInBands)
766
0
            {
767
0
                CPLError(CE_Failure, CPLE_AppDefined,
768
0
                         "Invalid band in argument '%s'", pszKey);
769
0
                return CE_Failure;
770
0
            }
771
0
            oGainDatasetNameMap[nBand - 1] = pszValue;
772
0
        }
773
0
        else if (STARTS_WITH_CI(pszKey, "gain_dataset_band_"))
774
0
        {
775
0
            const int nBand = atoi(pszKey + strlen("gain_dataset_band_"));
776
0
            if (nBand <= 0 || nBand > nInBands)
777
0
            {
778
0
                CPLError(CE_Failure, CPLE_AppDefined,
779
0
                         "Invalid band in argument '%s'", pszKey);
780
0
                return CE_Failure;
781
0
            }
782
0
            oGainDatasetBandMap[nBand - 1] = atoi(pszValue);
783
0
        }
784
0
        else if (STARTS_WITH_CI(pszKey, "offset_dataset_filename_"))
785
0
        {
786
0
            const int nBand = atoi(pszKey + strlen("offset_dataset_filename_"));
787
0
            if (nBand <= 0 || nBand > nInBands)
788
0
            {
789
0
                CPLError(CE_Failure, CPLE_AppDefined,
790
0
                         "Invalid band in argument '%s'", pszKey);
791
0
                return CE_Failure;
792
0
            }
793
0
            oOffsetDatasetNameMap[nBand - 1] = pszValue;
794
0
        }
795
0
        else if (STARTS_WITH_CI(pszKey, "offset_dataset_band_"))
796
0
        {
797
0
            const int nBand = atoi(pszKey + strlen("offset_dataset_band_"));
798
0
            if (nBand <= 0 || nBand > nInBands)
799
0
            {
800
0
                CPLError(CE_Failure, CPLE_AppDefined,
801
0
                         "Invalid band in argument '%s'", pszKey);
802
0
                return CE_Failure;
803
0
            }
804
0
            oOffsetDatasetBandMap[nBand - 1] = atoi(pszValue);
805
0
        }
806
0
        else if (EQUAL(pszKey, "min"))
807
0
        {
808
0
            data->m_dfClampMin = CPLAtof(pszValue);
809
0
        }
810
0
        else if (EQUAL(pszKey, "max"))
811
0
        {
812
0
            data->m_dfClampMax = CPLAtof(pszValue);
813
0
        }
814
0
        else
815
0
        {
816
0
            CPLError(CE_Warning, CPLE_AppDefined,
817
0
                     "Unrecognized argument name %s. Ignored", pszKey);
818
0
        }
819
0
    }
820
821
0
    if (!CheckAllBands(oGainDatasetNameMap, nInBands))
822
0
    {
823
0
        CPLError(CE_Failure, CPLE_AppDefined,
824
0
                 "Missing gain_dataset_filename_XX element(s)");
825
0
        return CE_Failure;
826
0
    }
827
0
    if (!CheckAllBands(oGainDatasetBandMap, nInBands))
828
0
    {
829
0
        CPLError(CE_Failure, CPLE_AppDefined,
830
0
                 "Missing gain_dataset_band_XX element(s)");
831
0
        return CE_Failure;
832
0
    }
833
0
    if (!CheckAllBands(oOffsetDatasetNameMap, nInBands))
834
0
    {
835
0
        CPLError(CE_Failure, CPLE_AppDefined,
836
0
                 "Missing offset_dataset_filename_XX element(s)");
837
0
        return CE_Failure;
838
0
    }
839
0
    if (!CheckAllBands(oOffsetDatasetBandMap, nInBands))
840
0
    {
841
0
        CPLError(CE_Failure, CPLE_AppDefined,
842
0
                 "Missing offset_dataset_band_XX element(s)");
843
0
        return CE_Failure;
844
0
    }
845
846
0
    data->m_oGainBands.resize(nInBands);
847
0
    data->m_oOffsetBands.resize(nInBands);
848
849
0
    constexpr int IDX_GAIN = 0;
850
0
    constexpr int IDX_OFFSET = 1;
851
0
    for (int i : {IDX_GAIN, IDX_OFFSET})
852
0
    {
853
0
        const auto &oMapNames =
854
0
            (i == IDX_GAIN) ? oGainDatasetNameMap : oOffsetDatasetNameMap;
855
0
        const auto &oMapBands =
856
0
            (i == IDX_GAIN) ? oGainDatasetBandMap : oOffsetDatasetBandMap;
857
0
        for (const auto &kv : oMapNames)
858
0
        {
859
0
            const int nInBandIdx = kv.first;
860
0
            const auto osFilename = GDALDataset::BuildFilename(
861
0
                kv.second.c_str(), pszVRTPath, bRelativeToVRT);
862
0
            auto oIter = data->m_oDatasetMap.find(osFilename);
863
0
            if (oIter == data->m_oDatasetMap.end())
864
0
            {
865
0
                auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
866
0
                    osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
867
0
                    nullptr, nullptr, nullptr));
868
0
                if (!poDS)
869
0
                    return CE_Failure;
870
0
                GDALGeoTransform auxGT;
871
0
                if (poDS->GetGeoTransform(auxGT) != CE_None)
872
0
                {
873
0
                    CPLError(CE_Failure, CPLE_AppDefined,
874
0
                             "%s lacks a geotransform", osFilename.c_str());
875
0
                    return CE_Failure;
876
0
                }
877
0
                oIter = data->m_oDatasetMap
878
0
                            .insert(std::pair(osFilename, std::move(poDS)))
879
0
                            .first;
880
0
            }
881
0
            auto poDS = oIter->second.get();
882
0
            const auto oIterBand = oMapBands.find(nInBandIdx);
883
0
            CPLAssert(oIterBand != oMapBands.end());
884
0
            const int nAuxBand = oIterBand->second;
885
0
            if (nAuxBand <= 0 || nAuxBand > poDS->GetRasterCount())
886
0
            {
887
0
                CPLError(CE_Failure, CPLE_AppDefined,
888
0
                         "Invalid band number (%d) for a %s dataset", nAuxBand,
889
0
                         (i == IDX_GAIN) ? "gain" : "offset");
890
0
                return CE_Failure;
891
0
            }
892
0
            auto poAuxBand = poDS->GetRasterBand(nAuxBand);
893
0
            int bAuxBandHasNoData = false;
894
0
            const double dfAuxNoData =
895
0
                poAuxBand->GetNoDataValue(&bAuxBandHasNoData);
896
0
            if (i == IDX_GAIN)
897
0
            {
898
0
                data->m_oGainBands[nInBandIdx] = poAuxBand;
899
0
                if (!bGainNodataSpecified && bAuxBandHasNoData)
900
0
                    data->m_dfGainNodata = dfAuxNoData;
901
0
            }
902
0
            else
903
0
            {
904
0
                data->m_oOffsetBands[nInBandIdx] = poAuxBand;
905
0
                if (!bOffsetNodataSpecified && bAuxBandHasNoData)
906
0
                    data->m_dfOffsetNodata = dfAuxNoData;
907
0
            }
908
0
        }
909
0
    }
910
911
0
    SetOutputValuesForInNoDataAndOutNoData(
912
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bNodataSpecified,
913
0
        dfNoData, bNodataSpecified, dfNoData, bIsFinalStep);
914
915
0
    *ppWorkingData = data.release();
916
0
    return CE_None;
917
0
}
918
919
/************************************************************************/
920
/*                        LocalScaleOffsetFree()                        */
921
/************************************************************************/
922
923
/** Free function for 'LocalScaleOffset' builtin function. */
924
static void LocalScaleOffsetFree(const char * /*pszFuncName*/,
925
                                 void * /*pUserData*/,
926
                                 VRTPDWorkingDataPtr pWorkingData)
927
0
{
928
0
    LocalScaleOffsetData *data =
929
0
        static_cast<LocalScaleOffsetData *>(pWorkingData);
930
0
    CPLAssert(data->m_osSignature == LocalScaleOffsetData::EXPECTED_SIGNATURE);
931
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
932
0
    delete data;
933
0
}
934
935
/************************************************************************/
936
/*                            LoadAuxData()                             */
937
/************************************************************************/
938
939
// Load auxiliary corresponding offset, gain or trimming data.
940
static bool LoadAuxData(double dfULX, double dfULY, double dfLRX, double dfLRY,
941
                        size_t nElts, int nBufXSize, int nBufYSize,
942
                        const char *pszAuxType, GDALRasterBand *poAuxBand,
943
                        std::vector<VRTProcessedDataset::NoInitByte> &abyBuffer)
944
0
{
945
0
    GDALGeoTransform auxGT, auxInvGT;
946
947
    // Compute pixel/line coordinates from the georeferenced extent
948
0
    CPL_IGNORE_RET_VAL(poAuxBand->GetDataset()->GetGeoTransform(
949
0
        auxGT));  // return code already tested
950
0
    CPL_IGNORE_RET_VAL(auxGT.GetInverse(auxInvGT));
951
0
    const double dfULPixel =
952
0
        auxInvGT[0] + auxInvGT[1] * dfULX + auxInvGT[2] * dfULY;
953
0
    const double dfULLine =
954
0
        auxInvGT[3] + auxInvGT[4] * dfULX + auxInvGT[5] * dfULY;
955
0
    const double dfLRPixel =
956
0
        auxInvGT[0] + auxInvGT[1] * dfLRX + auxInvGT[2] * dfLRY;
957
0
    const double dfLRLine =
958
0
        auxInvGT[3] + auxInvGT[4] * dfLRX + auxInvGT[5] * dfLRY;
959
0
    if (dfULPixel >= dfLRPixel || dfULLine >= dfLRLine)
960
0
    {
961
0
        CPLError(CE_Failure, CPLE_AppDefined,
962
0
                 "Unexpected computed %s pixel/line", pszAuxType);
963
0
        return false;
964
0
    }
965
0
    if (dfULPixel < -1 || dfULLine < -1)
966
0
    {
967
0
        CPLError(CE_Failure, CPLE_AppDefined,
968
0
                 "Unexpected computed %s upper left (pixel,line)=(%f,%f)",
969
0
                 pszAuxType, dfULPixel, dfULLine);
970
0
        return false;
971
0
    }
972
0
    if (dfLRPixel > poAuxBand->GetXSize() + 1 ||
973
0
        dfLRLine > poAuxBand->GetYSize() + 1)
974
0
    {
975
0
        CPLError(CE_Failure, CPLE_AppDefined,
976
0
                 "Unexpected computed %s lower right (pixel,line)=(%f,%f)",
977
0
                 pszAuxType, dfLRPixel, dfLRLine);
978
0
        return false;
979
0
    }
980
981
0
    const int nAuxXOff = std::clamp(static_cast<int>(std::round(dfULPixel)), 0,
982
0
                                    poAuxBand->GetXSize() - 1);
983
0
    const int nAuxYOff = std::clamp(static_cast<int>(std::round(dfULLine)), 0,
984
0
                                    poAuxBand->GetYSize() - 1);
985
0
    const int nAuxX2Off = std::min(poAuxBand->GetXSize(),
986
0
                                   static_cast<int>(std::round(dfLRPixel)));
987
0
    const int nAuxY2Off =
988
0
        std::min(poAuxBand->GetYSize(), static_cast<int>(std::round(dfLRLine)));
989
990
0
    try
991
0
    {
992
0
        abyBuffer.resize(nElts * sizeof(float));
993
0
    }
994
0
    catch (const std::bad_alloc &)
995
0
    {
996
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
997
0
                 "Out of memory allocating working buffer");
998
0
        return false;
999
0
    }
1000
0
    GDALRasterIOExtraArg sExtraArg;
1001
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1002
0
    sExtraArg.bFloatingPointWindowValidity = true;
1003
0
    CPL_IGNORE_RET_VAL(sExtraArg.eResampleAlg);
1004
0
    sExtraArg.eResampleAlg = GRIORA_Bilinear;
1005
0
    sExtraArg.dfXOff = std::max(0.0, dfULPixel);
1006
0
    sExtraArg.dfYOff = std::max(0.0, dfULLine);
1007
0
    sExtraArg.dfXSize = std::min<double>(poAuxBand->GetXSize(), dfLRPixel) -
1008
0
                        std::max(0.0, dfULPixel);
1009
0
    sExtraArg.dfYSize = std::min<double>(poAuxBand->GetYSize(), dfLRLine) -
1010
0
                        std::max(0.0, dfULLine);
1011
0
    return (poAuxBand->RasterIO(
1012
0
                GF_Read, nAuxXOff, nAuxYOff, std::max(1, nAuxX2Off - nAuxXOff),
1013
0
                std::max(1, nAuxY2Off - nAuxYOff), abyBuffer.data(), nBufXSize,
1014
0
                nBufYSize, GDT_Float32, 0, 0, &sExtraArg) == CE_None);
1015
0
}
1016
1017
/************************************************************************/
1018
/*                      LocalScaleOffsetProcess()                       */
1019
/************************************************************************/
1020
1021
/** Processing function for 'LocalScaleOffset' builtin function. */
1022
static CPLErr LocalScaleOffsetProcess(
1023
    const char * /*pszFuncName*/, void * /*pUserData*/,
1024
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs*/,
1025
    int nBufXSize, int nBufYSize, const void *pInBuffer, size_t nInBufferSize,
1026
    GDALDataType eInDT, int nInBands, const double *CPL_RESTRICT padfInNoData,
1027
    void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT, int nOutBands,
1028
    const double *CPL_RESTRICT padfOutNoData, double dfSrcXOff,
1029
    double dfSrcYOff, double dfSrcXSize, double dfSrcYSize,
1030
    const double adfSrcGT[], const char * /* pszVRTPath */,
1031
    CSLConstList /*papszExtra*/)
1032
0
{
1033
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1034
1035
0
    CPL_IGNORE_RET_VAL(eInDT);
1036
0
    CPLAssert(eInDT == GDT_Float64);
1037
0
    CPL_IGNORE_RET_VAL(eOutDT);
1038
0
    CPLAssert(eOutDT == GDT_Float64);
1039
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
1040
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
1041
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
1042
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
1043
0
    CPLAssert(nInBands == nOutBands);
1044
0
    CPL_IGNORE_RET_VAL(nOutBands);
1045
1046
0
    LocalScaleOffsetData *data =
1047
0
        static_cast<LocalScaleOffsetData *>(pWorkingData);
1048
0
    CPLAssert(data->m_osSignature == LocalScaleOffsetData::EXPECTED_SIGNATURE);
1049
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1050
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1051
1052
    // Compute georeferenced extent of input region
1053
0
    const double dfULX =
1054
0
        adfSrcGT[0] + adfSrcGT[1] * dfSrcXOff + adfSrcGT[2] * dfSrcYOff;
1055
0
    const double dfULY =
1056
0
        adfSrcGT[3] + adfSrcGT[4] * dfSrcXOff + adfSrcGT[5] * dfSrcYOff;
1057
0
    const double dfLRX = adfSrcGT[0] + adfSrcGT[1] * (dfSrcXOff + dfSrcXSize) +
1058
0
                         adfSrcGT[2] * (dfSrcYOff + dfSrcYSize);
1059
0
    const double dfLRY = adfSrcGT[3] + adfSrcGT[4] * (dfSrcXOff + dfSrcXSize) +
1060
0
                         adfSrcGT[5] * (dfSrcYOff + dfSrcYSize);
1061
1062
0
    auto &abyOffsetBuffer = data->m_abyGainBuffer;
1063
0
    auto &abyGainBuffer = data->m_abyOffsetBuffer;
1064
1065
0
    for (int iBand = 0; iBand < nInBands; ++iBand)
1066
0
    {
1067
0
        if (!LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize,
1068
0
                         nBufYSize, "gain", data->m_oGainBands[iBand],
1069
0
                         abyGainBuffer) ||
1070
0
            !LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize,
1071
0
                         nBufYSize, "offset", data->m_oOffsetBands[iBand],
1072
0
                         abyOffsetBuffer))
1073
0
        {
1074
0
            return CE_Failure;
1075
0
        }
1076
1077
0
        const double *CPL_RESTRICT padfSrcThisBand = padfSrc + iBand;
1078
0
        double *CPL_RESTRICT padfDstThisBand = padfDst + iBand;
1079
0
        const float *pafGain =
1080
0
            reinterpret_cast<const float *>(abyGainBuffer.data());
1081
0
        const float *pafOffset =
1082
0
            reinterpret_cast<const float *>(abyOffsetBuffer.data());
1083
0
        const double dfSrcNodata = padfInNoData[iBand];
1084
0
        const double dfDstNodata = padfOutNoData[iBand];
1085
0
        const double dfGainNodata = data->m_dfGainNodata;
1086
0
        const double dfOffsetNodata = data->m_dfOffsetNodata;
1087
0
        const double dfClampMin = data->m_dfClampMin;
1088
0
        const double dfClampMax = data->m_dfClampMax;
1089
0
        for (size_t i = 0; i < nElts; ++i)
1090
0
        {
1091
0
            const double dfSrcVal = *padfSrcThisBand;
1092
            // written this way to work with a NaN value
1093
0
            if (!(dfSrcVal != dfSrcNodata))
1094
0
            {
1095
0
                *padfDstThisBand = dfDstNodata;
1096
0
            }
1097
0
            else
1098
0
            {
1099
0
                const double dfGain = pafGain[i];
1100
0
                const double dfOffset = pafOffset[i];
1101
0
                if (!(dfGain != dfGainNodata) || !(dfOffset != dfOffsetNodata))
1102
0
                {
1103
0
                    *padfDstThisBand = dfDstNodata;
1104
0
                }
1105
0
                else
1106
0
                {
1107
0
                    double dfUnscaled = dfSrcVal * dfGain - dfOffset;
1108
0
                    if (dfUnscaled < dfClampMin)
1109
0
                        dfUnscaled = dfClampMin;
1110
0
                    if (dfUnscaled > dfClampMax)
1111
0
                        dfUnscaled = dfClampMax;
1112
1113
0
                    *padfDstThisBand = dfUnscaled;
1114
0
                }
1115
0
            }
1116
0
            padfSrcThisBand += nInBands;
1117
0
            padfDstThisBand += nInBands;
1118
0
        }
1119
0
    }
1120
1121
0
    return CE_None;
1122
0
}
1123
1124
/************************************************************************/
1125
/*                             TrimmingData                             */
1126
/************************************************************************/
1127
1128
namespace
1129
{
1130
/** Working structure for 'Trimming' builtin function. */
1131
struct TrimmingData
1132
{
1133
    static constexpr const char *const EXPECTED_SIGNATURE = "Trimming";
1134
    //! Signature (to make sure callback functions are called with the right argument)
1135
    const std::string m_osSignature = EXPECTED_SIGNATURE;
1136
1137
    //! Nodata value for trimming dataset
1138
    double m_dfTrimmingNodata = std::numeric_limits<double>::quiet_NaN();
1139
1140
    //! Maximum saturating RGB output value.
1141
    double m_dfTopRGB = 0;
1142
1143
    //! Maximum threshold beyond which we give up saturation
1144
    double m_dfToneCeil = 0;
1145
1146
    //! Margin to allow for dynamics in brightest areas (in [0,1] range)
1147
    double m_dfTopMargin = 0;
1148
1149
    //! Index (zero-based) of input/output red band.
1150
    int m_nRedBand = 1 - 1;
1151
1152
    //! Index (zero-based) of input/output green band.
1153
    int m_nGreenBand = 2 - 1;
1154
1155
    //! Index (zero-based) of input/output blue band.
1156
    int m_nBlueBand = 3 - 1;
1157
1158
    //! Trimming dataset
1159
    std::unique_ptr<GDALDataset> m_poTrimmingDS{};
1160
1161
    //! Trimming raster band.
1162
    GDALRasterBand *m_poTrimmingBand = nullptr;
1163
1164
    //! Working buffer that contain trimming values.
1165
    std::vector<VRTProcessedDataset::NoInitByte> m_abyTrimmingBuffer{};
1166
};
1167
}  // namespace
1168
1169
/************************************************************************/
1170
/*                            TrimmingInit()                            */
1171
/************************************************************************/
1172
1173
/** Init function for 'Trimming' builtin function. */
1174
static CPLErr TrimmingInit(const char * /*pszFuncName*/, void * /*pUserData*/,
1175
                           CSLConstList papszFunctionArgs, int nInBands,
1176
                           GDALDataType eInDT, double *padfInNoData,
1177
                           int *pnOutBands, GDALDataType *peOutDT,
1178
                           double **ppadfOutNoData, const char *pszVRTPath,
1179
                           VRTPDWorkingDataPtr *ppWorkingData)
1180
0
{
1181
0
    CPLAssert(eInDT == GDT_Float64);
1182
1183
0
    const bool bIsFinalStep = *pnOutBands != 0;
1184
0
    *peOutDT = eInDT;
1185
0
    *ppWorkingData = nullptr;
1186
1187
0
    if (bIsFinalStep)
1188
0
    {
1189
0
        if (*pnOutBands != nInBands)
1190
0
        {
1191
0
            CPLError(CE_Failure, CPLE_NotSupported,
1192
0
                     "Trimming step: input band count (%d) is different from "
1193
0
                     "output band count (%d)",
1194
0
                     nInBands, *pnOutBands);
1195
0
            return CE_Failure;
1196
0
        }
1197
0
    }
1198
0
    else
1199
0
    {
1200
0
        *pnOutBands = nInBands;
1201
0
    }
1202
1203
0
    auto data = std::make_unique<TrimmingData>();
1204
1205
0
    bool bNodataSpecified = false;
1206
0
    double dfNoData = std::numeric_limits<double>::quiet_NaN();
1207
0
    std::string osTrimmingFilename;
1208
0
    bool bTrimmingNodataSpecified = false;
1209
0
    bool bRelativeToVRT = false;
1210
1211
0
    for (const auto &[pszKey, pszValue] :
1212
0
         cpl::IterateNameValue(papszFunctionArgs))
1213
0
    {
1214
0
        if (EQUAL(pszKey, "relativeToVRT"))
1215
0
        {
1216
0
            bRelativeToVRT = CPLTestBool(pszValue);
1217
0
        }
1218
0
        else if (EQUAL(pszKey, "nodata"))
1219
0
        {
1220
0
            bNodataSpecified = true;
1221
0
            dfNoData = CPLAtof(pszValue);
1222
0
        }
1223
0
        else if (EQUAL(pszKey, "trimming_nodata"))
1224
0
        {
1225
0
            bTrimmingNodataSpecified = true;
1226
0
            data->m_dfTrimmingNodata = CPLAtof(pszValue);
1227
0
        }
1228
0
        else if (EQUAL(pszKey, "trimming_dataset_filename"))
1229
0
        {
1230
0
            osTrimmingFilename = pszValue;
1231
0
        }
1232
0
        else if (EQUAL(pszKey, "red_band"))
1233
0
        {
1234
0
            const int nBand = atoi(pszValue) - 1;
1235
0
            if (nBand < 0 || nBand >= nInBands)
1236
0
            {
1237
0
                CPLError(CE_Failure, CPLE_AppDefined,
1238
0
                         "Invalid band in argument '%s'", pszKey);
1239
0
                return CE_Failure;
1240
0
            }
1241
0
            data->m_nRedBand = nBand;
1242
0
        }
1243
0
        else if (EQUAL(pszKey, "green_band"))
1244
0
        {
1245
0
            const int nBand = atoi(pszValue) - 1;
1246
0
            if (nBand < 0 || nBand >= nInBands)
1247
0
            {
1248
0
                CPLError(CE_Failure, CPLE_AppDefined,
1249
0
                         "Invalid band in argument '%s'", pszKey);
1250
0
                return CE_Failure;
1251
0
            }
1252
0
            data->m_nGreenBand = nBand;
1253
0
        }
1254
0
        else if (EQUAL(pszKey, "blue_band"))
1255
0
        {
1256
0
            const int nBand = atoi(pszValue) - 1;
1257
0
            if (nBand < 0 || nBand >= nInBands)
1258
0
            {
1259
0
                CPLError(CE_Failure, CPLE_AppDefined,
1260
0
                         "Invalid band in argument '%s'", pszKey);
1261
0
                return CE_Failure;
1262
0
            }
1263
0
            data->m_nBlueBand = nBand;
1264
0
        }
1265
0
        else if (EQUAL(pszKey, "top_rgb"))
1266
0
        {
1267
0
            data->m_dfTopRGB = CPLAtof(pszValue);
1268
0
        }
1269
0
        else if (EQUAL(pszKey, "tone_ceil"))
1270
0
        {
1271
0
            data->m_dfToneCeil = CPLAtof(pszValue);
1272
0
        }
1273
0
        else if (EQUAL(pszKey, "top_margin"))
1274
0
        {
1275
0
            data->m_dfTopMargin = CPLAtof(pszValue);
1276
0
        }
1277
0
        else
1278
0
        {
1279
0
            CPLError(CE_Warning, CPLE_AppDefined,
1280
0
                     "Unrecognized argument name %s. Ignored", pszKey);
1281
0
        }
1282
0
    }
1283
1284
0
    if (data->m_nRedBand == data->m_nGreenBand ||
1285
0
        data->m_nRedBand == data->m_nBlueBand ||
1286
0
        data->m_nGreenBand == data->m_nBlueBand)
1287
0
    {
1288
0
        CPLError(
1289
0
            CE_Failure, CPLE_NotSupported,
1290
0
            "red_band, green_band and blue_band must have distinct values");
1291
0
        return CE_Failure;
1292
0
    }
1293
1294
0
    const auto osFilename = GDALDataset::BuildFilename(
1295
0
        osTrimmingFilename.c_str(), pszVRTPath, bRelativeToVRT);
1296
0
    data->m_poTrimmingDS.reset(GDALDataset::Open(
1297
0
        osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
1298
0
        nullptr, nullptr));
1299
0
    if (!data->m_poTrimmingDS)
1300
0
        return CE_Failure;
1301
0
    if (data->m_poTrimmingDS->GetRasterCount() != 1)
1302
0
    {
1303
0
        CPLError(CE_Failure, CPLE_NotSupported,
1304
0
                 "Trimming dataset should have a single band");
1305
0
        return CE_Failure;
1306
0
    }
1307
0
    data->m_poTrimmingBand = data->m_poTrimmingDS->GetRasterBand(1);
1308
1309
0
    GDALGeoTransform auxGT;
1310
0
    if (data->m_poTrimmingDS->GetGeoTransform(auxGT) != CE_None)
1311
0
    {
1312
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks a geotransform",
1313
0
                 osFilename.c_str());
1314
0
        return CE_Failure;
1315
0
    }
1316
0
    int bAuxBandHasNoData = false;
1317
0
    const double dfAuxNoData =
1318
0
        data->m_poTrimmingBand->GetNoDataValue(&bAuxBandHasNoData);
1319
0
    if (!bTrimmingNodataSpecified && bAuxBandHasNoData)
1320
0
        data->m_dfTrimmingNodata = dfAuxNoData;
1321
1322
0
    SetOutputValuesForInNoDataAndOutNoData(
1323
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bNodataSpecified,
1324
0
        dfNoData, bNodataSpecified, dfNoData, bIsFinalStep);
1325
1326
0
    *ppWorkingData = data.release();
1327
0
    return CE_None;
1328
0
}
1329
1330
/************************************************************************/
1331
/*                            TrimmingFree()                            */
1332
/************************************************************************/
1333
1334
/** Free function for 'Trimming' builtin function. */
1335
static void TrimmingFree(const char * /*pszFuncName*/, void * /*pUserData*/,
1336
                         VRTPDWorkingDataPtr pWorkingData)
1337
0
{
1338
0
    TrimmingData *data = static_cast<TrimmingData *>(pWorkingData);
1339
0
    CPLAssert(data->m_osSignature == TrimmingData::EXPECTED_SIGNATURE);
1340
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
1341
0
    delete data;
1342
0
}
1343
1344
/************************************************************************/
1345
/*                          TrimmingProcess()                           */
1346
/************************************************************************/
1347
1348
/** Processing function for 'Trimming' builtin function. */
1349
static CPLErr TrimmingProcess(
1350
    const char * /*pszFuncName*/, void * /*pUserData*/,
1351
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs*/,
1352
    int nBufXSize, int nBufYSize, const void *pInBuffer, size_t nInBufferSize,
1353
    GDALDataType eInDT, int nInBands, const double *CPL_RESTRICT padfInNoData,
1354
    void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT, int nOutBands,
1355
    const double *CPL_RESTRICT padfOutNoData, double dfSrcXOff,
1356
    double dfSrcYOff, double dfSrcXSize, double dfSrcYSize,
1357
    const double adfSrcGT[], const char * /* pszVRTPath */,
1358
    CSLConstList /*papszExtra*/)
1359
0
{
1360
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1361
1362
0
    CPL_IGNORE_RET_VAL(eInDT);
1363
0
    CPLAssert(eInDT == GDT_Float64);
1364
0
    CPL_IGNORE_RET_VAL(eOutDT);
1365
0
    CPLAssert(eOutDT == GDT_Float64);
1366
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
1367
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
1368
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
1369
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
1370
0
    CPLAssert(nInBands == nOutBands);
1371
0
    CPL_IGNORE_RET_VAL(nOutBands);
1372
1373
0
    TrimmingData *data = static_cast<TrimmingData *>(pWorkingData);
1374
0
    CPLAssert(data->m_osSignature == TrimmingData::EXPECTED_SIGNATURE);
1375
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1376
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1377
1378
    // Compute georeferenced extent of input region
1379
0
    const double dfULX =
1380
0
        adfSrcGT[0] + adfSrcGT[1] * dfSrcXOff + adfSrcGT[2] * dfSrcYOff;
1381
0
    const double dfULY =
1382
0
        adfSrcGT[3] + adfSrcGT[4] * dfSrcXOff + adfSrcGT[5] * dfSrcYOff;
1383
0
    const double dfLRX = adfSrcGT[0] + adfSrcGT[1] * (dfSrcXOff + dfSrcXSize) +
1384
0
                         adfSrcGT[2] * (dfSrcYOff + dfSrcYSize);
1385
0
    const double dfLRY = adfSrcGT[3] + adfSrcGT[4] * (dfSrcXOff + dfSrcXSize) +
1386
0
                         adfSrcGT[5] * (dfSrcYOff + dfSrcYSize);
1387
1388
0
    if (!LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize, nBufYSize,
1389
0
                     "trimming", data->m_poTrimmingBand,
1390
0
                     data->m_abyTrimmingBuffer))
1391
0
    {
1392
0
        return CE_Failure;
1393
0
    }
1394
1395
0
    const float *pafTrimming =
1396
0
        reinterpret_cast<const float *>(data->m_abyTrimmingBuffer.data());
1397
0
    const int nRedBand = data->m_nRedBand;
1398
0
    const int nGreenBand = data->m_nGreenBand;
1399
0
    const int nBlueBand = data->m_nBlueBand;
1400
0
    const double dfTopMargin = data->m_dfTopMargin;
1401
0
    const double dfTopRGB = data->m_dfTopRGB;
1402
0
    const double dfToneCeil = data->m_dfToneCeil;
1403
0
#if !defined(trimming_non_optimized_version)
1404
0
    const double dfInvToneCeil = 1.0 / dfToneCeil;
1405
0
#endif
1406
0
    const bool bRGBBandsAreFirst =
1407
0
        std::max(std::max(nRedBand, nGreenBand), nBlueBand) <= 2;
1408
0
    const double dfNoDataTrimming = data->m_dfTrimmingNodata;
1409
0
    const double dfNoDataRed = padfInNoData[nRedBand];
1410
0
    const double dfNoDataGreen = padfInNoData[nGreenBand];
1411
0
    const double dfNoDataBlue = padfInNoData[nBlueBand];
1412
0
    for (size_t i = 0; i < nElts; ++i)
1413
0
    {
1414
        // Extract local saturation value from trimming image
1415
0
        const double dfLocalMaxRGB = pafTrimming[i];
1416
0
        const double dfReducedRGB =
1417
0
            std::min((1.0 - dfTopMargin) * dfTopRGB / dfLocalMaxRGB, 1.0);
1418
1419
0
        const double dfRed = padfSrc[nRedBand];
1420
0
        const double dfGreen = padfSrc[nGreenBand];
1421
0
        const double dfBlue = padfSrc[nBlueBand];
1422
0
        bool bNoDataPixel = false;
1423
0
        if ((dfLocalMaxRGB != dfNoDataTrimming) && (dfRed != dfNoDataRed) &&
1424
0
            (dfGreen != dfNoDataGreen) && (dfBlue != dfNoDataBlue))
1425
0
        {
1426
            // RGB bands specific process
1427
0
            const double dfMaxRGB = std::max(std::max(dfRed, dfGreen), dfBlue);
1428
0
#if !defined(trimming_non_optimized_version)
1429
0
            const double dfRedTimesToneRed = std::min(dfRed, dfToneCeil);
1430
0
            const double dfGreenTimesToneGreen = std::min(dfGreen, dfToneCeil);
1431
0
            const double dfBlueTimesToneBlue = std::min(dfBlue, dfToneCeil);
1432
0
            const double dfInvToneMaxRGB =
1433
0
                std::max(dfMaxRGB * dfInvToneCeil, 1.0);
1434
0
            const double dfReducedRGBTimesInvToneMaxRGB =
1435
0
                dfReducedRGB * dfInvToneMaxRGB;
1436
0
            padfDst[nRedBand] = std::min(
1437
0
                dfRedTimesToneRed * dfReducedRGBTimesInvToneMaxRGB, dfTopRGB);
1438
0
            padfDst[nGreenBand] =
1439
0
                std::min(dfGreenTimesToneGreen * dfReducedRGBTimesInvToneMaxRGB,
1440
0
                         dfTopRGB);
1441
0
            padfDst[nBlueBand] = std::min(
1442
0
                dfBlueTimesToneBlue * dfReducedRGBTimesInvToneMaxRGB, dfTopRGB);
1443
#else
1444
            // Original formulas. Slightly less optimized than the above ones.
1445
            const double dfToneMaxRGB = std::min(dfToneCeil / dfMaxRGB, 1.0);
1446
            const double dfToneRed = std::min(dfToneCeil / dfRed, 1.0);
1447
            const double dfToneGreen = std::min(dfToneCeil / dfGreen, 1.0);
1448
            const double dfToneBlue = std::min(dfToneCeil / dfBlue, 1.0);
1449
            padfDst[nRedBand] = std::min(
1450
                dfReducedRGB * dfRed * dfToneRed / dfToneMaxRGB, dfTopRGB);
1451
            padfDst[nGreenBand] = std::min(
1452
                dfReducedRGB * dfGreen * dfToneGreen / dfToneMaxRGB, dfTopRGB);
1453
            padfDst[nBlueBand] = std::min(
1454
                dfReducedRGB * dfBlue * dfToneBlue / dfToneMaxRGB, dfTopRGB);
1455
#endif
1456
1457
            // Other bands processing (NIR, ...): only apply RGB reduction factor
1458
0
            if (bRGBBandsAreFirst)
1459
0
            {
1460
                // optimization
1461
0
                for (int iBand = 3; iBand < nInBands; ++iBand)
1462
0
                {
1463
0
                    if (padfSrc[iBand] != padfInNoData[iBand])
1464
0
                    {
1465
0
                        padfDst[iBand] = dfReducedRGB * padfSrc[iBand];
1466
0
                    }
1467
0
                    else
1468
0
                    {
1469
0
                        bNoDataPixel = true;
1470
0
                        break;
1471
0
                    }
1472
0
                }
1473
0
            }
1474
0
            else
1475
0
            {
1476
0
                for (int iBand = 0; iBand < nInBands; ++iBand)
1477
0
                {
1478
0
                    if (iBand != nRedBand && iBand != nGreenBand &&
1479
0
                        iBand != nBlueBand)
1480
0
                    {
1481
0
                        if (padfSrc[iBand] != padfInNoData[iBand])
1482
0
                        {
1483
0
                            padfDst[iBand] = dfReducedRGB * padfSrc[iBand];
1484
0
                        }
1485
0
                        else
1486
0
                        {
1487
0
                            bNoDataPixel = true;
1488
0
                            break;
1489
0
                        }
1490
0
                    }
1491
0
                }
1492
0
            }
1493
0
        }
1494
0
        else
1495
0
        {
1496
0
            bNoDataPixel = true;
1497
0
        }
1498
0
        if (bNoDataPixel)
1499
0
        {
1500
0
            for (int iBand = 0; iBand < nInBands; ++iBand)
1501
0
            {
1502
0
                padfDst[iBand] = padfOutNoData[iBand];
1503
0
            }
1504
0
        }
1505
1506
0
        padfSrc += nInBands;
1507
0
        padfDst += nInBands;
1508
0
    }
1509
1510
0
    return CE_None;
1511
0
}
1512
1513
/************************************************************************/
1514
/*                           ExpressionInit()                           */
1515
/************************************************************************/
1516
1517
namespace
1518
{
1519
1520
class ExpressionData
1521
{
1522
  public:
1523
    ExpressionData(int nInBands, int nBatchSize, std::string_view osExpression,
1524
                   std::string_view osDialect)
1525
0
        : m_nInBands(nInBands), m_nNominalBatchSize(nBatchSize),
1526
0
          m_nBatchCount(DIV_ROUND_UP(nInBands, nBatchSize)), m_adfResults{},
1527
0
          m_osExpression(std::string(osExpression)),
1528
0
          m_osDialect(std::string(osDialect)), m_oNominalBatchEnv{},
1529
0
          m_oPartialBatchEnv{}
1530
0
    {
1531
0
    }
1532
1533
    CPLErr Compile()
1534
0
    {
1535
0
        auto eErr = m_oNominalBatchEnv.Initialize(m_osExpression, m_osDialect,
1536
0
                                                  m_nNominalBatchSize);
1537
0
        if (eErr != CE_None)
1538
0
        {
1539
0
            return eErr;
1540
0
        }
1541
1542
0
        const auto nPartialBatchSize = m_nInBands % m_nNominalBatchSize;
1543
0
        if (nPartialBatchSize)
1544
0
        {
1545
0
            eErr = m_oPartialBatchEnv.Initialize(m_osExpression, m_osDialect,
1546
0
                                                 nPartialBatchSize);
1547
0
        }
1548
1549
0
        return eErr;
1550
0
    }
1551
1552
    CPLErr Evaluate(const double *padfInputs, size_t nExpectedOutBands)
1553
0
    {
1554
0
        m_adfResults.clear();
1555
1556
0
        for (int iBatch = 0; iBatch < m_nBatchCount; iBatch++)
1557
0
        {
1558
0
            const auto nBandsRemaining =
1559
0
                static_cast<int>(m_nInBands - (m_nNominalBatchSize * iBatch));
1560
0
            const auto nBatchSize =
1561
0
                std::min(m_nNominalBatchSize, nBandsRemaining);
1562
1563
0
            auto &oEnv = GetEnv(nBatchSize);
1564
1565
0
            const double *pdfStart = padfInputs + iBatch * m_nNominalBatchSize;
1566
0
            const double *pdfEnd = pdfStart + nBatchSize;
1567
1568
0
            std::copy(pdfStart, pdfEnd, oEnv.m_adfValuesForPixel.begin());
1569
1570
0
            if (auto eErr = oEnv.m_poExpression->Evaluate(); eErr != CE_None)
1571
0
            {
1572
0
                return eErr;
1573
0
            }
1574
1575
0
            const auto &adfResults = oEnv.m_poExpression->Results();
1576
0
            if (m_nBatchCount > 1)
1577
0
            {
1578
0
                std::copy(adfResults.begin(), adfResults.end(),
1579
0
                          std::back_inserter(m_adfResults));
1580
0
            }
1581
0
        }
1582
1583
0
        if (nExpectedOutBands > 0)
1584
0
        {
1585
0
            if (Results().size() != static_cast<std::size_t>(nExpectedOutBands))
1586
0
            {
1587
0
                CPLError(CE_Failure, CPLE_AppDefined,
1588
0
                         "Expression returned %d values but "
1589
0
                         "%d output bands were expected.",
1590
0
                         static_cast<int>(Results().size()),
1591
0
                         static_cast<int>(nExpectedOutBands));
1592
0
                return CE_Failure;
1593
0
            }
1594
0
        }
1595
1596
0
        return CE_None;
1597
0
    }
1598
1599
    const std::vector<double> &Results() const
1600
0
    {
1601
0
        if (m_nBatchCount == 1)
1602
0
        {
1603
0
            return m_oNominalBatchEnv.m_poExpression->Results();
1604
0
        }
1605
0
        else
1606
0
        {
1607
0
            return m_adfResults;
1608
0
        }
1609
0
    }
1610
1611
  private:
1612
    const int m_nInBands;
1613
    const int m_nNominalBatchSize;
1614
    const int m_nBatchCount;
1615
    std::vector<double> m_adfResults;
1616
1617
    const CPLString m_osExpression;
1618
    const CPLString m_osDialect;
1619
1620
    struct InvocationEnv
1621
    {
1622
        std::vector<double> m_adfValuesForPixel;
1623
        std::unique_ptr<gdal::MathExpression> m_poExpression;
1624
1625
        CPLErr Initialize(const CPLString &osExpression,
1626
                          const CPLString &osDialect, int nBatchSize)
1627
0
        {
1628
0
            m_poExpression =
1629
0
                gdal::MathExpression::Create(osExpression, osDialect.c_str());
1630
            // cppcheck-suppress knownConditionTrueFalse
1631
0
            if (m_poExpression == nullptr)
1632
0
            {
1633
0
                return CE_Failure;
1634
0
            }
1635
1636
0
            m_adfValuesForPixel.resize(nBatchSize);
1637
1638
0
            for (int i = 0; i < nBatchSize; i++)
1639
0
            {
1640
0
                std::string osVar = "B" + std::to_string(i + 1);
1641
0
                m_poExpression->RegisterVariable(osVar,
1642
0
                                                 &m_adfValuesForPixel[i]);
1643
0
            }
1644
1645
0
            if (osExpression.ifind("BANDS") != std::string::npos)
1646
0
            {
1647
0
                m_poExpression->RegisterVector("BANDS", &m_adfValuesForPixel);
1648
0
            }
1649
1650
0
            return m_poExpression->Compile();
1651
0
        }
1652
    };
1653
1654
    InvocationEnv &GetEnv(int nBatchSize)
1655
0
    {
1656
0
        if (nBatchSize == m_nNominalBatchSize)
1657
0
        {
1658
0
            return m_oNominalBatchEnv;
1659
0
        }
1660
0
        else
1661
0
        {
1662
0
            return m_oPartialBatchEnv;
1663
0
        }
1664
0
    }
1665
1666
    InvocationEnv m_oNominalBatchEnv;
1667
    InvocationEnv m_oPartialBatchEnv;
1668
};
1669
1670
}  // namespace
1671
1672
static CPLErr ExpressionInit(const char * /*pszFuncName*/, void * /*pUserData*/,
1673
                             CSLConstList papszFunctionArgs, int nInBands,
1674
                             GDALDataType eInDT, double * /* padfInNoData */,
1675
                             int *pnOutBands, GDALDataType *peOutDT,
1676
                             double ** /* ppadfOutNoData */,
1677
                             const char * /* pszVRTPath */,
1678
                             VRTPDWorkingDataPtr *ppWorkingData)
1679
0
{
1680
0
    CPLAssert(eInDT == GDT_Float64);
1681
1682
0
    *peOutDT = eInDT;
1683
0
    *ppWorkingData = nullptr;
1684
1685
0
    const char *pszBatchSize =
1686
0
        CSLFetchNameValue(papszFunctionArgs, "batch_size");
1687
0
    auto nBatchSize = nInBands;
1688
1689
0
    if (pszBatchSize != nullptr)
1690
0
    {
1691
0
        nBatchSize = std::min(nInBands, std::atoi(pszBatchSize));
1692
0
    }
1693
1694
0
    if (nBatchSize < 1)
1695
0
    {
1696
0
        CPLError(CE_Failure, CPLE_IllegalArg, "batch_size must be at least 1");
1697
0
        return CE_Failure;
1698
0
    }
1699
1700
0
    const char *pszDialect = CSLFetchNameValue(papszFunctionArgs, "dialect");
1701
0
    if (pszDialect == nullptr)
1702
0
    {
1703
0
        pszDialect = "muparser";
1704
0
    }
1705
1706
0
    const char *pszExpression =
1707
0
        CSLFetchNameValue(papszFunctionArgs, "expression");
1708
1709
0
    auto data = std::make_unique<ExpressionData>(nInBands, nBatchSize,
1710
0
                                                 pszExpression, pszDialect);
1711
1712
0
    if (auto eErr = data->Compile(); eErr != CE_None)
1713
0
    {
1714
0
        return eErr;
1715
0
    }
1716
1717
0
    if (*pnOutBands == 0)
1718
0
    {
1719
0
        std::vector<double> aDummyValues(nInBands);
1720
0
        if (auto eErr = data->Evaluate(aDummyValues.data(), 0); eErr != CE_None)
1721
0
        {
1722
0
            return eErr;
1723
0
        }
1724
1725
0
        *pnOutBands = static_cast<int>(data->Results().size());
1726
0
    }
1727
1728
0
    *ppWorkingData = data.release();
1729
1730
0
    return CE_None;
1731
0
}
1732
1733
static void ExpressionFree(const char * /* pszFuncName */,
1734
                           void * /* pUserData */,
1735
                           VRTPDWorkingDataPtr pWorkingData)
1736
0
{
1737
0
    ExpressionData *data = static_cast<ExpressionData *>(pWorkingData);
1738
0
    delete data;
1739
0
}
1740
1741
static CPLErr ExpressionProcess(
1742
    const char * /* pszFuncName */, void * /* pUserData */,
1743
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs */,
1744
    int nBufXSize, int nBufYSize, const void *pInBuffer,
1745
    size_t /* nInBufferSize */, GDALDataType eInDT, int nInBands,
1746
    const double *CPL_RESTRICT /* padfInNoData */, void *pOutBuffer,
1747
    size_t /* nOutBufferSize */, GDALDataType eOutDT, int nOutBands,
1748
    const double *CPL_RESTRICT /* padfOutNoData */, double /* dfSrcXOff */,
1749
    double /* dfSrcYOff */, double /* dfSrcXSize */, double /* dfSrcYSize */,
1750
    const double /* adfSrcGT */[], const char * /* pszVRTPath "*/,
1751
    CSLConstList /* papszExtra */)
1752
0
{
1753
0
    ExpressionData *expr = static_cast<ExpressionData *>(pWorkingData);
1754
1755
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1756
1757
0
    CPL_IGNORE_RET_VAL(eInDT);
1758
0
    CPLAssert(eInDT == GDT_Float64);
1759
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1760
1761
0
    CPLAssert(eOutDT == GDT_Float64);
1762
0
    CPL_IGNORE_RET_VAL(eOutDT);
1763
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1764
1765
0
    for (size_t i = 0; i < nElts; i++)
1766
0
    {
1767
0
        if (auto eErr = expr->Evaluate(padfSrc, nOutBands); eErr != CE_None)
1768
0
        {
1769
0
            return eErr;
1770
0
        }
1771
1772
0
        const auto &adfResults = expr->Results();
1773
0
        std::copy(adfResults.begin(), adfResults.end(), padfDst);
1774
1775
0
        padfDst += nOutBands;
1776
0
        padfSrc += nInBands;
1777
0
    }
1778
1779
0
    return CE_None;
1780
0
}
1781
1782
/************************************************************************/
1783
/*            GDALVRTRegisterDefaultProcessedDatasetFuncs()             */
1784
/************************************************************************/
1785
1786
/** Register builtin functions that can be used in a VRTProcessedDataset.
1787
 */
1788
void GDALVRTRegisterDefaultProcessedDatasetFuncs()
1789
24
{
1790
24
    GDALVRTRegisterProcessedDatasetFunc(
1791
24
        "BandAffineCombination", nullptr,
1792
24
        "<ProcessedDatasetFunctionArgumentsList>"
1793
24
        "   <Argument name='src_nodata' type='double' "
1794
24
        "description='Override input nodata value'/>"
1795
24
        "   <Argument name='dst_nodata' type='double' "
1796
24
        "description='Override output nodata value'/>"
1797
24
        "   <Argument name='replacement_nodata' "
1798
24
        "description='value to substitute to a valid computed value that "
1799
24
        "would be nodata' type='double'/>"
1800
24
        "   <Argument name='dst_intended_datatype' type='string' "
1801
24
        "description='Intented datatype of output (which might be "
1802
24
        "different than the working data type)'/>"
1803
24
        "   <Argument name='coefficients_{band}' "
1804
24
        "description='Comma-separated coefficients for combining bands. "
1805
24
        "First one is constant term' "
1806
24
        "type='double_list' required='true'/>"
1807
24
        "   <Argument name='min' description='clamp min value' type='double'/>"
1808
24
        "   <Argument name='max' description='clamp max value' type='double'/>"
1809
24
        "</ProcessedDatasetFunctionArgumentsList>",
1810
24
        GDT_Float64, nullptr, 0, nullptr, 0, BandAffineCombinationInit,
1811
24
        BandAffineCombinationFree, BandAffineCombinationProcess, nullptr);
1812
1813
24
    GDALVRTRegisterProcessedDatasetFunc(
1814
24
        "LUT", nullptr,
1815
24
        "<ProcessedDatasetFunctionArgumentsList>"
1816
24
        "   <Argument name='src_nodata' type='double' "
1817
24
        "description='Override input nodata value'/>"
1818
24
        "   <Argument name='dst_nodata' type='double' "
1819
24
        "description='Override output nodata value'/>"
1820
24
        "   <Argument name='lut_{band}' "
1821
24
        "description='List of the form [src value 1]:[dest value 1],"
1822
24
        "[src value 2]:[dest value 2],...' "
1823
24
        "type='string' required='true'/>"
1824
24
        "</ProcessedDatasetFunctionArgumentsList>",
1825
24
        GDT_Float64, nullptr, 0, nullptr, 0, LUTInit, LUTFree, LUTProcess,
1826
24
        nullptr);
1827
1828
24
    GDALVRTRegisterProcessedDatasetFunc(
1829
24
        "LocalScaleOffset", nullptr,
1830
24
        "<ProcessedDatasetFunctionArgumentsList>"
1831
24
        "   <Argument name='relativeToVRT' "
1832
24
        "description='Whether gain and offset filenames are relative to "
1833
24
        "the VRT' type='boolean' default='false'/>"
1834
24
        "   <Argument name='gain_dataset_filename_{band}' "
1835
24
        "description='Filename to the gain dataset' "
1836
24
        "type='string' required='true'/>"
1837
24
        "   <Argument name='gain_dataset_band_{band}' "
1838
24
        "description='Band of the gain dataset' "
1839
24
        "type='integer' required='true'/>"
1840
24
        "   <Argument name='offset_dataset_filename_{band}' "
1841
24
        "description='Filename to the offset dataset' "
1842
24
        "type='string' required='true'/>"
1843
24
        "   <Argument name='offset_dataset_band_{band}' "
1844
24
        "description='Band of the offset dataset' "
1845
24
        "type='integer' required='true'/>"
1846
24
        "   <Argument name='min' description='clamp min value' type='double'/>"
1847
24
        "   <Argument name='max' description='clamp max value' type='double'/>"
1848
24
        "   <Argument name='nodata' type='double' "
1849
24
        "description='Override dataset nodata value'/>"
1850
24
        "   <Argument name='gain_nodata' type='double' "
1851
24
        "description='Override gain dataset nodata value'/>"
1852
24
        "   <Argument name='offset_nodata' type='double' "
1853
24
        "description='Override offset dataset nodata value'/>"
1854
24
        "</ProcessedDatasetFunctionArgumentsList>",
1855
24
        GDT_Float64, nullptr, 0, nullptr, 0, LocalScaleOffsetInit,
1856
24
        LocalScaleOffsetFree, LocalScaleOffsetProcess, nullptr);
1857
1858
24
    GDALVRTRegisterProcessedDatasetFunc(
1859
24
        "Trimming", nullptr,
1860
24
        "<ProcessedDatasetFunctionArgumentsList>"
1861
24
        "   <Argument name='relativeToVRT' "
1862
24
        "description='Whether trimming_dataset_filename is relative to the VRT'"
1863
24
        " type='boolean' default='false'/>"
1864
24
        "   <Argument name='trimming_dataset_filename' "
1865
24
        "description='Filename to the trimming dataset' "
1866
24
        "type='string' required='true'/>"
1867
24
        "   <Argument name='red_band' type='integer' default='1'/>"
1868
24
        "   <Argument name='green_band' type='integer' default='2'/>"
1869
24
        "   <Argument name='blue_band' type='integer' default='3'/>"
1870
24
        "   <Argument name='top_rgb' "
1871
24
        "description='Maximum saturating RGB output value' "
1872
24
        "type='double' required='true'/>"
1873
24
        "   <Argument name='tone_ceil' "
1874
24
        "description='Maximum threshold beyond which we give up saturation' "
1875
24
        "type='double' required='true'/>"
1876
24
        "   <Argument name='top_margin' "
1877
24
        "description='Margin to allow for dynamics in brightest areas "
1878
24
        "(between 0 and 1, should be close to 0)' "
1879
24
        "type='double' required='true'/>"
1880
24
        "   <Argument name='nodata' type='double' "
1881
24
        "description='Override dataset nodata value'/>"
1882
24
        "   <Argument name='trimming_nodata' type='double' "
1883
24
        "description='Override trimming dataset nodata value'/>"
1884
24
        "</ProcessedDatasetFunctionArgumentsList>",
1885
24
        GDT_Float64, nullptr, 0, nullptr, 0, TrimmingInit, TrimmingFree,
1886
24
        TrimmingProcess, nullptr);
1887
1888
24
    GDALVRTRegisterProcessedDatasetFunc(
1889
24
        "Expression", nullptr,
1890
24
        "<ProcessedDatasetFunctionArgumentsList>"
1891
24
        "    <Argument name='expression' description='the expression to "
1892
24
        "evaluate' type='string' required='true' />"
1893
24
        "    <Argument name='dialect' description='expression dialect' "
1894
24
        "type='string' />"
1895
24
        "    <Argument name='batch_size' description='batch size' "
1896
24
        "type='integer' />"
1897
24
        "</ProcessedDatasetFunctionArgumentsList>",
1898
24
        GDT_Float64, nullptr, 0, nullptr, 0, ExpressionInit, ExpressionFree,
1899
24
        ExpressionProcess, nullptr);
1900
24
}