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 1083 : GDALNoDataMaskBand::GDALNoDataMaskBand(GDALRasterBand *poParentIn)
36 1083 : : m_poParent(poParentIn)
37 : {
38 1083 : poDS = nullptr;
39 1083 : nBand = 0;
40 :
41 1083 : nRasterXSize = m_poParent->GetXSize();
42 1083 : nRasterYSize = m_poParent->GetYSize();
43 :
44 1083 : eDataType = GDT_Byte;
45 1083 : m_poParent->GetBlockSize(&nBlockXSize, &nBlockYSize);
46 :
47 1083 : const auto eParentDT = m_poParent->GetRasterDataType();
48 1083 : if (eParentDT == GDT_Int64)
49 15 : m_nNoDataValueInt64 = m_poParent->GetNoDataValueAsInt64();
50 1068 : else if (eParentDT == GDT_UInt64)
51 14 : m_nNoDataValueUInt64 = m_poParent->GetNoDataValueAsUInt64();
52 : else
53 1054 : m_dfNoDataValue = m_poParent->GetNoDataValue();
54 1083 : }
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_Byte;
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 16623 : static GDALDataType GetWorkDataType(GDALDataType eDataType)
93 : {
94 16623 : GDALDataType eWrkDT = GDT_Unknown;
95 16623 : switch (eDataType)
96 : {
97 1461 : case GDT_Byte:
98 1461 : eWrkDT = GDT_Byte;
99 1461 : break;
100 :
101 1830 : case GDT_Int16:
102 1830 : eWrkDT = GDT_Int16;
103 1830 : break;
104 :
105 67 : case GDT_UInt16:
106 67 : eWrkDT = GDT_UInt16;
107 67 : break;
108 :
109 31 : case GDT_UInt32:
110 31 : eWrkDT = GDT_UInt32;
111 31 : break;
112 :
113 129 : case GDT_Int8:
114 : case GDT_Int32:
115 : case GDT_CInt16:
116 : case GDT_CInt32:
117 129 : eWrkDT = GDT_Int32;
118 129 : break;
119 :
120 12957 : case GDT_Float16:
121 : case GDT_CFloat16:
122 : case GDT_Float32:
123 : case GDT_CFloat32:
124 12957 : eWrkDT = GDT_Float32;
125 12957 : break;
126 :
127 100 : case GDT_Float64:
128 : case GDT_CFloat64:
129 100 : eWrkDT = GDT_Float64;
130 100 : 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 16623 : return eWrkDT;
144 : }
145 :
146 : /************************************************************************/
147 : /* IsNoDataInRange() */
148 : /************************************************************************/
149 :
150 1062 : bool GDALNoDataMaskBand::IsNoDataInRange(double dfNoDataValue,
151 : GDALDataType eDataTypeIn)
152 : {
153 1062 : GDALDataType eWrkDT = GetWorkDataType(eDataTypeIn);
154 1062 : switch (eWrkDT)
155 : {
156 445 : case GDT_Byte:
157 : {
158 445 : return GDALIsValueInRange<GByte>(dfNoDataValue);
159 : }
160 :
161 0 : case GDT_Int8:
162 : {
163 0 : return GDALIsValueInRange<signed char>(dfNoDataValue);
164 : }
165 :
166 121 : case GDT_Int16:
167 : {
168 121 : return GDALIsValueInRange<GInt16>(dfNoDataValue);
169 : }
170 :
171 55 : case GDT_UInt16:
172 : {
173 55 : return GDALIsValueInRange<GUInt16>(dfNoDataValue);
174 : }
175 :
176 20 : case GDT_UInt32:
177 : {
178 20 : return GDALIsValueInRange<GUInt32>(dfNoDataValue);
179 : }
180 42 : case GDT_Int32:
181 : {
182 42 : 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 320 : case GDT_Float32:
202 : {
203 609 : return std::isnan(dfNoDataValue) || std::isinf(dfNoDataValue) ||
204 609 : GDALIsValueInRange<float>(dfNoDataValue);
205 : }
206 :
207 59 : case GDT_Float64:
208 : {
209 59 : 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 34 : return IRasterIO(GF_Read, nXOff, nYOff, nXSizeRequest, nYSizeRequest,
247 : pImage, nXSizeRequest, nYSizeRequest, GDT_Byte, 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 : static void
259 915 : SetZeroOr255(GByte *pabyDestAndSrc, size_t nBufSize, GByte byNoData)
260 : {
261 4671760 : for (size_t i = 0; i < nBufSize; ++i)
262 : {
263 4670840 : pabyDestAndSrc[i] = (pabyDestAndSrc[i] == byNoData) ? 0 : 255;
264 : }
265 915 : }
266 :
267 : template <class T>
268 : #if (defined(__GNUC__) && !defined(__clang__))
269 : __attribute__((optimize("tree-vectorize")))
270 : #endif
271 : static void
272 3444 : SetZeroOr255(GByte *pabyDest, const T *panSrc, size_t nBufSize, T nNoData)
273 : {
274 999312 : for (size_t i = 0; i < nBufSize; ++i)
275 : {
276 995868 : pabyDest[i] = (panSrc[i] == nNoData) ? 0 : 255;
277 : }
278 3444 : }
279 :
280 : template <class T>
281 1845 : static void SetZeroOr255(GByte *pabyDest, const T *panSrc, int nBufXSize,
282 : int nBufYSize, GSpacing nPixelSpace,
283 : GSpacing nLineSpace, T nNoData)
284 : {
285 1845 : if (nPixelSpace == 1)
286 : {
287 5281 : for (int iY = 0; iY < nBufYSize; iY++)
288 : {
289 3444 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nNoData);
290 3444 : pabyDest += nLineSpace;
291 3444 : panSrc += nBufXSize;
292 : }
293 : }
294 : else
295 : {
296 8 : size_t i = 0;
297 40 : for (int iY = 0; iY < nBufYSize; iY++)
298 : {
299 32 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
300 224 : for (int iX = 0; iX < nBufXSize; iX++)
301 : {
302 192 : *pabyLineDest = (panSrc[i] == nNoData) ? 0 : 255;
303 192 : ++i;
304 192 : pabyLineDest += nPixelSpace;
305 : }
306 : }
307 : }
308 1845 : }
309 :
310 : /************************************************************************/
311 : /* IRasterIO() */
312 : /************************************************************************/
313 :
314 15561 : CPLErr GDALNoDataMaskBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
315 : int nXSize, int nYSize, void *pData,
316 : int nBufXSize, int nBufYSize,
317 : GDALDataType eBufType,
318 : GSpacing nPixelSpace, GSpacing nLineSpace,
319 : GDALRasterIOExtraArg *psExtraArg)
320 : {
321 15561 : if (eRWFlag != GF_Read)
322 : {
323 0 : return CE_Failure;
324 : }
325 15561 : const auto eParentDT = m_poParent->GetRasterDataType();
326 15561 : const GDALDataType eWrkDT = GetWorkDataType(eParentDT);
327 :
328 : // Optimization in common use case (#4488).
329 : // This avoids triggering the block cache on this band, which helps
330 : // reducing the global block cache consumption.
331 15561 : if (eBufType == GDT_Byte && eWrkDT == GDT_Byte && nPixelSpace == 1 &&
332 912 : nLineSpace >= nBufXSize)
333 : {
334 912 : const CPLErr eErr = m_poParent->RasterIO(
335 : GF_Read, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
336 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
337 912 : if (eErr != CE_None)
338 0 : return eErr;
339 :
340 912 : GByte *pabyData = static_cast<GByte *>(pData);
341 912 : const GByte byNoData = static_cast<GByte>(m_dfNoDataValue);
342 :
343 912 : if (nLineSpace == nBufXSize)
344 : {
345 911 : const size_t nBufSize = static_cast<size_t>(nBufXSize) * nBufYSize;
346 911 : SetZeroOr255(pabyData, nBufSize, byNoData);
347 : }
348 : else
349 : {
350 1 : assert(nLineSpace > nBufXSize);
351 5 : for (int iY = 0; iY < nBufYSize; iY++)
352 : {
353 4 : SetZeroOr255(pabyData, nBufXSize, byNoData);
354 4 : pabyData += nLineSpace;
355 : }
356 : }
357 912 : return CE_None;
358 : }
359 :
360 : const auto AllocTempBufferOrFallback =
361 14649 : [this, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
362 : nBufYSize, eBufType, nPixelSpace, nLineSpace,
363 29354 : psExtraArg](int nWrkDTSize) -> std::pair<CPLErr, void *>
364 : {
365 14649 : auto poParentDS = m_poParent->GetDataset();
366 : // Check if we must simulate a memory allocation failure
367 : // Before checking the env variable, which is slightly expensive,
368 : // check first for a special dataset name, which is a cheap test.
369 : const char *pszOptVal =
370 14649 : poParentDS && strcmp(poParentDS->GetDescription(), "__debug__") == 0
371 29298 : ? CPLGetConfigOption(
372 : "GDAL_SIMUL_MEM_ALLOC_FAILURE_NODATA_MASK_BAND", "NO")
373 14649 : : "NO";
374 : const bool bSimulMemAllocFailure =
375 29294 : EQUAL(pszOptVal, "ALWAYS") ||
376 14645 : (CPLTestBool(pszOptVal) &&
377 14 : GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") ==
378 14649 : nullptr);
379 14649 : void *pTemp = nullptr;
380 14649 : if (!bSimulMemAllocFailure)
381 : {
382 14637 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
383 14637 : pTemp = VSI_MALLOC3_VERBOSE(nWrkDTSize, nBufXSize, nBufYSize);
384 : }
385 14649 : if (!pTemp)
386 : {
387 : const bool bAllocHasAlreadyFailed =
388 12 : GDALMajorObject::GetMetadataItem(__func__, "__INTERNAL__") !=
389 12 : nullptr;
390 12 : CPLError(bAllocHasAlreadyFailed ? CE_Failure : CE_Warning,
391 : CPLE_OutOfMemory,
392 : "GDALNoDataMaskBand::IRasterIO(): cannot allocate %d x %d "
393 : "x %d bytes%s",
394 : nBufXSize, nBufYSize, nWrkDTSize,
395 : bAllocHasAlreadyFailed
396 : ? ""
397 : : ". Falling back to block-based approach");
398 12 : if (bAllocHasAlreadyFailed)
399 2 : return std::pair(CE_Failure, nullptr);
400 : // Sets a metadata item to prevent potential infinite recursion
401 10 : GDALMajorObject::SetMetadataItem(__func__, "IN", "__INTERNAL__");
402 10 : const CPLErr eErr = GDALRasterBand::IRasterIO(
403 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
404 10 : nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg);
405 10 : GDALMajorObject::SetMetadataItem(__func__, nullptr, "__INTERNAL__");
406 10 : return std::pair(eErr, nullptr);
407 : }
408 14637 : return std::pair(CE_None, pTemp);
409 14649 : };
410 :
411 14649 : if (eBufType == GDT_Byte)
412 : {
413 14524 : const int nWrkDTSize = GDALGetDataTypeSizeBytes(eWrkDT);
414 14524 : auto [eErr, pTemp] = AllocTempBufferOrFallback(nWrkDTSize);
415 14524 : if (!pTemp)
416 12 : return eErr;
417 :
418 29024 : eErr = m_poParent->RasterIO(
419 : GF_Read, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize, nBufYSize,
420 14512 : eWrkDT, nWrkDTSize, static_cast<GSpacing>(nBufXSize) * nWrkDTSize,
421 : psExtraArg);
422 14512 : if (eErr != CE_None)
423 : {
424 0 : VSIFree(pTemp);
425 0 : return eErr;
426 : }
427 :
428 14512 : const bool bIsNoDataNan = std::isnan(m_dfNoDataValue) != 0;
429 14512 : GByte *pabyDest = static_cast<GByte *>(pData);
430 :
431 : /* --------------------------------------------------------------------
432 : */
433 : /* Process different cases. */
434 : /* --------------------------------------------------------------------
435 : */
436 14512 : switch (eWrkDT)
437 : {
438 1 : case GDT_Byte:
439 : {
440 1 : const auto nNoData = static_cast<GByte>(m_dfNoDataValue);
441 1 : const auto *panSrc = static_cast<const GByte *>(pTemp);
442 1 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
443 : nPixelSpace, nLineSpace, nNoData);
444 : }
445 1 : break;
446 :
447 1707 : case GDT_Int16:
448 : {
449 1707 : const auto nNoData = static_cast<int16_t>(m_dfNoDataValue);
450 1707 : const auto *panSrc = static_cast<const int16_t *>(pTemp);
451 1707 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
452 : nPixelSpace, nLineSpace, nNoData);
453 : }
454 1707 : break;
455 :
456 10 : case GDT_UInt16:
457 : {
458 10 : const auto nNoData = static_cast<uint16_t>(m_dfNoDataValue);
459 10 : const auto *panSrc = static_cast<const uint16_t *>(pTemp);
460 10 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
461 : nPixelSpace, nLineSpace, nNoData);
462 : }
463 10 : break;
464 :
465 9 : case GDT_UInt32:
466 : {
467 9 : const auto nNoData = static_cast<GUInt32>(m_dfNoDataValue);
468 9 : const auto *panSrc = static_cast<const GUInt32 *>(pTemp);
469 9 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
470 : nPixelSpace, nLineSpace, nNoData);
471 : }
472 9 : break;
473 :
474 84 : case GDT_Int32:
475 : {
476 84 : const auto nNoData = static_cast<GInt32>(m_dfNoDataValue);
477 84 : const auto *panSrc = static_cast<const GInt32 *>(pTemp);
478 84 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
479 : nPixelSpace, nLineSpace, nNoData);
480 : }
481 84 : break;
482 :
483 12631 : case GDT_Float32:
484 : {
485 12631 : const float fNoData = static_cast<float>(m_dfNoDataValue);
486 12631 : const float *pafSrc = static_cast<const float *>(pTemp);
487 :
488 12631 : size_t i = 0;
489 25962 : for (int iY = 0; iY < nBufYSize; iY++)
490 : {
491 13331 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
492 15237700 : for (int iX = 0; iX < nBufXSize; iX++)
493 : {
494 15224400 : const float fVal = pafSrc[i];
495 15224400 : if (bIsNoDataNan && std::isnan(fVal))
496 48 : *pabyLineDest = 0;
497 15224300 : else if (ARE_REAL_EQUAL(fVal, fNoData))
498 971228 : *pabyLineDest = 0;
499 : else
500 14253100 : *pabyLineDest = 255;
501 15224400 : ++i;
502 15224400 : pabyLineDest += nPixelSpace;
503 : }
504 : }
505 : }
506 12631 : break;
507 :
508 36 : case GDT_Float64:
509 : {
510 36 : const double *padfSrc = static_cast<const double *>(pTemp);
511 :
512 36 : size_t i = 0;
513 132 : for (int iY = 0; iY < nBufYSize; iY++)
514 : {
515 96 : GByte *pabyLineDest = pabyDest + iY * nLineSpace;
516 429 : for (int iX = 0; iX < nBufXSize; iX++)
517 : {
518 333 : const double dfVal = padfSrc[i];
519 333 : if (bIsNoDataNan && std::isnan(dfVal))
520 54 : *pabyLineDest = 0;
521 279 : else if (ARE_REAL_EQUAL(dfVal, m_dfNoDataValue))
522 104 : *pabyLineDest = 0;
523 : else
524 175 : *pabyLineDest = 255;
525 333 : ++i;
526 333 : pabyLineDest += nPixelSpace;
527 : }
528 : }
529 : }
530 36 : break;
531 :
532 17 : case GDT_Int64:
533 : {
534 17 : const auto *panSrc = static_cast<const int64_t *>(pTemp);
535 17 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
536 : nPixelSpace, nLineSpace, m_nNoDataValueInt64);
537 : }
538 17 : break;
539 :
540 17 : case GDT_UInt64:
541 : {
542 17 : const auto *panSrc = static_cast<const uint64_t *>(pTemp);
543 17 : SetZeroOr255(pabyDest, panSrc, nBufXSize, nBufYSize,
544 : nPixelSpace, nLineSpace, m_nNoDataValueUInt64);
545 : }
546 17 : break;
547 :
548 0 : default:
549 0 : CPLAssert(false);
550 : break;
551 : }
552 :
553 14512 : VSIFree(pTemp);
554 14512 : return CE_None;
555 : }
556 :
557 : // Output buffer is non-Byte. Ask for Byte and expand to user requested
558 : // type
559 125 : auto [eErr, pTemp] = AllocTempBufferOrFallback(sizeof(GByte));
560 125 : if (!pTemp)
561 0 : return eErr;
562 :
563 250 : eErr = IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pTemp, nBufXSize,
564 125 : nBufYSize, GDT_Byte, 1, nBufXSize, psExtraArg);
565 125 : if (eErr != CE_None)
566 : {
567 0 : VSIFree(pTemp);
568 0 : return eErr;
569 : }
570 :
571 282 : for (int iY = 0; iY < nBufYSize; iY++)
572 : {
573 157 : GDALCopyWords64(
574 157 : static_cast<GByte *>(pTemp) + static_cast<size_t>(iY) * nBufXSize,
575 157 : GDT_Byte, 1, static_cast<GByte *>(pData) + iY * nLineSpace,
576 : eBufType, static_cast<int>(nPixelSpace), nBufXSize);
577 : }
578 125 : VSIFree(pTemp);
579 125 : return CE_None;
580 : }
581 :
582 : /************************************************************************/
583 : /* EmitErrorMessageIfWriteNotSupported() */
584 : /************************************************************************/
585 :
586 2 : bool GDALNoDataMaskBand::EmitErrorMessageIfWriteNotSupported(
587 : const char *pszCaller) const
588 : {
589 2 : ReportError(CE_Failure, CPLE_NoWriteAccess,
590 : "%s: attempt to write to a nodata implicit mask band.",
591 : pszCaller);
592 :
593 2 : return true;
594 : }
595 :
596 : //! @endcond
|