Coverage Report

Created: 2026-10-28 07:09

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
            if (adfInputValues.empty())
525
0
            {
526
0
                CPLError(CE_Failure, CPLE_AppDefined,
527
0
                         "Argument '%s' must have at least one entry", pszKey);
528
0
                return CE_Failure;
529
0
            }
530
0
            oMap[nBand - 1] = std::pair(std::move(adfInputValues),
531
0
                                        std::move(adfOutputValues));
532
0
        }
533
0
        else
534
0
        {
535
0
            CPLError(CE_Warning, CPLE_AppDefined,
536
0
                     "Unrecognized argument name %s. Ignored", pszKey);
537
0
        }
538
0
    }
539
540
0
    SetOutputValuesForInNoDataAndOutNoData(
541
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bSrcNodataSpecified,
542
0
        dfSrcNoData, bDstNodataSpecified, dfDstNoData, bIsFinalStep);
543
544
0
    int iExpected = 0;
545
    // Check we have values for all bands and convert to vector
546
0
    for (auto &oIter : oMap)
547
0
    {
548
0
        if (oIter.first != iExpected)
549
0
        {
550
0
            CPLError(CE_Failure, CPLE_AppDefined, "Argument lut_%d is missing",
551
0
                     iExpected + 1);
552
0
            return CE_Failure;
553
0
        }
554
0
        ++iExpected;
555
0
        data->m_aadfLUTInputs.emplace_back(std::move(oIter.second.first));
556
0
        data->m_aadfLUTOutputs.emplace_back(std::move(oIter.second.second));
557
0
    }
558
559
0
    if (static_cast<int>(oMap.size()) < *pnOutBands)
560
0
    {
561
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing lut_XX element(s)");
562
0
        return CE_Failure;
563
0
    }
564
565
0
    *ppWorkingData = data.release();
566
0
    return CE_None;
567
0
}
568
569
/************************************************************************/
570
/*                              LUTFree()                               */
571
/************************************************************************/
572
573
/** Free function for 'LUT' builtin function. */
574
static void LUTFree(const char * /*pszFuncName*/, void * /*pUserData*/,
575
                    VRTPDWorkingDataPtr pWorkingData)
576
0
{
577
0
    LUTData *data = static_cast<LUTData *>(pWorkingData);
578
0
    CPLAssert(data->m_osSignature == LUTData::EXPECTED_SIGNATURE);
579
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
580
0
    delete data;
581
0
}
582
583
/************************************************************************/
584
/*                             LUTProcess()                             */
585
/************************************************************************/
586
587
/** Processing function for 'LUT' builtin function. */
588
static CPLErr
589
LUTProcess(const char * /*pszFuncName*/, void * /*pUserData*/,
590
           VRTPDWorkingDataPtr pWorkingData,
591
           CSLConstList /* papszFunctionArgs*/, int nBufXSize, int nBufYSize,
592
           const void *pInBuffer, size_t nInBufferSize, GDALDataType eInDT,
593
           int nInBands, const double *CPL_RESTRICT padfInNoData,
594
           void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT,
595
           int nOutBands, const double *CPL_RESTRICT padfOutNoData,
596
           double /*dfSrcXOff*/, double /*dfSrcYOff*/, double /*dfSrcXSize*/,
597
           double /*dfSrcYSize*/, const double /*adfSrcGT*/[],
598
           const char * /* pszVRTPath */, CSLConstList /*papszExtra*/)
599
0
{
600
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
601
602
0
    CPL_IGNORE_RET_VAL(eInDT);
603
0
    CPLAssert(eInDT == GDT_Float64);
604
0
    CPL_IGNORE_RET_VAL(eOutDT);
605
0
    CPLAssert(eOutDT == GDT_Float64);
606
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
607
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
608
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
609
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
610
0
    CPLAssert(nInBands == nOutBands);
611
0
    CPL_IGNORE_RET_VAL(nOutBands);
612
613
0
    const LUTData *data = static_cast<LUTData *>(pWorkingData);
614
0
    CPLAssert(data->m_osSignature == LUTData::EXPECTED_SIGNATURE);
615
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
616
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
617
0
    for (size_t i = 0; i < nElts; ++i)
618
0
    {
619
0
        for (int iBand = 0; iBand < nInBands; ++iBand)
620
0
        {
621
            // written this way to work with a NaN value
622
0
            if (!(*padfSrc != padfInNoData[iBand]))
623
0
                *padfDst = padfOutNoData[iBand];
624
0
            else
625
0
                *padfDst = data->LookupValue(iBand, *padfSrc);
626
0
            ++padfSrc;
627
0
            ++padfDst;
628
0
        }
629
0
    }
630
631
0
    return CE_None;
632
0
}
633
634
/************************************************************************/
635
/*                         LocalScaleOffsetData                         */
636
/************************************************************************/
637
638
namespace
639
{
640
/** Working structure for 'LocalScaleOffset' builtin function. */
641
struct LocalScaleOffsetData
642
{
643
    static constexpr const char *const EXPECTED_SIGNATURE = "LocalScaleOffset";
644
    //! Signature (to make sure callback functions are called with the right argument)
645
    const std::string m_osSignature = EXPECTED_SIGNATURE;
646
647
    //! Nodata value for gain dataset(s)
648
    double m_dfGainNodata = std::numeric_limits<double>::quiet_NaN();
649
650
    //! Nodata value for offset dataset(s)
651
    double m_dfOffsetNodata = std::numeric_limits<double>::quiet_NaN();
652
653
    //! Minimum clamping value.
654
    double m_dfClampMin = std::numeric_limits<double>::quiet_NaN();
655
656
    //! Maximum clamping value.
657
    double m_dfClampMax = std::numeric_limits<double>::quiet_NaN();
658
659
    //! Map from gain/offset dataset name to datasets
660
    std::map<std::string, std::unique_ptr<GDALDataset>> m_oDatasetMap{};
661
662
    //! Vector of size nInBands that point to the raster band from which to read gains.
663
    std::vector<GDALRasterBand *> m_oGainBands{};
664
665
    //! Vector of size nInBands that point to the raster band from which to read offsets.
666
    std::vector<GDALRasterBand *> m_oOffsetBands{};
667
668
    //! Working buffer that contain gain values.
669
    std::vector<VRTProcessedDataset::NoInitByte> m_abyGainBuffer{};
670
671
    //! Working buffer that contain offset values.
672
    std::vector<VRTProcessedDataset::NoInitByte> m_abyOffsetBuffer{};
673
};
674
}  // namespace
675
676
/************************************************************************/
677
/*                           CheckAllBands()                            */
678
/************************************************************************/
679
680
/** Return true if the key of oMap is the sequence of all integers between
681
 * 0 and nExpectedBandCount-1.
682
 */
683
template <class T>
684
static bool CheckAllBands(const std::map<int, T> &oMap, int nExpectedBandCount)
685
0
{
686
0
    int iExpected = 0;
687
0
    for (const auto &kv : oMap)
688
0
    {
689
0
        if (kv.first != iExpected)
690
0
            return false;
691
0
        ++iExpected;
692
0
    }
693
0
    return iExpected == nExpectedBandCount;
694
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)
695
696
/************************************************************************/
697
/*                        LocalScaleOffsetInit()                        */
698
/************************************************************************/
699
700
/** Init function for 'LocalScaleOffset' builtin function. */
701
static CPLErr
702
LocalScaleOffsetInit(const char * /*pszFuncName*/, void * /*pUserData*/,
703
                     CSLConstList papszFunctionArgs, int nInBands,
704
                     GDALDataType eInDT, double *padfInNoData, int *pnOutBands,
705
                     GDALDataType *peOutDT, double **ppadfOutNoData,
706
                     const char *pszVRTPath, VRTPDWorkingDataPtr *ppWorkingData)
