Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdalnodatamaskband.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Implementation of GDALNoDataMaskBand, a class implementing all
5
 *           a default band mask based on nodata values.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2007, Frank Warmerdam
10
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_port.h"
16
#include "gdal_priv.h"
17
18
#include <algorithm>
19
#include <cassert>
20
#include <cmath>
21
#include <cstring>
22
#include <utility>
23
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_vsi.h"
27
#include "gdal.h"
28
#include "gdal_priv_templates.hpp"
29
#include "gdal_typetraits.h"
30
#include "vrtdataset.h"
31
32
//! @cond Doxygen_Suppress
33
/************************************************************************/
34
/*                         GDALNoDataMaskBand()                         */
35
/************************************************************************/
36
37
GDALNoDataMaskBand::GDALNoDataMaskBand(GDALRasterBand *poParentIn)
38
0
    : m_poParent(poParentIn)
39
0
{
40
0
    poDS = nullptr;
41
0
    nBand = 0;
42
43
0
    nRasterXSize = m_poParent->GetXSize();
44
0
    nRasterYSize = m_poParent->GetYSize();
45
46
0
    eDataType = GDT_UInt8;
47
0
    m_poParent->GetBlockSize(&nBlockXSize, &nBlockYSize);
48
49
0
    const auto eParentDT = m_poParent->GetRasterDataType();
50
0
    if (eParentDT == GDT_Int64)
51
0
        m_nNoDataValueInt64 = m_poParent->GetNoDataValueAsInt64();
52
0
    else if (eParentDT == GDT_UInt64)
53
0
        m_nNoDataValueUInt64 = m_poParent->GetNoDataValueAsUInt64();
54
0
    else
55
0
        m_dfNoDataValue = m_poParent->GetNoDataValue();
56
0
}
57
58
/************************************************************************/
59
/*                         GDALNoDataMaskBand()                         */
60
/************************************************************************/
61
62
GDALNoDataMaskBand::GDALNoDataMaskBand(GDALRasterBand *poParentIn,
63
                                       double dfNoDataValue)
64
0
    : m_poParent(poParentIn)
65
0
{
66
0
    poDS = nullptr;
67
0
    nBand = 0;
68
69
0
    nRasterXSize = m_poParent->GetXSize();
70
0
    nRasterYSize = m_poParent->GetYSize();
71
72
0
    eDataType = GDT_UInt8;
73
0
    m_poParent->GetBlockSize(&nBlockXSize, &nBlockYSize);
74
75
0
    const auto eParentDT = m_poParent->GetRasterDataType();
76
0
    if (eParentDT == GDT_Int64)
77
0
        m_nNoDataValueInt64 = static_cast<int64_t>(dfNoDataValue);
78
0
    else if (eParentDT == GDT_UInt64)
79
0
        m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoDataValue);
80
0
    else
81
0
        m_dfNoDataValue = dfNoDataValue;
82
0
}
83
84
/************************************************************************/
85
/*                        ~GDALNoDataMaskBand()                         */
86
/************************************************************************/
87
88
0
GDALNoDataMaskBand::~GDALNoDataMaskBand() = default;
89
90
/************************************************************************/
91
/*                          GetWorkDataType()                           */
92
/************************************************************************/
93
94
static GDALDataType GetWorkDataType(GDALDataType eDataType)
95
0
{
96
0
    GDALDataType eWrkDT = GDT_Unknown;
97
0
    switch (eDataType)
98
0
    {
99
0
        case GDT_UInt8:
100
0
            eWrkDT = GDT_UInt8;
101
0
            break;
102
103
0
        case GDT_Int16:
104
0
            eWrkDT = GDT_Int16;
105
0
            break;
106
107
0
        case GDT_UInt16:
108
0
            eWrkDT = GDT_UInt16;
109
0
            break;
110
111
0
        case GDT_UInt32:
112
0
            eWrkDT = GDT_UInt32;
113
0
            break;
114
115
0
        case GDT_Int8:
116
0
        case GDT_Int32:
117
0
        case GDT_CInt16:
118
0
        case GDT_CInt32:
119
0
            eWrkDT = GDT_Int32;
120
0
            break;
121
122
0
        case GDT_Float16:
123
0
        case GDT_CFloat16:
124
0
        case GDT_Float32:
125
0
        case GDT_CFloat32:
126
0
            eWrkDT = GDT_Float32;
127
0
            break;
128
129
0
        case GDT_Float64:
130
0
        case GDT_CFloat64:
131
0
            eWrkDT = GDT_Float64;
132
0
            break;
133
134
0
        case GDT_Int64:
135
0
        case GDT_UInt64:
136
0
            eWrkDT = eDataType;
137
0
            break;
138
139
0
        case GDT_Unknown:
140
0
        case GDT_TypeCount:
141
0
            CPLAssert(false);
142
0
            eWrkDT = GDT_Float64;
143
0
            break;
144
0
    }
145
0
    return eWrkDT;
146
0
}
147
148
/************************************************************************/
149
/*                          IsNoDataInRange()                           */
150
/************************************************************************/
151
152
bool GDALNoDataMaskBand::IsNoDataInRange(double dfNoDataValue,
153
                                         GDALDataType eDataTypeIn)
