Line data Source code
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 :
30 : //! @cond Doxygen_Suppress
31 : /************************************************************************/
32 : /* GDALNoDataMaskBand() */
33 : /************************************************************************/
34 :
35 1282 : GDALNoDataMaskBand::GDALNoDataMaskBand(GDALRasterBand *poParentIn)
36 1282 : : m_poParent(poParentIn)
37 : {
38 1282 : poDS = nullptr;
39 1282 : nBand = 0;
40 :
41 1282 : nRasterXSize = m_poParent->GetXSize();
42 1282 : nRasterYSize = m_poParent->GetYSize();
43 :
44 1282 : eDataType = GDT_UInt8;
45 1282 : m_poParent->GetBlockSize(&nBlockXSize, &nBlockYSize);
46 :
47 1282 : const auto eParentDT = m_poParent->GetRasterDataType();
48 1282 : if (eParentDT == GDT_Int64)
49 15 : m_nNoDataValueInt64 = m_poParent->GetNoDataValueAsInt64();
50 1267 : else if (eParentDT == GDT_UInt64)
51 14 : m_nNoDataValueUInt64 = m_poParent->GetNoDataValueAsUInt64();
52 : else
53 1253 : m_dfNoDataValue = m_poParent->GetNoDataValue();
54 1282 : }
55 :
56 : /************************************************************************/
57 : /* GDALNoDataMaskBand() */
58 : /************************************************************************/
59 :
60 4 : GDALNoDataMaskBand::GDALNoDataMaskBand(GDALRasterBand *poParentIn,
61 4 : double dfNoDataValue)
62 4 : : m_poParent(poParentIn)
63 : {
64 4 : poDS = nullptr;
65 4 : nBand = 0;
66 :
67 4 : nRasterXSize = m_poParent->GetXSize();
68 4 : nRasterYSize = m_poParent->GetYSize();
69 :
70 4 : eDataType = GDT_UInt8;
71 4 : m_poParent->GetBlockSize(&nBlockXSize, &nBlockYSize);
72 :
73 4 : const auto eParentDT = m_poParent->GetRasterDataType();
74 4 : if (eParentDT == GDT_Int64)
75 0 : m_nNoDataValueInt64 = static_cast<int64_t>(dfNoDataValue);
76 4 : else if (eParentDT == GDT_UInt64)
77 0 : m_nNoDataValueUInt64 = static_cast<uint64_t>(dfNoDataValue);
78 : else
79 4 : m_dfNoDataValue = dfNoDataValue;
80 4 : }
81 :
82 : /************************************************************************/
83 : /* ~GDALNoDataMaskBand() */
84 : /************************************************************************/
85 :
86 : GDALNoDataMaskBand::~GDALNoDataMaskBand() = default;
87 :
88 : /************************************************************************/
89 : /* GetWorkDataType() */
90 : /************************************************************************/
91 :
92 18023 : static GDALDataType GetWorkDataType(GDALDataType eDataType)
93 : {
94 18023 : GDALDataType eWrkDT = GDT_Unknown;
95 18023 : switch (eDataType)
96 : {
97 1663 : case GDT_UInt8:
98 1663 : eWrkDT = GDT_UInt8;
99 1663 : break;
100 :
101 1850 : case GDT_Int16:
102 1850 : eWrkDT = GDT_Int16;
103 1850 : break;
104 :
105 67 : case GDT_UInt16:
106 67 : eWrkDT = GDT_UInt16;
107 67 : break;
108 :
109 33 : case GDT_UInt32:
110 33 : eWrkDT = GDT_UInt32;
111 33 : break;
112 :
113 132 : case GDT_Int8:
114 : case GDT_Int32:
115 : case GDT_CInt16:
116 : case GDT_CInt32:
117 132 : eWrkDT = GDT_Int32;
118 132 : break;
119 :
120 14117 : case GDT_Float16:
121 : case GDT_CFloat16:
122 : case GDT_Float32:
123 : case GDT_CFloat32:
124 14117 : eWrkDT = GDT_Float32;
125 14117 : break;
126 :
127 113 : case GDT_Float64:
128 : case GDT_CFloat64:
129 113 : eWrkDT = GDT_Float64;
130 113 : break;
131 :
132 48 : case GDT_Int64:
133 : case GDT_UInt64:
134 48 : eWrkDT = eDataType;
135 48 : break;
136 :
137 0 : case GDT_Unknown:
138 : case GDT_TypeCount:
139 0 : CPLAssert(false);
140 : eWrkDT = GDT_Float64;
141 : break;
142 : }
143 18023 : return eWrkDT;
144 : }
145 :
146 : /************************************************************************/
147 : /* IsNoDataInRange() */
148 : /************************************************************************/
149 :
150 1301 : bool GDALNoDataMaskBand::IsNoDataInRange(double dfNoDataValue,
151 : GDALDataType eDataTypeIn)
152 : {
153 1301 : GDALDataType eWrkDT = GetWorkDataType(eDataTypeIn);
154 1301 : switch (eWrkDT)
155 : {
156 597 : case GDT_UInt8:
157 : {
158 597 : return GDALIsValueInRange<GByte>(dfNoDataValue);
159 : }
160 :
161 0 : case GDT_Int8:
162 : {
163 0 : return GDALIsValueInRange<signed char>(dfNoDataValue);
164 : }
165 :
166 141 : case GDT_Int16:
167 : {
168 141 : return GDALIsValueInRange<GInt16>(dfNoDataValue);
169 : }
170 :
171 55 : case GDT_UInt16:
172 : {
173 55 : return GDALIsValueInRange<GUInt16>(dfNoDataValue);
174 : }
175 :
176 22 : case GDT_UInt32:
177 : {
178 22 : return GDALIsValueInRange<GUInt32>(dfNoDataValue);
179 : }
180 45 : case GDT_Int32:
181 : {
182 45 : return GDALIsValueInRange<GInt32>(dfNoDataValue);
183 : }
184 :
185 0 : case GDT_UInt64:
186 : {
187 0 : return GDALIsValueInRange<uint64_t>(dfNoDataValue);
188 : }
189 :
190 0 : case GDT_Int64:
191 : {
192 0 : return GDALIsValueInRange<int64_t>(dfNoDataValue);
193 : }
194 :
195 0 : case GDT_Float16:
196 : {
197 0 : return std::isnan(dfNoDataValue) || std::isinf(dfNoDataValue) ||
198 0 : GDALIsValueInRange<GFloat16>(dfNoDataValue);
199 : }
200 :
201 372 : case GDT_Float32:
202 : {
203 709 : return std::isnan(dfNoDataValue) || std::isinf(dfNoDataValue) ||
204 709 : GDALIsValueInRange<float>(dfNoDataValue);
205 : }
206 :
207 69 : case GDT_Float64:
208 : {
209 69 : return true;
210 : }
211 :
212 0 : case GDT_CFloat16:
213 : case GDT_CFloat32:
214 : case GDT_CFloat64:
215 : case GDT_CInt16:
216 : case GDT_CInt32:
217 : case GDT_Unknown:
218 : case GDT_TypeCount:
219 0 : break;
220 : }
221 :
222 0 : CPLAssert(false);
223 : return false;
224 : }
225 :
226 : /************************************************************************/
227 : /* IReadBlock() */
228 : /************************************************************************/
229 :
230 34 : CPLErr GDALNoDataMaskBand::IReadBlock(int nXBlockOff, int nYBlockOff,
231 : void *pImage)
232 :
233 : {
234 34 : const int nXOff = nXBlockOff * nBlockXSize;
235 34 : const int nXSizeRequest = std::min(nBlockXSize, nRasterXSize - nXOff);
236 34 : const int nYOff = nYBlockOff * nBlockYSize;
237 34 : const int nYSizeRequest = std::min(nBlockYSize, nRasterYSize - nYOff);
238 :
239 34 : if (nBlockXSize != nXSizeRequest || nBlockYSize != nYSizeRequest)
240 : {
241 0 : memset(pImage, 0, static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
242 : }
243 :
244 : GDALRasterIOExtraArg sExtraArg;
245 34 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
246 68 : return IRasterIO(GF_Read, nXOff, nYOff, nXSizeRequest, nYSizeRequest,
247 : pImage, nXSizeRequest, nYSizeRequest, GDT_UInt8, 1,
248 68 : nBlockXSize, &sExtraArg);
249 : }
250 :
251 : /************************************************************************/
252 : /* SetZeroOr255() */
253 : /************************************************************************/
254 :
255 : #if (defined(__GNUC__) && !defined(__clang__))
256 : __attribute__((optimize("tree-vectorize")))
257 : #endif
258 965 : static void SetZeroOr255(GByte *pabyDestAndSrc, size_t nBufSize, GByte byNoData)
259 : {
260 4673420 : for (size_t i = 0; i < nBufSize; ++i)
261 : {
262 4672460 : pabyDestAndSrc[i] = (pabyDestAndSrc[i] == byNoData) ? 0 : 255;
263 : }
264 965 : }
265 :
266 : template <class T>
267 : #if (defined(__GNUC__) && !defined(__clang__))
268 : __attribute__((optimize("tree-vectorize")))
269 : #endif
270 3444 : static void SetZeroOr255(GByte *pabyDest, const T *panSrc, size_t nBufSize,
271 : T nNoData)
272 : {
273 999312 : for (size_t i = 0; i < nBufSize; ++i)
274 : {
275 995868 : pabyDest[i] = (panSrc[i] == nNoData) ? 0 : 255;
276 : }
277 3444 : }
278 :
279 : template <class T>
280 1845 : static void SetZeroOr255(GByte *pabyDest, const T *panSrc, int nBufXSize,
281 : int nBufYSize, GSpacing nPixelSpace,
282 : GSpacing nLineSpace, T nNoData)
283 : {
284 1845 : if (nPixelSpace == 1)
285 : {
286 5281 : for (int iY = 0; iY < nBufYSize; iY++)
287 : {
288 3444 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nNoData);
289 3444 : pabyDest += nLineSpace;
290 3444 : panSrc += nBufXSize;
291 : }
292 : }
293 : else
294 : {
295 8 : size_t i = 0;
296 40 : for (int iY = 0; iY < nBufYSize; iY++)
297 : {
298 32 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
299 224 : for (int iX = 0; iX < nBufXSize; iX++)
300 : {
301 192 : *pabyLineDest = (panSrc[i] == nNoData) ? 0 : 255;
302 192 : ++i;
303 192 : pabyLineDest += nPixelSpace;
304 : }
305 : }
306 : }
307 1845 : }
308 :
309 : /************************************************************************/
310 : /* IRasterIO() */
311 : /************************************************************************/
312 :
313 16722 : CPLErr GDALNoDataMaskBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
314 : int nXSize, int nYSize, void *pData,
315 : int nBufXSize, int nBufYSize,
316 : GDALDataType eBufType,
317 : GSpacing nPixelSpace, GSpacing nLineSpace,
318 : GDALRasterIOExtraArg *psExtraArg)
319 : {
320 16722 : if (eRWFlag != GF_Read)
321 : {
322 0 : return CE_Failure;
323 : }
324 16722 : const auto eParentDT = m_poParent->GetRasterDataType();
325 16722 : const GDALDataType eWrkDT = GetWorkDataType(eParentDT);
326 :
327 : // Optimization in common use case (#4488).
328 : // This avoids triggering the block cache on this band, which helps
329 : // reducing the global block cache consumption.
330 16722 : if (eBufType == GDT_UInt8 && eWrkDT == GDT_UInt8 && nPixelSpace == 1 &&
331 962 : nLineSpace >= nBufXSize)
332 : {
333 962 : const CPLErr eErr = m_poParent->RasterIO(
334 : GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
335 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
336 962 : if (eErr != CE_None)
337 0 : return eErr;
338 :
339 962 : GByte *pabyData = static_cast<GByte *>(pData);
340 962 : const GByte byNoData = static_cast<GByte>(m_dfNoDataValue);
341 :
342 962 : if (nLineSpace == nBufXSize)
343 : {
344 961 : const size_t nBufSize = static_cast<size_t>(nBufXSize) * nBufYSize;
345 961 : SetZeroOr255(pabyData, nBufSize, byNoData);
346 : }
347 : else
348 : {
349 1 : assert(nLineSpace > nBufXSize);
350 5 : for (int iY = 0; iY < nBufYSize; iY++)
351 : {
352 4 : SetZeroOr255(pabyData, nBufXSize, byNoData);
353 4 : pabyData += nLineSpace;
354 : }
355 : }
356 962 : return CE_None;
357 : }
358 :
359 : const auto AllocTempBufferOrFallback =
360 15760 : [this, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
361 : nBufYSize, eBufType, nPixelSpace, nLineSpace,
362 31576 : psExtraArg](int nWrkDTSize) -> std::pair<CPLErr, void *>
363 : {
364 15760 : auto poParentDS = m_poParent->GetDataset();
365 : // Check if we must simulate a memory allocation failure
366 : // Before checking the env variable, which is slightly expensive,
367 : // check first for a special dataset name, which is a cheap test.
368 : const char *pszOptVal =
369 15760 : poParentDS && strcmp(poParentDS->GetDescription(), "__debug__") == 0
370 31520 : ? CPLGetConfigOption(
371 : "GDAL_SIMUL_MEM_ALLOC_FAILURE_NODATA_MASK_BAND", "NO")
372 15760 : : "NO";
373 : const bool bSimulMemAllocFailure =
374 31516 : EQUAL(pszOptVal, "ALWAYS") ||
375 15756 : (CPLTestBool(pszOptVal) &&
376 14 : GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") ==
377 15760 : nullptr);
378 15760 : void *pTemp = nullptr;
379 15760 : if (!bSimulMemAllocFailure)
380 : {
381 15748 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
382 15748 : pTemp = VSI_MALLOC3_VERBOSE(nWrkDTSize, nBufXSize, nBufYSize);
383 : }
384 15760 : if (!pTemp)
385 : {
386 : const bool bAllocHasAlreadyFailed =
387 12 : GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") !=
388 12 : nullptr;
389 12 : CPLError(bAllocHasAlreadyFailed ? CE_Failure : CE_Warning,
390 : CPLE_OutOfMemory,
391 : "GDALNoDataMaskBand::IRasterIO(): cannot allocate %d x %d "
392 : "x %d bytes%s",
393 : nBufXSize, nBufYSize, nWrkDTSize,
394 : bAllocHasAlreadyFailed
395 : ? ""
396 : : ". Falling back to block-based approach");
397 12 : if (bAllocHasAlreadyFailed)
398 2 : return std::pair(CE_Failure, nullptr);
399 : // Sets a metadata item to prevent potential infinite recursion
400 10 : GDALMajorObject::SetMetadataItem(__func__, "IN", "__INTERNAL__");
401 10 : const CPLErr eErr = GDALRasterBand::IRasterIO(
402 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
403 10 : nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg);
404 10 : GDALMajorObject::SetMetadataItem(__func__, nullptr, "__INTERNAL__");
405 10 : return std::pair(eErr, nullptr);
406 : }
407 15748 : return std::pair(CE_None, pTemp);
408 15760 : };
409 :
410 15760 : if (eBufType == GDT_UInt8)
411 : {
412 15635 : const int nWrkDTSize = GDALGetDataTypeSizeBytes(eWrkDT);
413 15635 : auto [eErr, pTemp] = AllocTempBufferOrFallback(nWrkDTSize);
414 15635 : if (!pTemp)
415 12 : return eErr;
416 :
417 31246 : eErr = m_poParent->RasterIO(
418 : GF_Read, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize, nBufYSize,
419 15623 : eWrkDT, nWrkDTSize, static_cast<GSpacing>(nBufXSize) * nWrkDTSize,
420 : psExtraArg);
421 15623 : if (eErr != CE_None)
422 : {
423 0 : VSIFree(pTemp);
424 0 : return eErr;
425 : }
426 :
427 15623 : const bool bIsNoDataNan = std::isnan(m_dfNoDataValue) != 0;
428 15623 : GByte *pabyDest = static_cast<GByte *>(pData);
429 :
430 : /* --------------------------------------------------------------------
431 : */
432 : /* Process different cases. */
433 : /* --------------------------------------------------------------------
434 : */
435 15623 : switch (eWrkDT)
436 : {
437 1 : case GDT_UInt8:
438 : {
439 1 : const auto nNoData = static_cast<GByte>(m_dfNoDataValue);
440 1 : const auto *panSrc = static_cast<const GByte *>(pTemp);
441 1 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
442 : nPixelSpace, nLineSpace, nNoData);
443 : }
444 1 : break;
445 :
446 1707 : case GDT_Int16:
447 : {
448 1707 : const auto nNoData = static_cast<int16_t>(m_dfNoDataValue);
449 1707 : const auto *panSrc = static_cast<const int16_t *>(pTemp);
450 1707 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
451 : nPixelSpace, nLineSpace, nNoData);
452 : }
453 1707 : break;
454 :
455 10 : case GDT_UInt16:
456 : {
457 10 : const auto nNoData = static_cast<uint16_t>(m_dfNoDataValue);
458 10 : const auto *panSrc = static_cast<const uint16_t *>(pTemp);
459 10 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
460 : nPixelSpace, nLineSpace, nNoData);
461 : }
462 10 : break;
463 :
464 9 : case GDT_UInt32:
465 : {
466 9 : const auto nNoData = static_cast<GUInt32>(m_dfNoDataValue);
467 9 : const auto *panSrc = static_cast<const GUInt32 *>(pTemp);
468 9 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
469 : nPixelSpace, nLineSpace, nNoData);
470 : }
471 9 : break;
472 :
473 84 : case GDT_Int32:
474 : {
475 84 : const auto nNoData = static_cast<GInt32>(m_dfNoDataValue);
476 84 : const auto *panSrc = static_cast<const GInt32 *>(pTemp);
477 84 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
478 : nPixelSpace, nLineSpace, nNoData);
479 : }
480 84 : break;
481 :
482 13739 : case GDT_Float32:
483 : {
484 13739 : const float fNoData = static_cast<float>(m_dfNoDataValue);
485 13739 : const float *pafSrc = static_cast<const float *>(pTemp);
486 :
487 13739 : size_t i = 0;
488 30730 : for (int iY = 0; iY < nBufYSize; iY++)
489 : {
490 16991 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
491 15400200 : for (int iX = 0; iX < nBufXSize; iX++)
492 : {
493 15383200 : const float fVal = pafSrc[i];
494 15383200 : if (bIsNoDataNan && std::isnan(fVal))
495 48 : *pabyLineDest = 0;
496 15383200 : else if (ARE_REAL_EQUAL(fVal, fNoData))
497 972982 : *pabyLineDest = 0;
498 : else
499 14410200 : *pabyLineDest = 255;
500 15383200 : ++i;
501 15383200 : pabyLineDest += nPixelSpace;
502 : }
503 : }
504 : }
505 13739 : break;
506 :
507 39 : case GDT_Float64:
508 : {
509 39 : const double *padfSrc = static_cast<const double *>(pTemp);
510 :
511 39 : size_t i = 0;
512 159 : for (int iY = 0; iY < nBufYSize; iY++)
513 : {
514 120 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
515 665 : for (int iX = 0; iX < nBufXSize; iX++)
516 : {
517 545 : const double dfVal = padfSrc[i];
518 545 : if (bIsNoDataNan && std::isnan(dfVal))
519 54 : *pabyLineDest = 0;
520 491 : else if (ARE_REAL_EQUAL(dfVal, m_dfNoDataValue))
521 241 : *pabyLineDest = 0;
522 : else
523 250 : *pabyLineDest = 255;
524 545 : ++i;
525 545 : pabyLineDest += nPixelSpace;
526 : }
527 : }
528 : }
529 39 : break;
530 :
531 17 : case GDT_Int64:
532 : {
533 17 : const auto *panSrc = static_cast<const int64_t *>(pTemp);
534 17 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
535 : nPixelSpace, nLineSpace, m_nNoDataValueInt64);
536 : }
537 17 : break;
538 :
539 17 : case GDT_UInt64:
540 : {
541 17 : const auto *panSrc = static_cast<const uint64_t *>(pTemp);
542 17 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
543 : nPixelSpace, nLineSpace, m_nNoDataValueUInt64);
544 : }
545 17 : break;
546 :
547 0 : default:
548 0 : CPLAssert(false);
549 : break;
550 : }
551 :
552 15623 : VSIFree(pTemp);
553 15623 : return CE_None;
554 : }
555 :
556 : // Output buffer is non-Byte. Ask for Byte and expand to user requested
557 : // type
558 125 : auto [eErr, pTemp] = AllocTempBufferOrFallback(sizeof(GByte));
559 125 : if (!pTemp)
560 0 : return eErr;
561 :
562 125 : eErr = IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize,
563 : nBufYSize, GDT_UInt8, 1, nBufXSize, psExtraArg);
564 125 : if (eErr != CE_None)
565 : {
566 0 : VSIFree(pTemp);
567 0 : return eErr;
568 : }
569 :
570 282 : for (int iY = 0; iY < nBufYSize; iY++)
571 : {
572 157 : GDALCopyWords64(
573 157 : static_cast<GByte *>(pTemp) + static_cast<size_t>(iY) * nBufXSize,
574 157 : GDT_UInt8, 1, static_cast<GByte *>(pData) + iY * nLineSpace,
575 : eBufType, static_cast<int>(nPixelSpace), nBufXSize);
576 : }
577 125 : VSIFree(pTemp);
578 125 : return CE_None;
579 : }
580 :
581 : /************************************************************************/
582 : /* EmitErrorMessageIfWriteNotSupported() */
583 : /************************************************************************/
584 :
585 2 : bool GDALNoDataMaskBand::EmitErrorMessageIfWriteNotSupported(
586 : const char *pszCaller) const
587 : {
588 2 : ReportError(CE_Failure, CPLE_NoWriteAccess,
589 : "%s: attempt to write to a nodata implicit mask band.",
590 : pszCaller);
591 :
592 2 : return true;
593 : }
594 :
595 : //! @endcond
|