707
0
{
708
0
    CPLAssert(eInDT == GDT_Float64);
709
710
0
    const bool bIsFinalStep = *pnOutBands != 0;
711
0
    *peOutDT = eInDT;
712
0
    *ppWorkingData = nullptr;
713
714
0
    if (bIsFinalStep)
715
0
    {
716
0
        if (*pnOutBands != nInBands)
717
0
        {
718
0
            CPLError(CE_Failure, CPLE_NotSupported,
719
0
                     "LocalScaleOffset step: input band count (%d) is "
720
0
                     "different from output band count (%d)",
721
0
                     nInBands, *pnOutBands);
722
0
            return CE_Failure;
723
0
        }
724
0
    }
725
0
    else
726
0
    {
727
0
        *pnOutBands = nInBands;
728
0
    }
729
730
0
    auto data = std::make_unique<LocalScaleOffsetData>();
731
732
0
    bool bNodataSpecified = false;
733
0
    double dfNoData = std::numeric_limits<double>::quiet_NaN();
734
735
0
    bool bGainNodataSpecified = false;
736
0
    bool bOffsetNodataSpecified = false;
737
738
0
    std::map<int, std::string> oGainDatasetNameMap;
739
0
    std::map<int, int> oGainDatasetBandMap;
740
741
0
    std::map<int, std::string> oOffsetDatasetNameMap;
742
0
    std::map<int, int> oOffsetDatasetBandMap;
743
744
0
    bool bRelativeToVRT = false;
745
746
0
    for (const auto &[pszKey, pszValue] :
747
0
         cpl::IterateNameValue(papszFunctionArgs))
748
0
    {
749
0
        if (EQUAL(pszKey, "relativeToVRT"))
750
0
        {
751
0
            bRelativeToVRT = CPLTestBool(pszValue);
752
0
        }
753
0
        else if (EQUAL(pszKey, "nodata"))
754
0
        {
755
0
            bNodataSpecified = true;
756
0
            dfNoData = CPLAtof(pszValue);
757
0
        }
758
0
        else if (EQUAL(pszKey, "gain_nodata"))
759
0
        {
760
0
            bGainNodataSpecified = true;
761
0
            data->m_dfGainNodata = CPLAtof(pszValue);
762
0
        }
763
0
        else if (EQUAL(pszKey, "offset_nodata"))
764
0
        {
765
0
            bOffsetNodataSpecified = true;
766
0
            data->m_dfOffsetNodata = CPLAtof(pszValue);
767
0
        }
768
0
        else if (STARTS_WITH_CI(pszKey, "gain_dataset_filename_"))
769
0
        {
770
0
            const int nBand = atoi(pszKey + strlen("gain_dataset_filename_"));
771
0
            if (nBand <= 0 || nBand > nInBands)
772
0
            {
773
0
                CPLError(CE_Failure, CPLE_AppDefined,
774
0
                         "Invalid band in argument '%s'", pszKey);
775
0
                return CE_Failure;
776
0
            }
777
0
            oGainDatasetNameMap[nBand - 1] = pszValue;
778
0
        }
779
0
        else if (STARTS_WITH_CI(pszKey, "gain_dataset_band_"))
780
0
        {
781
0
            const int nBand = atoi(pszKey + strlen("gain_dataset_band_"));
782
0
            if (nBand <= 0 || nBand > nInBands)
783
0
            {
784
0
                CPLError(CE_Failure, CPLE_AppDefined,
785
0
                         "Invalid band in argument '%s'", pszKey);
786
0
                return CE_Failure;
787
0
            }
788
0
            oGainDatasetBandMap[nBand - 1] = atoi(pszValue);
789
0
        }
790
0
        else if (STARTS_WITH_CI(pszKey, "offset_dataset_filename_"))
791
0
        {
792
0
            const int nBand = atoi(pszKey + strlen("offset_dataset_filename_"));
793
0
            if (nBand <= 0 || nBand > nInBands)
794
0
            {
795
0
                CPLError(CE_Failure, CPLE_AppDefined,
796
0
                         "Invalid band in argument '%s'", pszKey);
797
0
                return CE_Failure;
798
0
            }
799
0
            oOffsetDatasetNameMap[nBand - 1] = pszValue;
800
0
        }
801
0
        else if (STARTS_WITH_CI(pszKey, "offset_dataset_band_"))
802
0
        {
803
0
            const int nBand = atoi(pszKey + strlen("offset_dataset_band_"));
804
0
            if (nBand <= 0 || nBand > nInBands)
805
0
            {
806
0
                CPLError(CE_Failure, CPLE_AppDefined,
807
0
                         "Invalid band in argument '%s'", pszKey);
808
0
                return CE_Failure;
809
0
            }
810
0
            oOffsetDatasetBandMap[nBand - 1] = atoi(pszValue);
811
0
        }
812
0
        else if (EQUAL(pszKey, "min"))
813
0
        {
814
0
            data->m_dfClampMin = CPLAtof(pszValue);
815
0
        }
816
0
        else if (EQUAL(pszKey, "max"))
817
0
        {
818
0
            data->m_dfClampMax = CPLAtof(pszValue);
819
0
        }
820
0
        else
821
0
        {
822
0
            CPLError(CE_Warning, CPLE_AppDefined,
823
0
                     "Unrecognized argument name %s. Ignored", pszKey);
824
0
        }
825
0
    }
826
827
0
    if (!CheckAllBands(oGainDatasetNameMap, nInBands))
828
0
    {
829
0
        CPLError(CE_Failure, CPLE_AppDefined,
830
0
                 "Missing gain_dataset_filename_XX element(s)");
831
0
        return CE_Failure;
832
0
    }
833
0
    if (!CheckAllBands(oGainDatasetBandMap, nInBands))
834
0
    {
835
0
        CPLError(CE_Failure, CPLE_AppDefined,
836
0
                 "Missing gain_dataset_band_XX element(s)");
837
0
        return CE_Failure;
838
0
    }
839
0
    if (!CheckAllBands(oOffsetDatasetNameMap, nInBands))
840
0
    {
841
0
        CPLError(CE_Failure, CPLE_AppDefined,
842
0
                 "Missing offset_dataset_filename_XX element(s)");
843
0
        return CE_Failure;
844
0
    }
845
0
    if (!CheckAllBands(oOffsetDatasetBandMap, nInBands))
846
0
    {
847
0
        CPLError(CE_Failure, CPLE_AppDefined,
848
0
                 "Missing offset_dataset_band_XX element(s)");
849
0
        return CE_Failure;
850
0
    }
851
852
0
    data->m_oGainBands.resize(nInBands);
853
0
    data->m_oOffsetBands.resize(nInBands);
854
855
0
    constexpr int IDX_GAIN = 0;
856
0
    constexpr int IDX_OFFSET = 1;
857
0
    for (int i : {IDX_GAIN, IDX_OFFSET})
858
0
    {
859
0
        const auto &oMapNames =
860
0
            (i == IDX_GAIN) ? oGainDatasetNameMap : oOffsetDatasetNameMap;
861
0
        const auto &oMapBands =
862
0
            (i == IDX_GAIN) ? oGainDatasetBandMap : oOffsetDatasetBandMap;
863
0
        for (const auto &kv : oMapNames)
864
0
        {
865
0
            const int nInBandIdx = kv.first;
866
0
            const auto osFilename = GDALDataset::BuildFilename(
867
0
                kv.second.c_str(), pszVRTPath, bRelativeToVRT);
868
0
            auto oIter = data->m_oDatasetMap.find(osFilename);
869
0
            if (oIter == data->m_oDatasetMap.end())
870
0
            {
871
0
                auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
872
0
                    osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
873
0
                    nullptr, nullptr, nullptr));
874
0
                if (!poDS)
875
0
                    return CE_Failure;
876
0
                GDALGeoTransform auxGT;
877
0
                if (poDS->GetGeoTransform(auxGT) != CE_None)
878
0
                {
879
0
                    CPLError(CE_Failure, CPLE_AppDefined,
880
0
                             "%s lacks a geotransform", osFilename.c_str());
881
0
                    return CE_Failure;
882
0
                }
883
0
                oIter = data->m_oDatasetMap
884
0
                            .insert(std::pair(osFilename, std::move(poDS)))
885
0
                            .first;
886
0
            }
887
0
            auto poDS = oIter->second.get();
888
0
            const auto oIterBand = oMapBands.find(nInBandIdx);
889
0
            CPLAssert(oIterBand != oMapBands.end());
890
0
            const int nAuxBand = oIterBand->second;
891
0
            if (nAuxBand <= 0 || nAuxBand > poDS->GetRasterCount())
892
0
            {
893
0
                CPLError(CE_Failure, CPLE_AppDefined,
894
0
                         "Invalid band number (%d) for a %s dataset", nAuxBand,
895
0
                         (i == IDX_GAIN) ? "gain" : "offset");
896
0
                return CE_Failure;
897
0
            }
898
0
            auto poAuxBand = poDS->GetRasterBand(nAuxBand);
899
0
            int bAuxBandHasNoData = false;
900
0
            const double dfAuxNoData =
901
0
                poAuxBand->GetNoDataValue(&bAuxBandHasNoData);
902
0
            if (i == IDX_GAIN)
903
0
            {
904
0
                data->m_oGainBands[nInBandIdx] = poAuxBand;
905
0
                if (!bGainNodataSpecified && bAuxBandHasNoData)
906
0
                    data->m_dfGainNodata = dfAuxNoData;
907
0
            }
908
0
            else
909
0
            {
910
0
                data->m_oOffsetBands[nInBandIdx] = poAuxBand;
911
0
                if (!bOffsetNodataSpecified && bAuxBandHasNoData)
912
0
                    data->m_dfOffsetNodata = dfAuxNoData;
913
0
            }
914
0
        }