154
0
{
155
0
    GDALDataType eWrkDT = GetWorkDataType(eDataTypeIn);
156
0
    switch (eWrkDT)
157
0
    {
158
0
        case GDT_UInt8:
159
0
        {
160
0
            return GDALIsValueInRange<GByte>(dfNoDataValue);
161
0
        }
162
163
0
        case GDT_Int8:
164
0
        {
165
0
            return GDALIsValueInRange<signed char>(dfNoDataValue);
166
0
        }
167
168
0
        case GDT_Int16:
169
0
        {
170
0
            return GDALIsValueInRange<GInt16>(dfNoDataValue);
171
0
        }
172
173
0
        case GDT_UInt16:
174
0
        {
175
0
            return GDALIsValueInRange<GUInt16>(dfNoDataValue);
176
0
        }
177
178
0
        case GDT_UInt32:
179
0
        {
180
0
            return GDALIsValueInRange<GUInt32>(dfNoDataValue);
181
0
        }
182
0
        case GDT_Int32:
183
0
        {
184
0
            return GDALIsValueInRange<GInt32>(dfNoDataValue);
185
0
        }
186
187
0
        case GDT_UInt64:
188
0
        {
189
0
            return GDALIsValueInRange<uint64_t>(dfNoDataValue);
190
0
        }
191
192
0
        case GDT_Int64:
193
0
        {
194
0
            return GDALIsValueInRange<int64_t>(dfNoDataValue);
195
0
        }
196
197
0
        case GDT_Float16:
198
0
        {
199
0
            return std::isnan(dfNoDataValue) || std::isinf(dfNoDataValue) ||
200
0
                   GDALIsValueInRange<GFloat16>(dfNoDataValue);
201
0
        }
202
203
0
        case GDT_Float32:
204
0
        {
205
0
            return std::isnan(dfNoDataValue) || std::isinf(dfNoDataValue) ||
206
0
                   GDALIsValueInRange<float>(dfNoDataValue);
207
0
        }
208
209
0
        case GDT_Float64:
210
0
        {
211
0
            return true;
212
0
        }
213
214
0
        case GDT_CFloat16:
215
0
        case GDT_CFloat32:
216
0
        case GDT_CFloat64:
217
0
        case GDT_CInt16:
218
0
        case GDT_CInt32:
219
0
        case GDT_Unknown:
220
0
        case GDT_TypeCount:
221
0
            break;
222
0
    }
223
224
0
    CPLAssert(false);
225
0
    return false;
226
0
}
227
228
/************************************************************************/
229
/*                             IReadBlock()                             */
230
/************************************************************************/
231
232
CPLErr GDALNoDataMaskBand::IReadBlock(int nXBlockOff, int nYBlockOff,
233
                                      void *pImage)
