Coverage Report

Created: 2025-06-13 06:29

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