915
0
    }
916
917
0
    SetOutputValuesForInNoDataAndOutNoData(
918
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bNodataSpecified,
919
0
        dfNoData, bNodataSpecified, dfNoData, bIsFinalStep);
920
921
0
    *ppWorkingData = data.release();
922
0
    return CE_None;
923
0
}
924
925
/************************************************************************/
926
/*                        LocalScaleOffsetFree()                        */
927
/************************************************************************/
928
929
/** Free function for 'LocalScaleOffset' builtin function. */
930
static void LocalScaleOffsetFree(const char * /*pszFuncName*/,
931
                                 void * /*pUserData*/,
932
                                 VRTPDWorkingDataPtr pWorkingData)
933
0
{
934
0
    LocalScaleOffsetData *data =
935
0
        static_cast<LocalScaleOffsetData *>(pWorkingData);
936
0
    CPLAssert(data->m_osSignature == LocalScaleOffsetData::EXPECTED_SIGNATURE);
937
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
938
0
    delete data;
939
0
}
940
941
/************************************************************************/
942
/*                            LoadAuxData()                             */
943
/************************************************************************/
944
945
// Load auxiliary corresponding offset, gain or trimming data.
946
static bool LoadAuxData(double dfULX, double dfULY, double dfLRX, double dfLRY,
947
                        size_t nElts, int nBufXSize, int nBufYSize,
948
                        const char *pszAuxType, GDALRasterBand *poAuxBand,
949
                        std::vector<VRTProcessedDataset::NoInitByte> &abyBuffer)
950
0
{
951
0
    GDALGeoTransform auxGT, auxInvGT;
952
953
    // Compute pixel/line coordinates from the georeferenced extent
954
0
    CPL_IGNORE_RET_VAL(poAuxBand->GetDataset()->GetGeoTransform(
955
0
        auxGT));  // return code already tested
956
0
    CPL_IGNORE_RET_VAL(auxGT.GetInverse(auxInvGT));
957
0
    const double dfULPixel =
958
0
        auxInvGT[0] + auxInvGT[1] * dfULX + auxInvGT[2] * dfULY;
959
0
    const double dfULLine =
960
0
        auxInvGT[3] + auxInvGT[4] * dfULX + auxInvGT[5] * dfULY;
961
0
    const double dfLRPixel =
962
0
        auxInvGT[0] + auxInvGT[1] * dfLRX + auxInvGT[2] * dfLRY;
963
0
    const double dfLRLine =
964
0
        auxInvGT[3] + auxInvGT[4] * dfLRX + auxInvGT[5] * dfLRY;
965
0
    if (dfULPixel >= dfLRPixel || dfULLine >= dfLRLine)
966
0
    {
967
0
        CPLError(CE_Failure, CPLE_AppDefined,
968
0
                 "Unexpected computed %s pixel/line", pszAuxType);
969
0
        return false;
970
0
    }
971
0
    if (dfULPixel < -1 || dfULLine < -1)
972
0
    {
973
0
        CPLError(CE_Failure, CPLE_AppDefined,
974
0
                 "Unexpected computed %s upper left (pixel,line)=(%f,%f)",
975
0
                 pszAuxType, dfULPixel, dfULLine);
976
0
        return false;
977
0
    }
978
0
    if (dfLRPixel > poAuxBand->GetXSize() + 1 ||
979
0
        dfLRLine > poAuxBand->GetYSize() + 1)
980
0
    {
981
0
        CPLError(CE_Failure, CPLE_AppDefined,
982
0
                 "Unexpected computed %s lower right (pixel,line)=(%f,%f)",
983
0
                 pszAuxType, dfLRPixel, dfLRLine);
984
0
        return false;
985
0
    }
986
987
0
    const int nAuxXOff = std::clamp(static_cast<int>(std::round(dfULPixel)), 0,
988
0
                                    poAuxBand->GetXSize() - 1);
989
0
    const int nAuxYOff = std::clamp(static_cast<int>(std::round(dfULLine)), 0,
990
0
                                    poAuxBand->GetYSize() - 1);
991
0
    const int nAuxX2Off = std::min(poAuxBand->GetXSize(),
992
0
                                   static_cast<int>(std::round(dfLRPixel)));
993
0
    const int nAuxY2Off =
994
0
        std::min(poAuxBand->GetYSize(), static_cast<int>(std::round(dfLRLine)));
995
996
0
    try
997
0
    {
998
0
        abyBuffer.resize(nElts * sizeof(float));
999
0
    }
1000
0
    catch (const std::bad_alloc &)
1001
0
    {
1002
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
1003
0
                 "Out of memory allocating working buffer");
1004
0
        return false;
1005
0
    }
1006
0
    GDALRasterIOExtraArg sExtraArg;
1007
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1008
0
    sExtraArg.bFloatingPointWindowValidity = true;
1009
0
    CPL_IGNORE_RET_VAL(sExtraArg.eResampleAlg);
1010
0
    sExtraArg.eResampleAlg = GRIORA_Bilinear;
1011
0
    sExtraArg.dfXOff = std::max(0.0, dfULPixel);
1012
0
    sExtraArg.dfYOff = std::max(0.0, dfULLine);
1013
0
    sExtraArg.dfXSize = std::min<double>(poAuxBand->GetXSize(), dfLRPixel) -
1014
0
                        std::max(0.0, dfULPixel);
1015
0
    sExtraArg.dfYSize = std::min<double>(poAuxBand->GetYSize(), dfLRLine) -
1016
0
                        std::max(0.0, dfULLine);
1017
0
    return (poAuxBand->RasterIO(
1018
0
                GF_Read, nAuxXOff, nAuxYOff, std::max(1, nAuxX2Off - nAuxXOff),
1019
0
                std::max(1, nAuxY2Off - nAuxYOff), abyBuffer.data(), nBufXSize,
1020
0
                nBufYSize, GDT_Float32, 0, 0, &sExtraArg) == CE_None);
1021
0
}
1022
1023
/************************************************************************/
1024
/*                      LocalScaleOffsetProcess()                       */
1025
/************************************************************************/
1026
1027
/** Processing function for 'LocalScaleOffset' builtin function. */
1028
static CPLErr LocalScaleOffsetProcess(
1029
    const char * /*pszFuncName*/, void * /*pUserData*/,
1030
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs*/,
1031
    int nBufXSize, int nBufYSize, const void *pInBuffer, size_t nInBufferSize,
1032
    GDALDataType eInDT, int nInBands, const double *CPL_RESTRICT padfInNoData,
1033
    void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT, int nOutBands,
1034
    const double *CPL_RESTRICT padfOutNoData, double dfSrcXOff,
1035
    double dfSrcYOff, double dfSrcXSize, double dfSrcYSize,
1036
    const double adfSrcGT[], const char * /* pszVRTPath */,
1037
    CSLConstList /*papszExtra*/)