234
235
0
{
236
0
    const int nXOff = nXBlockOff * nBlockXSize;
237
0
    const int nXSizeRequest = std::min(nBlockXSize, nRasterXSize - nXOff);
238
0
    const int nYOff = nYBlockOff * nBlockYSize;
239
0
    const int nYSizeRequest = std::min(nBlockYSize, nRasterYSize - nYOff);
240
241
0
    if (nBlockXSize != nXSizeRequest || nBlockYSize != nYSizeRequest)
242
0
    {
243
0
        memset(pImage, 0, static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
244
0
    }
245
246
0
    GDALRasterIOExtraArg sExtraArg;
247
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
248
0
    return IRasterIO(GF_Read, nXOff, nYOff, nXSizeRequest, nYSizeRequest,
249
0
                     pImage, nXSizeRequest, nYSizeRequest, GDT_UInt8, 1,
250
0
                     nBlockXSize, &sExtraArg);
251
0
}
252
253
/************************************************************************/
254
/*                            SetZeroOr255()                            */
255
/************************************************************************/
256
257
#if (defined(__GNUC__) && !defined(__clang__))
258
__attribute__((optimize("tree-vectorize")))
259
#endif
260
static void SetZeroOr255(GByte *pabyDestAndSrc, size_t nBufSize, GByte byNoData)
261
0
{
262
0
    for (size_t i = 0; i < nBufSize; ++i)
263
0
    {
264
0
        pabyDestAndSrc[i] = (pabyDestAndSrc[i] == byNoData) ? 0 : 255;
265
0
    }
266
0
}
267
268
template <class T>
269
#if (defined(__GNUC__) && !defined(__clang__))
270
__attribute__((optimize("tree-vectorize")))
271
#endif
272
static void SetZeroOr255(GByte *pabyDest, const T *panSrc, size_t nBufSize,
273
                         T nNoData)
274
0
{
275
0
    for (size_t i = 0; i < nBufSize; ++i)
276
0
    {
277
0
        pabyDest[i] = (panSrc[i] == nNoData) ? 0 : 255;
278
0
    }
279
0
}
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned char>(unsigned char*, unsigned char const*, unsigned long, unsigned char)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<short>(unsigned char*, short const*, unsigned long, short)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned short>(unsigned char*, unsigned short const*, unsigned long, unsigned short)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned int>(unsigned char*, unsigned int const*, unsigned long, unsigned int)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<int>(unsigned char*, int const*, unsigned long, int)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<long>(unsigned char*, long const*, unsigned long, long)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned long>(unsigned char*, unsigned long const*, unsigned long, unsigned long)
280
281
template <class T>
282
static void SetZeroOr255(GByte *pabyDest, const T *panSrc, int nBufXSize,
283
                         int nBufYSize, GSpacing nPixelSpace,
284
                         GSpacing nLineSpace, T nNoData)
285
0
{
286
0
    if (nPixelSpace == 1)
287
0
    {
288
0
        for (int iY = 0; iY < nBufYSize; iY++)
289
0
        {
290
0
            SetZeroOr255(pabyDest, panSrc, nBufXSize, nNoData);
291
0
            pabyDest += nLineSpace;
292
0
            panSrc += nBufXSize;
293
0
        }
294
0
    }
295
0
    else
296
0
    {
297
0
        size_t i = 0;
298
0
        for (int iY = 0; iY < nBufYSize; iY++)
299
0
        {
300
0
            GByte *pabyLineDest = pabyDest + iY * nLineSpace;
301
0
            for (int iX = 0; iX < nBufXSize; iX++)
302
0
            {
303
0
                *pabyLineDest = (panSrc[i] == nNoData) ? 0 : 255;
304
0
                ++i;
305
0
                pabyLineDest += nPixelSpace;
306
0
            }
307
0
        }
308
0
    }
309
0
}
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned char>(unsigned char*, unsigned char const*, int, int, long long, long long, unsigned char)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<short>(unsigned char*, short const*, int, int, long long, long long, short)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned short>(unsigned char*, unsigned short const*, int, int, long long, long long, unsigned short)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned int>(unsigned char*, unsigned int const*, int, int, long long, long long, unsigned int)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<int>(unsigned char*, int const*, int, int, long long, long long, int)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<long>(unsigned char*, long const*, int, int, long long, long long, long)
Unexecuted instantiation: gdalnodatamaskband.cpp:void SetZeroOr255<unsigned long>(unsigned char*, unsigned long const*, int, int, long long, long long, unsigned long)
310
311
/************************************************************************/
312
/*                         ParentBandIsNoData()                         */
313
/************************************************************************/
314
315
/** Callback function for a VRT raster band. Transforms input band into
316
 * a NoData mask, which can then be resampled.
317
 */
318
template <GDALDataType GDALTransferDataType>
319
static CPLErr ParentBandIsNoData(void *poData, int nXOff, int nYOff, int nXSize,
320
                                 int nYSize, void *pData)
321
0
{
322
0
    using TransferType =
323
0
        typename gdal::GDALDataTypeTraits<GDALTransferDataType>::type;
324
325
0
    GDALRasterBand *poSrcBand = static_cast<GDALRasterBand *>(poData);
326
327
0
    std::unique_ptr<TransferType, decltype(&CPLFree)> pabySrc{
328
0
        static_cast<TransferType *>(
329
0
            VSI_MALLOC3_VERBOSE(sizeof(TransferType), nXSize, nYSize)),
330
0
        CPLFree};
331
0
    if (pabySrc == nullptr)
332
0
    {
333
0
        return CE_Failure;
334
0
    }
335
336
0
    const auto eErr = poSrcBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
337
0
                                          pabySrc.get(), nXSize, nYSize,
338
0
                                          GDALTransferDataType, 0, 0, nullptr);
339
0
    if (eErr != CE_None)
340
0
    {
341
0
        return eErr;
342
0
    }
343
344
0
    const size_t nPixelCount = static_cast<size_t>(nXSize) * nYSize;
345
0
    GByte *pabyDst = static_cast<GByte *>(pData);
346
347
0
    const auto maybeNoData = poSrcBand->GetNoDataValue<TransferType>();
348
0
    if (!maybeNoData.has_value())
349
0
    {
350
0
        std::fill(pabyDst, pabyDst + nPixelCount, 255);
351
0
        return CE_None;
352
0
    }
353
0
    const TransferType noData = maybeNoData.value();
354
355
0
    for (size_t i = 0; i < nPixelCount; i++)
356
0
    {
357
        if constexpr (std::is_floating_point_v<TransferType>)
358
0
        {
359
0
            pabyDst[i] = (std::isnan(noData) && std::isnan(pabySrc.get()[i])) ||
360
0
                                 ARE_REAL_EQUAL(pabySrc.get()[i], noData)
361
0
                             ? 0
362
0
                             : 255;
363
        }
364
        else
365
0
        {
366
0
            pabyDst[i] = pabySrc.get()[i] == noData ? 0 : 255;
367
0
        }
368
0
    }
369
370
0
    return CE_None;
371
0
}
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)1>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)3>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)2>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)5>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)4>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)6>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)7>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)13>(void*, int, int, int, int, void*)
Unexecuted instantiation: gdalnodatamaskband.cpp:CPLErr ParentBandIsNoData<(GDALDataType)12>(void*, int, int, int, int, void*)
372
373
/************************************************************************/
374
/*                             IRasterIO()                              */
375
/************************************************************************/
376
377
CPLErr GDALNoDataMaskBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
378
                                     int nXSize, int nYSize, void *pData,