1038
0
{
1039
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1040
1041
0
    CPL_IGNORE_RET_VAL(eInDT);
1042
0
    CPLAssert(eInDT == GDT_Float64);
1043
0
    CPL_IGNORE_RET_VAL(eOutDT);
1044
0
    CPLAssert(eOutDT == GDT_Float64);
1045
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
1046
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
1047
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
1048
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
1049
0
    CPLAssert(nInBands == nOutBands);
1050
0
    CPL_IGNORE_RET_VAL(nOutBands);
1051
1052
0
    LocalScaleOffsetData *data =
1053
0
        static_cast<LocalScaleOffsetData *>(pWorkingData);
1054
0
    CPLAssert(data->m_osSignature == LocalScaleOffsetData::EXPECTED_SIGNATURE);
1055
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1056
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1057
1058
    // Compute georeferenced extent of input region
1059
0
    const double dfULX =
1060
0
        adfSrcGT[0] + adfSrcGT[1] * dfSrcXOff + adfSrcGT[2] * dfSrcYOff;
1061
0
    const double dfULY =
1062
0
        adfSrcGT[3] + adfSrcGT[4] * dfSrcXOff + adfSrcGT[5] * dfSrcYOff;
1063
0
    const double dfLRX = adfSrcGT[0] + adfSrcGT[1] * (dfSrcXOff + dfSrcXSize) +
1064
0
                         adfSrcGT[2] * (dfSrcYOff + dfSrcYSize);
1065
0
    const double dfLRY = adfSrcGT[3] + adfSrcGT[4] * (dfSrcXOff + dfSrcXSize) +
1066
0
                         adfSrcGT[5] * (dfSrcYOff + dfSrcYSize);
1067
1068
0
    auto &abyOffsetBuffer = data->m_abyGainBuffer;
1069
0
    auto &abyGainBuffer = data->m_abyOffsetBuffer;
1070
1071
0
    for (int iBand = 0; iBand < nInBands; ++iBand)
1072
0
    {
1073
0
        if (!LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize,
1074
0
                         nBufYSize, "gain", data->m_oGainBands[iBand],
1075
0
                         abyGainBuffer) ||
1076
0
            !LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize,
1077
0
                         nBufYSize, "offset", data->m_oOffsetBands[iBand],
1078
0
                         abyOffsetBuffer))
1079
0
        {
1080
0
            return CE_Failure;
1081
0
        }
1082
1083
0
        const double *CPL_RESTRICT padfSrcThisBand = padfSrc + iBand;
1084
0
        double *CPL_RESTRICT padfDstThisBand = padfDst + iBand;
1085
0
        const float *pafGain =
1086
0
            reinterpret_cast<const float *>(abyGainBuffer.data());
1087
0
        const float *pafOffset =
1088
0
            reinterpret_cast<const float *>(abyOffsetBuffer.data());
1089
0
        const double dfSrcNodata = padfInNoData[iBand];
1090
0
        const double dfDstNodata = padfOutNoData[iBand];
1091
0
        const double dfGainNodata = data->m_dfGainNodata;
1092
0
        const double dfOffsetNodata = data->m_dfOffsetNodata;
1093
0
        const double dfClampMin = data->m_dfClampMin;
1094
0
        const double dfClampMax = data->m_dfClampMax;
1095
0
        for (size_t i = 0; i < nElts; ++i)
1096
0
        {
1097
0
            const double dfSrcVal = *padfSrcThisBand;
1098
            // written this way to work with a NaN value
1099
0
            if (!(dfSrcVal != dfSrcNodata))
1100
0
            {
1101
0
                *padfDstThisBand = dfDstNodata;
1102
0
            }
1103
0
            else
1104
0
            {
1105
0
                const double dfGain = pafGain[i];
1106
0
                const double dfOffset = pafOffset[i];
1107
0
                if (!(dfGain != dfGainNodata) || !(dfOffset != dfOffsetNodata))
1108
0
                {
1109
0
                    *padfDstThisBand = dfDstNodata;
1110
0
                }
1111
0
                else
1112
0
                {
1113
0
                    double dfUnscaled = dfSrcVal * dfGain - dfOffset;
1114
0
                    if (dfUnscaled < dfClampMin)
1115
0
                        dfUnscaled = dfClampMin;
1116
0
                    if (dfUnscaled > dfClampMax)
1117
0
                        dfUnscaled = dfClampMax;
1118
1119
0
                    *padfDstThisBand = dfUnscaled;
1120
0
                }
1121
0
            }
1122
0
            padfSrcThisBand += nInBands;
1123
0
            padfDstThisBand += nInBands;
1124
0
        }
1125
0
    }
1126
1127
0
    return CE_None;
1128
0
}
1129
1130
/************************************************************************/
1131
/*                             TrimmingData                             */
1132
/************************************************************************/
1133
1134
namespace
1135
{
1136
/** Working structure for 'Trimming' builtin function. */
1137
struct TrimmingData
1138
{
1139
    static constexpr const char *const EXPECTED_SIGNATURE = "Trimming";
1140
    //! Signature (to make sure callback functions are called with the right argument)
1141
    const std::string m_osSignature = EXPECTED_SIGNATURE;
1142
1143
    //! Nodata value for trimming dataset
1144
    double m_dfTrimmingNodata = std::numeric_limits<double>::quiet_NaN();
1145
1146
    //! Maximum saturating RGB output value.
1147
    double m_dfTopRGB = 0;
1148
1149
    //! Maximum threshold beyond which we give up saturation
1150
    double m_dfToneCeil = 0;
1151
1152
    //! Margin to allow for dynamics in brightest areas (in [0,1] range)
1153
    double m_dfTopMargin = 0;
1154
1155
    //! Index (zero-based) of input/output red band.
1156
    int m_nRedBand = 1 - 1;
1157
1158
    //! Index (zero-based) of input/output green band.
1159
    int m_nGreenBand = 2 - 1;
1160
1161
    //! Index (zero-based) of input/output blue band.
1162
    int m_nBlueBand = 3 - 1;
1163
1164
    //! Trimming dataset
1165
    std::unique_ptr<GDALDataset> m_poTrimmingDS{};
1166
1167
    //! Trimming raster band.
1168
    GDALRasterBand *m_poTrimmingBand = nullptr;
1169
1170
    //! Working buffer that contain trimming values.
1171
    std::vector<VRTProcessedDataset::NoInitByte> m_abyTrimmingBuffer{};
1172
};
1173
}  // namespace
1174
1175
/************************************************************************/
1176
/*                            TrimmingInit()                            */
1177
/************************************************************************/
1178
1179
/** Init function for 'Trimming' builtin function. */
1180
static CPLErr TrimmingInit(const char * /*pszFuncName*/, void * /*pUserData*/,
1181
                           CSLConstList papszFunctionArgs, int nInBands,
1182
                           GDALDataType eInDT, double *padfInNoData,
1183
                           int *pnOutBands, GDALDataType *peOutDT,
1184
                           double **ppadfOutNoData, const char *pszVRTPath,
1185
                           VRTPDWorkingDataPtr *ppWorkingData)