379
                                     int nBufXSize, int nBufYSize,
380
                                     GDALDataType eBufType,
381
                                     GSpacing nPixelSpace, GSpacing nLineSpace,
382
                                     GDALRasterIOExtraArg *psExtraArg)
383
0
{
384
0
    if (eRWFlag != GF_Read)
385
0
    {
386
0
        return CE_Failure;
387
0
    }
388
389
0
    const auto eParentDT = m_poParent->GetRasterDataType();
390
0
    const GDALDataType eWrkDT = GetWorkDataType(eParentDT);
391
392
0
    if (psExtraArg->eResampleAlg == GRIORA_Mode)
393
0
    {
394
        // For mode resampling, we have to read the parent band at full resolution
395
        // and classify each pixel as valid / NoData.
396
0
        auto poVRT = VRTDataset::CreateVRTDataset("", nXSize, nYSize, 0,
397
0
                                                  GDT_UInt8, nullptr);
398
0
        poVRT->AddBand(GDT_Byte, nullptr);
399
0
        VRTSourcedRasterBand *poVRTBand =
400
0
            cpl::down_cast<VRTSourcedRasterBand *>(poVRT->GetRasterBand(1));
401
402
0
        switch (eWrkDT)
403
0
        {
404
0
            case GDT_UInt8:
405
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_UInt8>,
406
0
                                         m_poParent);
407
0
                break;
408
0
            case GDT_Int16:
409
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_Int16>,
410
0
                                         m_poParent);
411
0
                break;
412
0
            case GDT_UInt16:
413
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_UInt16>,
414
0
                                         m_poParent);