1186
0
{
1187
0
    CPLAssert(eInDT == GDT_Float64);
1188
1189
0
    const bool bIsFinalStep = *pnOutBands != 0;
1190
0
    *peOutDT = eInDT;
1191
0
    *ppWorkingData = nullptr;
1192
1193
0
    if (bIsFinalStep)
1194
0
    {
1195
0
        if (*pnOutBands != nInBands)
1196
0
        {
1197
0
            CPLError(CE_Failure, CPLE_NotSupported,
1198
0
                     "Trimming step: input band count (%d) is different from "
1199
0
                     "output band count (%d)",
1200
0
                     nInBands, *pnOutBands);
1201
0
            return CE_Failure;
1202
0
        }
1203
0
    }
1204
0
    else
1205
0
    {
1206
0
        *pnOutBands = nInBands;
1207
0
    }
1208
1209
0
    auto data = std::make_unique<TrimmingData>();
1210
1211
0
    bool bNodataSpecified = false;
1212
0
    double dfNoData = std::numeric_limits<double>::quiet_NaN();
1213
0
    std::string osTrimmingFilename;
1214
0
    bool bTrimmingNodataSpecified = false;
1215
0
    bool bRelativeToVRT = false;
1216
1217
0
    for (const auto &[pszKey, pszValue] :
1218
0
         cpl::IterateNameValue(papszFunctionArgs))
1219
0
    {
1220
0
        if (EQUAL(pszKey, "relativeToVRT"))
1221
0
        {
1222
0
            bRelativeToVRT = CPLTestBool(pszValue);
1223
0
        }
1224
0
        else if (EQUAL(pszKey, "nodata"))
1225
0
        {
1226
0
            bNodataSpecified = true;
1227
0
            dfNoData = CPLAtof(pszValue);
1228
0
        }
1229
0
        else if (EQUAL(pszKey, "trimming_nodata"))
1230
0
        {
1231
0
            bTrimmingNodataSpecified = true;
1232
0
            data->m_dfTrimmingNodata = CPLAtof(pszValue);
1233
0
        }
1234
0
        else if (EQUAL(pszKey, "trimming_dataset_filename"))
1235
0
        {
1236
0
            osTrimmingFilename = pszValue;
1237
0
        }
1238
0
        else if (EQUAL(pszKey, "red_band"))
1239
0
        {
1240
0
            const int nBand = atoi(pszValue) - 1;
1241
0
            if (nBand < 0 || nBand >= nInBands)
1242
0
            {
1243
0
                CPLError(CE_Failure, CPLE_AppDefined,
1244
0
                         "Invalid band in argument '%s'", pszKey);
1245
0
                return CE_Failure;
1246
0
            }
1247
0
            data->m_nRedBand = nBand;
1248
0
        }
1249
0
        else if (EQUAL(pszKey, "green_band"))
1250
0
        {
1251
0
            const int nBand = atoi(pszValue) - 1;
1252
0
            if (nBand < 0 || nBand >= nInBands)
1253
0
            {
1254
0
                CPLError(CE_Failure, CPLE_AppDefined,
1255
0
                         "Invalid band in argument '%s'", pszKey);
1256
0
                return CE_Failure;
1257
0
            }
1258
0
            data->m_nGreenBand = nBand;
1259
0
        }
1260
0
        else if (EQUAL(pszKey, "blue_band"))
1261
0
        {
1262
0
            const int nBand = atoi(pszValue) - 1;
1263
0
            if (nBand < 0 || nBand >= nInBands)
1264
0
            {
1265
0
                CPLError(CE_Failure, CPLE_AppDefined,
1266
0
                         "Invalid band in argument '%s'", pszKey);
1267
0
                return CE_Failure;
1268
0
            }
1269
0
            data->m_nBlueBand = nBand;
1270
0
        }
1271
0
        else if (EQUAL(pszKey, "top_rgb"))
1272
0
        {
1273
0
            data->m_dfTopRGB = CPLAtof(pszValue);
1274
0
        }
1275
0
        else if (EQUAL(pszKey, "tone_ceil"))
1276
0
        {
1277
0
            data->m_dfToneCeil = CPLAtof(pszValue);
1278
0
        }
1279
0
        else if (EQUAL(pszKey, "top_margin"))
1280
0
        {
1281
0
            data->m_dfTopMargin = CPLAtof(pszValue);
1282
0
        }
1283
0
        else
1284
0
        {
1285
0
            CPLError(CE_Warning, CPLE_AppDefined,
1286
0
                     "Unrecognized argument name %s. Ignored", pszKey);
1287
0
        }
1288
0
    }
1289
1290
0
    if (data->m_nRedBand == data->m_nGreenBand ||
1291
0
        data->m_nRedBand == data->m_nBlueBand ||
1292
0
        data->m_nGreenBand == data->m_nBlueBand)
1293
0
    {
1294
0
        CPLError(
1295
0
            CE_Failure, CPLE_NotSupported,
1296
0
            "red_band, green_band and blue_band must have distinct values");
1297
0
        return CE_Failure;
1298
0
    }
1299
1300
0
    const auto osFilename = GDALDataset::BuildFilename(
1301
0
        osTrimmingFilename.c_str(), pszVRTPath, bRelativeToVRT);
1302
0
    data->m_poTrimmingDS.reset(GDALDataset::Open(
1303
0
        osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
1304
0
        nullptr, nullptr));
1305
0
    if (!data->m_poTrimmingDS)
1306
0
        return CE_Failure;
1307
0
    if (data->m_poTrimmingDS->GetRasterCount() != 1)
1308
0
    {
1309
0
        CPLError(CE_Failure, CPLE_NotSupported,
1310
0
                 "Trimming dataset should have a single band");
1311
0
        return CE_Failure;
1312
0
    }
1313
0
    data->m_poTrimmingBand = data->m_poTrimmingDS->GetRasterBand(1);
1314
1315
0
    GDALGeoTransform auxGT;
1316
0
    if (data->m_poTrimmingDS->GetGeoTransform(auxGT) != CE_None)
1317
0
    {
1318
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks a geotransform",
1319
0
                 osFilename.c_str());
1320
0
        return CE_Failure;
1321
0
    }
1322
0
    int bAuxBandHasNoData = false;
1323
0
    const double dfAuxNoData =
1324
0
        data->m_poTrimmingBand->GetNoDataValue(&bAuxBandHasNoData);
1325
0
    if (!bTrimmingNodataSpecified && bAuxBandHasNoData)
1326
0
        data->m_dfTrimmingNodata = dfAuxNoData;
1327
1328
0
    SetOutputValuesForInNoDataAndOutNoData(
1329
0
        nInBands, padfInNoData, pnOutBands, ppadfOutNoData, bNodataSpecified,
1330
0
        dfNoData, bNodataSpecified, dfNoData, bIsFinalStep);
1331
1332
0
    *ppWorkingData = data.release();
1333
0
    return CE_None;
1334
0
}
1335
1336
/************************************************************************/
1337
/*                            TrimmingFree()                            */
1338
/************************************************************************/
1339
1340
/** Free function for 'Trimming' builtin function. */
1341
static void TrimmingFree(const char * /*pszFuncName*/, void * /*pUserData*/,
1342
                         VRTPDWorkingDataPtr pWorkingData)
1343
0
{
1344
0
    TrimmingData *data = static_cast<TrimmingData *>(pWorkingData);
1345
0
    CPLAssert(data->m_osSignature == TrimmingData::EXPECTED_SIGNATURE);
1346
0
    CPL_IGNORE_RET_VAL(data->m_osSignature);
1347
0
    delete data;
1348
0
}
1349
1350
/************************************************************************/
1351
/*                          TrimmingProcess()                           */
1352
/************************************************************************/
1353
1354
/** Processing function for 'Trimming' builtin function. */
1355
static CPLErr TrimmingProcess(
1356
    const char * /*pszFuncName*/, void * /*pUserData*/,
1357
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs*/,
1358
    int nBufXSize, int nBufYSize, const void *pInBuffer, size_t nInBufferSize,
1359
    GDALDataType eInDT, int nInBands, const double *CPL_RESTRICT padfInNoData,
1360
    void *pOutBuffer, size_t nOutBufferSize, GDALDataType eOutDT, int nOutBands,
1361
    const double *CPL_RESTRICT padfOutNoData, double dfSrcXOff,
1362
    double dfSrcYOff, double dfSrcXSize, double dfSrcYSize,
1363
    const double adfSrcGT[], const char * /* pszVRTPath */,
1364
    CSLConstList /*papszExtra*/)
1365
0
{
1366
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1367
1368
0
    CPL_IGNORE_RET_VAL(eInDT);
1369
0
    CPLAssert(eInDT == GDT_Float64);
1370
0
    CPL_IGNORE_RET_VAL(eOutDT);
1371
0
    CPLAssert(eOutDT == GDT_Float64);
1372
0
    CPL_IGNORE_RET_VAL(nInBufferSize);
1373
0
    CPLAssert(nInBufferSize == nElts * nInBands * sizeof(double));
1374
0
    CPL_IGNORE_RET_VAL(nOutBufferSize);
1375
0
    CPLAssert(nOutBufferSize == nElts * nOutBands * sizeof(double));
1376
0
    CPLAssert(nInBands == nOutBands);
1377
0
    CPL_IGNORE_RET_VAL(nOutBands);
1378
1379
0
    TrimmingData *data = static_cast<TrimmingData *>(pWorkingData);
1380
0
    CPLAssert(data->m_osSignature == TrimmingData::EXPECTED_SIGNATURE);
1381
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1382
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1383
1384
    // Compute georeferenced extent of input region
1385
0
    const double dfULX =
1386
0
        adfSrcGT[0] + adfSrcGT[1] * dfSrcXOff + adfSrcGT[2] * dfSrcYOff;
1387
0
    const double dfULY =
1388
0
        adfSrcGT[3] + adfSrcGT[4] * dfSrcXOff + adfSrcGT[5] * dfSrcYOff;
1389
0
    const double dfLRX = adfSrcGT[0] + adfSrcGT[1] * (dfSrcXOff + dfSrcXSize) +
1390
0
                         adfSrcGT[2] * (dfSrcYOff + dfSrcYSize);
1391
0
    const double dfLRY = adfSrcGT[3] + adfSrcGT[4] * (dfSrcXOff + dfSrcXSize) +
1392
0
                         adfSrcGT[5] * (dfSrcYOff + dfSrcYSize);
1393
1394
0
    if (!LoadAuxData(dfULX, dfULY, dfLRX, dfLRY, nElts, nBufXSize, nBufYSize,
1395
0
                     "trimming", data->m_poTrimmingBand,
1396
0
                     data->m_abyTrimmingBuffer))
1397
0
    {
1398
0
        return CE_Failure;
1399
0
    }
1400
1401
0
    const float *pafTrimming =
1402
0
        reinterpret_cast<const float *>(data->m_abyTrimmingBuffer.data());
1403
0
    const int nRedBand = data->m_nRedBand;
1404
0
    const int nGreenBand = data->m_nGreenBand;
1405
0
    const int nBlueBand = data->m_nBlueBand;
1406
0
    const double dfTopMargin = data->m_dfTopMargin;
1407
0
    const double dfTopRGB = data->m_dfTopRGB;
1408
0
    const double dfToneCeil = data->m_dfToneCeil;
1409
0
#if !defined(trimming_non_optimized_version)
1410
0
    const double dfInvToneCeil = 1.0 / dfToneCeil;
1411
0
#endif
1412
0
    const bool bRGBBandsAreFirst =
1413
0
        std::max(std::max(nRedBand, nGreenBand), nBlueBand) <= 2;
1414
0
    const double dfNoDataTrimming = data->m_dfTrimmingNodata;
1415
0
    const double dfNoDataRed = padfInNoData[nRedBand];
1416
0
    const double dfNoDataGreen = padfInNoData[nGreenBand];
1417
0
    const double dfNoDataBlue = padfInNoData[nBlueBand];
1418
0
    for (size_t i = 0; i < nElts; ++i)
1419
0
    {
1420
        // Extract local saturation value from trimming image
1421
0
        const double dfLocalMaxRGB = pafTrimming[i];
1422
0
        const double dfReducedRGB =
1423
0
            std::min((1.0 - dfTopMargin) * dfTopRGB / dfLocalMaxRGB, 1.0);
1424
1425
0
        const double dfRed = padfSrc[nRedBand];
1426
0
        const double dfGreen = padfSrc[nGreenBand];
1427
0
        const double dfBlue = padfSrc[nBlueBand];
1428
0
        bool bNoDataPixel = false;
1429
0
        if ((dfLocalMaxRGB != dfNoDataTrimming) && (dfRed != dfNoDataRed) &&
1430
0
            (dfGreen != dfNoDataGreen) && (dfBlue != dfNoDataBlue))
1431
0
        {
1432
            // RGB bands specific process
1433
0
            const double dfMaxRGB = std::max(std::max(dfRed, dfGreen), dfBlue);
1434
0
#if !defined(trimming_non_optimized_version)
1435
0
            const double dfRedTimesToneRed = std::min(dfRed, dfToneCeil);
1436
0
            const double dfGreenTimesToneGreen = std::min(dfGreen, dfToneCeil);
1437
0
            const double dfBlueTimesToneBlue = std::min(dfBlue, dfToneCeil);
1438
0
            const double dfInvToneMaxRGB =
1439
0
                std::max(dfMaxRGB * dfInvToneCeil, 1.0);
1440
0
            const double dfReducedRGBTimesInvToneMaxRGB =
1441
0
                dfReducedRGB * dfInvToneMaxRGB;
1442
0
            padfDst[nRedBand] = std::min(
1443
0
                dfRedTimesToneRed * dfReducedRGBTimesInvToneMaxRGB, dfTopRGB);
1444
0
            padfDst[nGreenBand] =
1445
0
                std::min(dfGreenTimesToneGreen * dfReducedRGBTimesInvToneMaxRGB,
1446
0
                         dfTopRGB);
1447
0
            padfDst[nBlueBand] = std::min(
1448
0
                dfBlueTimesToneBlue * dfReducedRGBTimesInvToneMaxRGB, dfTopRGB);
1449
#else
1450
            // Original formulas. Slightly less optimized than the above ones.
1451
            const double dfToneMaxRGB = std::min(dfToneCeil / dfMaxRGB, 1.0);
1452
            const double dfToneRed = std::min(dfToneCeil / dfRed, 1.0);
1453
            const double dfToneGreen = std::min(dfToneCeil / dfGreen, 1.0);
1454
            const double dfToneBlue = std::min(dfToneCeil / dfBlue, 1.0);
1455
            padfDst[nRedBand] = std::min(
1456
                dfReducedRGB * dfRed * dfToneRed / dfToneMaxRGB, dfTopRGB);
1457
            padfDst[nGreenBand] = std::min(
1458
                dfReducedRGB * dfGreen * dfToneGreen / dfToneMaxRGB, dfTopRGB);
1459
            padfDst[nBlueBand] = std::min(
1460
                dfReducedRGB * dfBlue * dfToneBlue / dfToneMaxRGB, dfTopRGB);
1461
#endif
1462
1463
            // Other bands processing (NIR, ...): only apply RGB reduction factor
1464
0
            if (bRGBBandsAreFirst)
1465
0
            {
1466
                // optimization
1467
0
                for (int iBand = 3; iBand < nInBands; ++iBand)
1468
0
                {
1469
0
                    if (padfSrc[iBand] != padfInNoData[iBand])
1470
0
                    {
1471
0
                        padfDst[iBand] = dfReducedRGB * padfSrc[iBand];
1472
0
                    }
1473
0
                    else
1474
0
                    {
1475
0
                        bNoDataPixel = true;
1476
0
                        break;
1477
0
                    }
1478
0
                }
1479
0
            }
1480
0
            else
1481
0
            {
1482
0
                for (int iBand = 0; iBand < nInBands; ++iBand)
1483
0
                {
1484
0
                    if (iBand != nRedBand && iBand != nGreenBand &&
1485
0
                        iBand != nBlueBand)
1486
0
                    {
1487
0
                        if (padfSrc[iBand] != padfInNoData[iBand])
1488
0
                        {
1489
0
                            padfDst[iBand] = dfReducedRGB * padfSrc[iBand];
1490
0
                        }
1491
0
                        else
1492
0
                        {
1493
0
                            bNoDataPixel = true;
1494
0
                            break;
1495
0
                        }
1496
0
                    }
1497
0
                }
1498
0
            }
1499
0
        }
1500
0
        else
1501
0
        {
1502
0
            bNoDataPixel = true;
1503
0
        }
1504
0
        if (bNoDataPixel)
1505
0
        {
1506
0
            for (int iBand = 0; iBand < nInBands; ++iBand)
1507
0
            {
1508
0
                padfDst[iBand] = padfOutNoData[iBand];
1509
0
            }
1510
0
        }
1511
1512
0
        padfSrc += nInBands;
1513
0
        padfDst += nInBands;
1514
0
    }
1515
1516
0
    return CE_None;
1517
0
}
1518
1519
/************************************************************************/
1520
/*                           ExpressionInit()                           */
1521
/************************************************************************/
1522
1523
namespace
1524
{
1525
1526
class ExpressionData
1527
{
1528
  public:
1529
    ExpressionData(int nInBands, int nBatchSize, std::string_view osExpression,
1530
                   std::string_view osDialect)
1531
0
        : m_nInBands(nInBands), m_nNominalBatchSize(nBatchSize),
1532
0
          m_nBatchCount(DIV_ROUND_UP(nInBands, nBatchSize)), m_adfResults{},
1533
0
          m_osExpression(std::string(osExpression)),
1534
0
          m_osDialect(std::string(osDialect)), m_oNominalBatchEnv{},
1535
0
          m_oPartialBatchEnv{}
1536
0
    {
1537
0
    }
1538
1539
    CPLErr Compile()
1540
0
    {
1541
0
        auto eErr = m_oNominalBatchEnv.Initialize(m_osExpression, m_osDialect,
1542
0
                                                  m_nNominalBatchSize);
1543
0
        if (eErr != CE_None)
1544
0
        {
1545
0
            return eErr;
1546
0
        }
1547
1548
0
        const auto nPartialBatchSize = m_nInBands % m_nNominalBatchSize;
1549
0
        if (nPartialBatchSize)
1550
0
        {
1551
0
            eErr = m_oPartialBatchEnv.Initialize(m_osExpression, m_osDialect,
1552
0
                                                 nPartialBatchSize);
1553
0
        }
1554
1555
0
        return eErr;
1556
0
    }
1557
1558
    CPLErr Evaluate(const double *padfInputs, size_t nExpectedOutBands)
1559
0
    {
1560
0
        m_adfResults.clear();
1561
1562
0
        for (int iBatch = 0; iBatch < m_nBatchCount; iBatch++)
1563
0
        {
1564
0
            const auto nBandsRemaining =
1565
0
                static_cast<int>(m_nInBands - (m_nNominalBatchSize * iBatch));
1566
0
            const auto nBatchSize =
1567
0
                std::min(m_nNominalBatchSize, nBandsRemaining);
1568
1569
0
            auto &oEnv = GetEnv(nBatchSize);
1570
1571
0
            const double *pdfStart = padfInputs + iBatch * m_nNominalBatchSize;
1572
0
            const double *pdfEnd = pdfStart + nBatchSize;
1573
1574
0
            std::copy(pdfStart, pdfEnd, oEnv.m_adfValuesForPixel.begin());
1575
1576
0
            if (auto eErr = oEnv.m_poExpression->Evaluate(); eErr != CE_None)
1577
0
            {
1578
0
                return eErr;
1579
0
            }
1580
1581
0
            const auto &adfResults = oEnv.m_poExpression->Results();
1582
0
            if (m_nBatchCount > 1)
1583
0
            {
1584
0
                std::copy(adfResults.begin(), adfResults.end(),
1585
0
                          std::back_inserter(m_adfResults));
1586
0
            }
1587
0
        }
1588
1589
0
        if (nExpectedOutBands > 0)
1590
0
        {
1591
0
            if (Results().size() != static_cast<std::size_t>(nExpectedOutBands))
1592
0
            {
1593
0
                CPLError(CE_Failure, CPLE_AppDefined,
1594
0
                         "Expression returned %d values but "
1595
0
                         "%d output bands were expected.",
1596
0
                         static_cast<int>(Results().size()),
1597
0
                         static_cast<int>(nExpectedOutBands));
1598
0
                return CE_Failure;
1599
0
            }
1600
0
        }
1601
1602
0
        return CE_None;
1603
0
    }
1604
1605
    const std::vector<double> &Results() const
1606
0
    {
1607
0
        if (m_nBatchCount == 1)
1608
0
        {
1609
0
            return m_oNominalBatchEnv.m_poExpression->Results();
1610
0
        }
1611
0
        else
1612
0
        {
1613
0
            return m_adfResults;
1614
0
        }
1615
0
    }
1616
1617
  private:
1618
    const int m_nInBands;
1619
    const int m_nNominalBatchSize;
1620
    const int m_nBatchCount;
1621
    std::vector<double> m_adfResults;
1622
1623
    const CPLString m_osExpression;
1624
    const CPLString m_osDialect;
1625
1626
    struct InvocationEnv
1627
    {
1628
        std::vector<double> m_adfValuesForPixel;
1629
        std::unique_ptr<gdal::MathExpression> m_poExpression;
1630
1631
        CPLErr Initialize(const CPLString &osExpression,
1632
                          const CPLString &osDialect, int nBatchSize)
1633
0
        {
1634
0
            m_poExpression =
1635
0
                gdal::MathExpression::Create(osExpression, osDialect.c_str());
1636
            // cppcheck-suppress knownConditionTrueFalse
1637
0
            if (m_poExpression == nullptr)
1638
0
            {
1639
0
                return CE_Failure;
1640
0
            }
1641
1642
0
            m_adfValuesForPixel.resize(nBatchSize);
1643
1644
0
            for (int i = 0; i < nBatchSize; i++)
1645
0
            {
1646
0
                std::string osVar = "B" + std::to_string(i + 1);
1647
0
                m_poExpression->RegisterVariable(osVar,
1648
0
                                                 &m_adfValuesForPixel[i]);
1649
0
            }
1650
1651
0
            if (osExpression.ifind("BANDS") != std::string::npos)
1652
0
            {
1653
0
                m_poExpression->RegisterVector("BANDS", &m_adfValuesForPixel);
1654
0
            }
1655
1656
0
            return m_poExpression->Compile();
1657
0
        }
1658
    };
1659
1660
    InvocationEnv &GetEnv(int nBatchSize)
1661
0
    {
1662
0
        if (nBatchSize == m_nNominalBatchSize)
1663
0
        {
1664
0
            return m_oNominalBatchEnv;
1665
0
        }
1666
0
        else
1667
0
        {
1668
0
            return m_oPartialBatchEnv;
1669
0
        }
1670
0
    }
1671
1672
    InvocationEnv m_oNominalBatchEnv;
1673
    InvocationEnv m_oPartialBatchEnv;
1674
};
1675
1676
}  // namespace
1677
1678
static CPLErr ExpressionInit(const char * /*pszFuncName*/, void * /*pUserData*/,
1679
                             CSLConstList papszFunctionArgs, int nInBands,
1680
                             GDALDataType eInDT, double * /* padfInNoData */,
1681
                             int *pnOutBands, GDALDataType *peOutDT,
1682
                             double ** /* ppadfOutNoData */,
1683
                             const char * /* pszVRTPath */,
1684
                             VRTPDWorkingDataPtr *ppWorkingData)