415
0
                break;
416
0
            case GDT_Int32:
417
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_Int32>,
418
0
                                         m_poParent);
419
0
                break;
420
0
            case GDT_UInt32:
421
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_UInt32>,
422
0
                                         m_poParent);
423
0
                break;
424
0
            case GDT_Float32:
425
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_Float32>,
426
0
                                         m_poParent);
427
0
                break;
428
0
            case GDT_Float64:
429
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_Float64>,
430
0
                                         m_poParent);
431
0
                break;
432
0
            case GDT_Int64:
433
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_Int64>,
434
0
                                         m_poParent);
435
0
                break;
436
0
            case GDT_UInt64:
437
0
                poVRTBand->AddFuncSource(ParentBandIsNoData<GDT_UInt64>,
438
0
                                         m_poParent);
439
0
                break;
440
0
            default:
441
0
                CPLAssert(false);
442
0
        }
443
444
0
        return poVRTBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pData,
445
0
                                   nBufXSize, nBufYSize, eBufType, nPixelSpace,
446
0
                                   nLineSpace, psExtraArg);
447
0
    }
448
449
    // Optimization in common use case (#4488).
450
    // This avoids triggering the block cache on this band, which helps
451
    // reducing the global block cache consumption.
452
0
    if (eBufType == GDT_UInt8 && eWrkDT == GDT_UInt8 && nPixelSpace == 1 &&
453
0
        nLineSpace >= nBufXSize)
454
0
    {
455
0
        const CPLErr eErr = m_poParent->RasterIO(
456
0
            GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
457
0
            eBufType, nPixelSpace, nLineSpace, psExtraArg);
458
0
        if (eErr != CE_None)
459
0
            return eErr;
460
461
0
        GByte *pabyData = static_cast<GByte *>(pData);
462
0
        const GByte byNoData = static_cast<GByte>(m_dfNoDataValue);
463
464
0
        if (nLineSpace == nBufXSize)
465
0
        {
466
0
            const size_t nBufSize = static_cast<size_t>(nBufXSize) * nBufYSize;
467
0
            SetZeroOr255(pabyData, nBufSize, byNoData);
468
0
        }
469
0
        else
470
0
        {
471
0
            assert(nLineSpace > nBufXSize);
472
0
            for (int iY = 0; iY < nBufYSize; iY++)
473
0
            {
474
0
                SetZeroOr255(pabyData, nBufXSize, byNoData);
475
0
                pabyData += nLineSpace;
476
0
            }
477
0
        }
478
0
        return CE_None;
479
0
    }
480
481
0
    const auto AllocTempBufferOrFallback =
482
0
        [this, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
483
0
         nBufYSize, eBufType, nPixelSpace, nLineSpace,
484
0
         psExtraArg](int nWrkDTSize) -> std::pair<CPLErr, void *>
485
0
    {
486
0
        auto poParentDS = m_poParent->GetDataset();
487
        // Check if we must simulate a memory allocation failure
488
        // Before checking the env variable, which is slightly expensive,
489
        // check first for a special dataset name, which is a cheap test.
490
0
        const char *pszOptVal =
491
0
            poParentDS && strcmp(poParentDS->GetDescription(), "__debug__") == 0
492
0
                ? CPLGetConfigOption(
493
0
                      "GDAL_SIMUL_MEM_ALLOC_FAILURE_NODATA_MASK_BAND", "NO")
494
0
                : "NO";
495
0
        const bool bSimulMemAllocFailure =
496
0
            EQUAL(pszOptVal, "ALWAYS") ||
497
0
            (CPLTestBool(pszOptVal) &&
498
0
             GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") ==
499
0
                 nullptr);
500
0
        void *pTemp = nullptr;
501
0
        if (!bSimulMemAllocFailure)
502
0
        {
503
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
504
0
            pTemp = VSI_MALLOC3_VERBOSE(nWrkDTSize, nBufXSize, nBufYSize);
505
0
        }
506
0
        if (!pTemp)
507
0
        {
508
0
            const bool bAllocHasAlreadyFailed =
509
0
                GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") !=
510
0
                nullptr;
511
0
            CPLError(bAllocHasAlreadyFailed ? CE_Failure : CE_Warning,
512
0
                     CPLE_OutOfMemory,
513
0
                     "GDALNoDataMaskBand::IRasterIO(): cannot allocate %d x %d "
514
0
                     "x %d bytes%s",
515
0
                     nBufXSize, nBufYSize, nWrkDTSize,
516
0
                     bAllocHasAlreadyFailed
517
0
                         ? ""
518
0
                         : ". Falling back to block-based approach");
519
0
            if (bAllocHasAlreadyFailed)
520
0
                return std::pair(CE_Failure, nullptr);
521
            // Sets a metadata item to prevent potential infinite recursion
522
0
            GDALMajorObject::SetMetadataItem(__func__, "IN", "__INTERNAL__");
523
0
            const CPLErr eErr = GDALRasterBand::IRasterIO(
524
0
                eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
525
0
                nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg);
526
0
            GDALMajorObject::SetMetadataItem(__func__, nullptr, "__INTERNAL__");
527
0
            return std::pair(eErr, nullptr);
528
0
        }
529
0
        return std::pair(CE_None, pTemp);
530
0
    };