1685
0
{
1686
0
    CPLAssert(eInDT == GDT_Float64);
1687
1688
0
    *peOutDT = eInDT;
1689
0
    *ppWorkingData = nullptr;
1690
1691
0
    const char *pszBatchSize =
1692
0
        CSLFetchNameValue(papszFunctionArgs, "batch_size");
1693
0
    auto nBatchSize = nInBands;
1694
1695
0
    if (pszBatchSize != nullptr)
1696
0
    {
1697
0
        nBatchSize = std::min(nInBands, std::atoi(pszBatchSize));
1698
0
    }
1699
1700
0
    if (nBatchSize < 1)
1701
0
    {
1702
0
        CPLError(CE_Failure, CPLE_IllegalArg, "batch_size must be at least 1");
1703
0
        return CE_Failure;
1704
0
    }
1705
1706
0
    const char *pszDialect = CSLFetchNameValue(papszFunctionArgs, "dialect");
1707
0
    if (pszDialect == nullptr)
1708
0
    {
1709
0
        pszDialect = "muparser";
1710
0
    }
1711
1712
0
    const char *pszExpression =
1713
0
        CSLFetchNameValue(papszFunctionArgs, "expression");
1714
1715
0
    auto data = std::make_unique<ExpressionData>(nInBands, nBatchSize,
1716
0
                                                 pszExpression, pszDialect);
1717
1718
0
    if (auto eErr = data->Compile(); eErr != CE_None)
1719
0
    {
1720
0
        return eErr;
1721
0
    }
1722
1723
0
    if (*pnOutBands == 0)
1724
0
    {
1725
0
        std::vector<double> aDummyValues(nInBands);
1726
0
        if (auto eErr = data->Evaluate(aDummyValues.data(), 0); eErr != CE_None)
1727
0
        {
1728
0
            return eErr;
1729
0
        }
1730
1731
0
        *pnOutBands = static_cast<int>(data->Results().size());
1732
0
    }
1733
1734
0
    *ppWorkingData = data.release();
1735
1736
0
    return CE_None;
1737
0
}
1738
1739
static void ExpressionFree(const char * /* pszFuncName */,
1740
                           void * /* pUserData */,
1741
                           VRTPDWorkingDataPtr pWorkingData)
1742
0
{
1743
0
    ExpressionData *data = static_cast<ExpressionData *>(pWorkingData);
1744
0
    delete data;
1745
0
}
1746
1747
static CPLErr ExpressionProcess(
1748
    const char * /* pszFuncName */, void * /* pUserData */,
1749
    VRTPDWorkingDataPtr pWorkingData, CSLConstList /* papszFunctionArgs */,
1750
    int nBufXSize, int nBufYSize, const void *pInBuffer,
1751
    size_t /* nInBufferSize */, GDALDataType eInDT, int nInBands,
1752
    const double *CPL_RESTRICT /* padfInNoData */, void *pOutBuffer,
1753
    size_t /* nOutBufferSize */, GDALDataType eOutDT, int nOutBands,
1754
    const double *CPL_RESTRICT /* padfOutNoData */, double /* dfSrcXOff */,
1755
    double /* dfSrcYOff */, double /* dfSrcXSize */, double /* dfSrcYSize */,
1756
    const double /* adfSrcGT */[], const char * /* pszVRTPath "*/,
1757
    CSLConstList /* papszExtra */)
1758
0
{
1759
0
    ExpressionData *expr = static_cast<ExpressionData *>(pWorkingData);
1760
1761
0
    const size_t nElts = static_cast<size_t>(nBufXSize) * nBufYSize;
1762
1763
0
    CPL_IGNORE_RET_VAL(eInDT);
1764
0
    CPLAssert(eInDT == GDT_Float64);
1765
0
    const double *CPL_RESTRICT padfSrc = static_cast<const double *>(pInBuffer);
1766
1767
0
    CPLAssert(eOutDT == GDT_Float64);
1768
0
    CPL_IGNORE_RET_VAL(eOutDT);
1769
0
    double *CPL_RESTRICT padfDst = static_cast<double *>(pOutBuffer);
1770
1771
0
    for (size_t i = 0; i < nElts; i++)
1772
0
    {
1773
0
        if (auto eErr = expr->Evaluate(padfSrc, nOutBands); eErr != CE_None)
1774
0
        {
1775
0
            return eErr;
1776
0
        }
1777
1778
0
        const auto &adfResults = expr->Results();
1779
0
        std::copy(adfResults.begin(), adfResults.end(), padfDst);
1780
1781
0
        padfDst += nOutBands;
1782
0
        padfSrc += nInBands;
1783
0
    }
1784
1785
0
    return CE_None;
1786
0
}
1787
1788
/************************************************************************/
1789
/*            GDALVRTRegisterDefaultProcessedDatasetFuncs()             */
1790
/************************************************************************/
1791
1792
/** Register builtin functions that can be used in a VRTProcessedDataset.
1793
 */
1794
void GDALVRTRegisterDefaultProcessedDatasetFuncs()
1795
1
{
1796
1
    GDALVRTRegisterProcessedDatasetFunc(
1797
1
        "BandAffineCombination", nullptr,
1798
1
        "<ProcessedDatasetFunctionArgumentsList>"
1799
1
        "   <Argument name='src_nodata' type='double' "
1800
1
        "description='Override input nodata value'/>"
1801
1
        "   <Argument name='dst_nodata' type='double' "
1802
1
        "description='Override output nodata value'/>"
1803
1
        "   <Argument name='replacement_nodata' "
1804
1
        "description='value to substitute to a valid computed value that "
1805
1
        "would be nodata' type='double'/>"
1806
1
        "   <Argument name='dst_intended_datatype' type='string' "
1807
1
        "description='Intented datatype of output (which might be "
1808
1
        "different than the working data type)'/>"
1809
1
        "   <Argument name='coefficients_{band}' "
1810
1
        "description='Comma-separated coefficients for combining bands. "
1811
1
        "First one is constant term' "
1812
1
        "type='double_list' required='true'/>"
1813
1
        "   <Argument name='min' description='clamp min value' type='double'/>"
1814
1
        "   <Argument name='max' description='clamp max value' type='double'/>"
1815
1
        "</ProcessedDatasetFunctionArgumentsList>",
1816
1
        GDT_Float64, nullptr, 0, nullptr, 0, BandAffineCombinationInit,
1817
1
        BandAffineCombinationFree, BandAffineCombinationProcess, nullptr);
1818
1819
1
    GDALVRTRegisterProcessedDatasetFunc(
1820
1
        "LUT", nullptr,
1821
1
        "<ProcessedDatasetFunctionArgumentsList>"
1822
1
        "   <Argument name='src_nodata' type='double' "
1823
1
        "description='Override input nodata value'/>"
1824
1
        "   <Argument name='dst_nodata' type='double' "
1825
1
        "description='Override output nodata value'/>"
1826
1
        "   <Argument name='lut_{band}' "
1827
1
        "description='List of the form [src value 1]:[dest value 1],"
1828
1
        "[src value 2]:[dest value 2],...' "
1829
1
        "type='string' required='true'/>"
1830
1
        "</ProcessedDatasetFunctionArgumentsList>",
1831
1
        GDT_Float64, nullptr, 0, nullptr, 0, LUTInit, LUTFree, LUTProcess,
1832
1
        nullptr);
1833
1834
1
    GDALVRTRegisterProcessedDatasetFunc(
1835
1
        "LocalScaleOffset", nullptr,
1836
1
        "<ProcessedDatasetFunctionArgumentsList>"
1837
1
        "   <Argument name='relativeToVRT' "
1838
1
        "description='Whether gain and offset filenames are relative to "
1839
1
        "the VRT' type='boolean' default='false'/>"
1840
1
        "   <Argument name='gain_dataset_filename_{band}' "
1841
1
        "description='Filename to the gain dataset' "
1842
1
        "type='string' required='true'/>"
1843
1
        "   <Argument name='gain_dataset_band_{band}' "
1844
1
        "description='Band of the gain dataset' "
1845
1
        "type='integer' required='true'/>"
1846
1
        "   <Argument name='offset_dataset_filename_{band}' "
1847
1
        "description='Filename to the offset dataset' "
1848
1
        "type='string' required='true'/>"
1849
1
        "   <Argument name='offset_dataset_band_{band}' "
1850
1
        "description='Band of the offset dataset' "
1851
1
        "type='integer' required='true'/>"
1852
1
        "   <Argument name='min' description='clamp min value' type='double'/>"
1853
1
        "   <Argument name='max' description='clamp max value' type='double'/>"
1854
1
        "   <Argument name='nodata' type='double' "
1855
1
        "description='Override dataset nodata value'/>"
1856
1
        "   <Argument name='gain_nodata' type='double' "
1857
1
        "description='Override gain dataset nodata value'/>"
1858
1
        "   <Argument name='offset_nodata' type='double' "
1859
1
        "description='Override offset dataset nodata value'/>"
1860
1
        "</ProcessedDatasetFunctionArgumentsList>",
1861
1
        GDT_Float64, nullptr, 0, nullptr, 0, LocalScaleOffsetInit,
1862
1
        LocalScaleOffsetFree, LocalScaleOffsetProcess, nullptr);
1863
1864
1
    GDALVRTRegisterProcessedDatasetFunc(
1865
1
        "Trimming", nullptr,
1866
1
        "<ProcessedDatasetFunctionArgumentsList>"
1867
1
        "   <Argument name='relativeToVRT' "
1868
1
        "description='Whether trimming_dataset_filename is relative to the VRT'"
1869
1
        " type='boolean' default='false'/>"
1870
1
        "   <Argument name='trimming_dataset_filename' "
1871
1
        "description='Filename to the trimming dataset' "
1872
1
        "type='string' required='true'/>"
1873
1
        "   <Argument name='red_band' type='integer' default='1'/>"
1874
1
        "   <Argument name='green_band' type='integer' default='2'/>"
1875
1
        "   <Argument name='blue_band' type='integer' default='3'/>"
1876
1
        "   <Argument name='top_rgb' "
1877
1
        "description='Maximum saturating RGB output value' "
1878
1
        "type='double' required='true'/>"
1879
1
        "   <Argument name='tone_ceil' "
1880
1
        "description='Maximum threshold beyond which we give up saturation' "
1881
1
        "type='double' required='true'/>"
1882
1
        "   <Argument name='top_margin' "
1883
1
        "description='Margin to allow for dynamics in brightest areas "
1884
1
        "(between 0 and 1, should be close to 0)' "
1885
1
        "type='double' required='true'/>"
1886
1
        "   <Argument name='nodata' type='double' "
1887
1
        "description='Override dataset nodata value'/>"
1888
1
        "   <Argument name='trimming_nodata' type='double' "
1889
1
        "description='Override trimming dataset nodata value'/>"
1890
1
        "</ProcessedDatasetFunctionArgumentsList>",
1891
1
        GDT_Float64, nullptr, 0, nullptr, 0, TrimmingInit, TrimmingFree,
1892
1
        TrimmingProcess, nullptr);
1893
1894
1
    GDALVRTRegisterProcessedDatasetFunc(
1895
1
        "Expression", nullptr,
1896
1
        "<ProcessedDatasetFunctionArgumentsList>"
1897
1
        "    <Argument name='expression' description='the expression to "
1898
1
        "evaluate' type='string' required='true' />"
1899
1
        "    <Argument name='dialect' description='expression dialect' "
1900
1
        "type='string' />"
1901
1
        "    <Argument name='batch_size' description='batch size' "
1902
1
        "type='integer' />"
1903
1
        "</ProcessedDatasetFunctionArgumentsList>",
1904
1
        GDT_Float64, nullptr, 0, nullptr, 0, ExpressionInit, ExpressionFree,
1905
1
        ExpressionProcess, nullptr);
1906
1
}