531
532
0
    if (eBufType == GDT_UInt8)
533
0
    {
534
0
        const int nWrkDTSize = GDALGetDataTypeSizeBytes(eWrkDT);
535
0
        auto [eErr, pTemp] = AllocTempBufferOrFallback(nWrkDTSize);
536
0
        if (!pTemp)
537
0
            return eErr;
538
539
0
        eErr = m_poParent->RasterIO(
540
0
            GF_Read, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize, nBufYSize,
541
0
            eWrkDT, nWrkDTSize, static_cast<GSpacing>(nBufXSize) * nWrkDTSize,
542
0
            psExtraArg);
543
0
        if (eErr != CE_None)
544
0
        {
545
0
            VSIFree(pTemp);
546
0
            return eErr;
547
0
        }
548
549
0
        const bool bIsNoDataNan = std::isnan(m_dfNoDataValue) != 0;
550
0
        GByte *pabyDest = static_cast<GByte *>(pData);
551
552
        /* --------------------------------------------------------------------
553
         */
554
        /*      Process different cases. */
555
        /* --------------------------------------------------------------------
556
         */
557
0
        switch (eWrkDT)
558
0
        {
559
0
            case GDT_UInt8:
560
0
            {
561
0
                const auto nNoData = static_cast<GByte>(m_dfNoDataValue);
562
0
                const auto *panSrc = static_cast<const GByte *>(pTemp);
563
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
564
0
                             nPixelSpace, nLineSpace, nNoData);
565
0
            }
566
0
            break;
567
568
0
            case GDT_Int16:
569
0
            {
570
0
                const auto nNoData = static_cast<int16_t>(m_dfNoDataValue);
571
0
                const auto *panSrc = static_cast<const int16_t *>(pTemp);
572
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
573
0
                             nPixelSpace, nLineSpace, nNoData);
574
0
            }
575
0
            break;
576
577
0
            case GDT_UInt16:
578
0
            {
579
0
                const auto nNoData = static_cast<uint16_t>(m_dfNoDataValue);
580
0
                const auto *panSrc = static_cast<const uint16_t *>(pTemp);
581
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
582
0
                             nPixelSpace, nLineSpace, nNoData);
583
0
            }
584
0
            break;
585
586
0
            case GDT_UInt32:
587
0
            {
588
0
                const auto nNoData = static_cast<GUInt32>(m_dfNoDataValue);
589
0
                const auto *panSrc = static_cast<const GUInt32 *>(pTemp);
590
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
591
0
                             nPixelSpace, nLineSpace, nNoData);
592
0
            }
593
0
            break;
594
595
0
            case GDT_Int32:
596
0
            {
597
0
                const auto nNoData = static_cast<GInt32>(m_dfNoDataValue);
598
0
                const auto *panSrc = static_cast<const GInt32 *>(pTemp);
599
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
600
0
                             nPixelSpace, nLineSpace, nNoData);
601
0
            }
602
0
            break;
603
604
0
            case GDT_Float32:
605
0
            {
606
0
                const float fNoData = static_cast<float>(m_dfNoDataValue);
607
0
                const float *pafSrc = static_cast<const float *>(pTemp);
608
609
0
                size_t i = 0;
610
0
                for (int iY = 0; iY < nBufYSize; iY++)
611
0
                {
612
0
                    GByte *pabyLineDest = pabyDest + iY * nLineSpace;
613
0
                    for (int iX = 0; iX < nBufXSize; iX++)
614
0
                    {
615
0
                        const float fVal = pafSrc[i];
616
0
                        if (bIsNoDataNan && std::isnan(fVal))
617
0
                            *pabyLineDest = 0;
618
0
                        else if (ARE_REAL_EQUAL(fVal, fNoData))
619
0
                            *pabyLineDest = 0;
620
0
                        else
621
0
                            *pabyLineDest = 255;
622
0
                        ++i;
623
0
                        pabyLineDest += nPixelSpace;
624
0
                    }
625
0
                }
626
0
            }
627
0
            break;
628
629
0
            case GDT_Float64:
630
0
            {
631
0
                const double *padfSrc = static_cast<const double *>(pTemp);
632
633
0
                size_t i = 0;
634
0
                for (int iY = 0; iY < nBufYSize; iY++)
635
0
                {
636
0
                    GByte *pabyLineDest = pabyDest + iY * nLineSpace;
637
0
                    for (int iX = 0; iX < nBufXSize; iX++)
638
0
                    {
639
0
                        const double dfVal = padfSrc[i];
640
0
                        if (bIsNoDataNan && std::isnan(dfVal))
641
0
                            *pabyLineDest = 0;
642
0
                        else if (ARE_REAL_EQUAL(dfVal, m_dfNoDataValue))
643
0
                            *pabyLineDest = 0;
644
0
                        else
645
0
                            *pabyLineDest = 255;
646
0
                        ++i;
647
0
                        pabyLineDest += nPixelSpace;
648
0
                    }
649
0
                }
650
0
            }
651
0
            break;
652
653
0
            case GDT_Int64:
654
0
            {
655
0
                const auto *panSrc = static_cast<const int64_t *>(pTemp);
656
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
657
0
                             nPixelSpace, nLineSpace, m_nNoDataValueInt64);
658
0
            }
659
0
            break;
660
661
0
            case GDT_UInt64:
662
0
            {
663
0
                const auto *panSrc = static_cast<const uint64_t *>(pTemp);
664
0
                SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
665
0
                             nPixelSpace, nLineSpace, m_nNoDataValueUInt64);
666
0
            }
667
0
            break;
668
669
0
            default:
670
0
                CPLAssert(false);
671
0
                break;
672
0
        }
673
674
0
        VSIFree(pTemp);
675
0
        return CE_None;
676
0
    }
677
678
    // Output buffer is non-Byte. Ask for Byte and expand to user requested
679
    // type
680
0
    auto [eErr, pTemp] = AllocTempBufferOrFallback(sizeof(GByte));
681
0
    if (!pTemp)
682
0
        return eErr;
683
684
0
    eErr = IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize,
685
0
                     nBufYSize, GDT_UInt8, 1, nBufXSize, psExtraArg);
686
0
    if (eErr != CE_None)
687
0
    {
688
0
        VSIFree(pTemp);
689
0
        return eErr;
690
0
    }
691
692
0
    for (int iY = 0; iY < nBufYSize; iY++)
693
0
    {
694
0
        GDALCopyWords64(
695
0
            static_cast<GByte *>(pTemp) + static_cast<size_t>(iY) * nBufXSize,
696
0
            GDT_UInt8, 1, static_cast<GByte *>(pData) + iY * nLineSpace,
697
0
            eBufType, static_cast<int>(nPixelSpace), nBufXSize);
698
0
    }
699
0
    VSIFree(pTemp);
700
0
    return CE_None;
701
0
}
702
703
/************************************************************************/
704
/*                EmitErrorMessageIfWriteNotSupported()                 */
705
/************************************************************************/
706
707
bool GDALNoDataMaskBand::EmitErrorMessageIfWriteNotSupported(
708
    const char *pszCaller) const
709
0
{
710
0
    ReportError(CE_Failure, CPLE_NoWriteAccess,
711
0
                "%s: attempt to write to a nodata implicit mask band.",
712
0
                pszCaller);
713
714
0
    return true;
715
0
}
716
717
//! @endcond