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  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "cpl_port.h"
32 #include "gdal_priv.h"
33 
34 #include <algorithm>
35 #include <cstring>
36 
37 #include "cpl_conv.h"
38 #include "cpl_error.h"
39 #include "cpl_vsi.h"
40 #include "gdal.h"
41 #include "gdal_priv_templates.hpp"
42 
43 CPL_CVSID("$Id: gdalnodatamaskband.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
44 
45 //! @cond Doxygen_Suppress
46 /************************************************************************/
47 /*                        GDALNoDataMaskBand()                          */
48 /************************************************************************/
49 
50 GDALNoDataMaskBand::GDALNoDataMaskBand( GDALRasterBand *poParentIn ) :
51     dfNoDataValue(poParentIn->GetNoDataValue()),
52     poParent(poParentIn)
53 {
54     poDS = nullptr;
55     nBand = 0;
56 
57     nRasterXSize = poParent->GetXSize();
58     nRasterYSize = poParent->GetYSize();
59 
60     eDataType = GDT_Byte;
61     poParent->GetBlockSize( &nBlockXSize, &nBlockYSize );
62 }
63 
64 /************************************************************************/
65 /*                       ~GDALNoDataMaskBand()                          */
66 /************************************************************************/
67 
68 GDALNoDataMaskBand::~GDALNoDataMaskBand() = default;
69 
70 /************************************************************************/
71 /*                          GetWorkDataType()                           */
72 /************************************************************************/
73 
74 static GDALDataType GetWorkDataType(GDALDataType eDataType)
75 {
76     GDALDataType eWrkDT = GDT_Unknown;
77     switch( eDataType )
78     {
79       case GDT_Byte:
80         eWrkDT = GDT_Byte;
81         break;
82 
83       case GDT_UInt16:
84       case GDT_UInt32:
85         eWrkDT = GDT_UInt32;
86         break;
87 
88       case GDT_Int16:
89       case GDT_Int32:
90       case GDT_CInt16:
91       case GDT_CInt32:
92         eWrkDT = GDT_Int32;
93         break;
94 
95       case GDT_Float32:
96       case GDT_CFloat32:
97         eWrkDT = GDT_Float32;
98         break;
99 
100       case GDT_Float64:
101       case GDT_CFloat64:
102         eWrkDT = GDT_Float64;
103         break;
104 
105       default:
106         CPLAssert( false );
107         eWrkDT = GDT_Float64;
108         break;
109     }
110     return eWrkDT;
111 }
112 
113 /************************************************************************/
114 /*                          IsNoDataInRange()                           */
115 /************************************************************************/
116 
117 bool GDALNoDataMaskBand::IsNoDataInRange(double dfNoDataValue,
118                                          GDALDataType eDataType)
119 {
120     GDALDataType eWrkDT = GetWorkDataType( eDataType );
121     switch( eWrkDT )
122     {
123       case GDT_Byte:
124       {
125           return GDALIsValueInRange<GByte>(dfNoDataValue);
126       }
127 
128       case GDT_UInt32:
129       {
130           return GDALIsValueInRange<GUInt32>(dfNoDataValue);
131       }
132 
133       case GDT_Int32:
134       {
135           return GDALIsValueInRange<GInt32>(dfNoDataValue);
136       }
137 
138       case GDT_Float32:
139       {
140           return CPLIsNan(dfNoDataValue) ||
141                  CPLIsInf(dfNoDataValue) ||
142                  GDALIsValueInRange<float>(dfNoDataValue);
143       }
144 
145       case GDT_Float64:
146       {
147           return true;
148       }
149 
150       default:
151         CPLAssert( false );
152         return false;
153     }
154 }
155 
156 /************************************************************************/
157 /*                             IReadBlock()                             */
158 /************************************************************************/
159 
160 CPLErr GDALNoDataMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff,
161                                        void * pImage )
162 
163 {
164     const int nXOff = nXBlockOff * nBlockXSize;
165     const int nXSizeRequest = std::min(nBlockXSize, nRasterXSize - nXOff);
166     const int nYOff = nYBlockOff * nBlockYSize;
167     const int nYSizeRequest = std::min(nBlockYSize, nRasterYSize - nYOff);
168 
169     if( nBlockXSize != nXSizeRequest || nBlockYSize != nYSizeRequest )
170     {
171         memset(pImage, 0, static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
172     }
173 
174     GDALRasterIOExtraArg sExtraArg;
175     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
176     return IRasterIO(GF_Read, nXOff, nYOff, nXSizeRequest, nYSizeRequest,
177                      pImage, nXSizeRequest, nYSizeRequest,
178                      GDT_Byte, 1, nBlockXSize, &sExtraArg);
179 }
180 
181 /************************************************************************/
182 /*                             IRasterIO()                              */
183 /************************************************************************/
184 
185 CPLErr GDALNoDataMaskBand::IRasterIO( GDALRWFlag eRWFlag,
186                                       int nXOff, int nYOff,
187                                       int nXSize, int nYSize,
188                                       void * pData,
189                                       int nBufXSize, int nBufYSize,
190                                       GDALDataType eBufType,
191                                       GSpacing nPixelSpace, GSpacing nLineSpace,
192                                       GDALRasterIOExtraArg* psExtraArg )
193 {
194     if( eRWFlag != GF_Read )
195     {
196         return CE_Failure;
197     }
198 
199     const GDALDataType eWrkDT = GetWorkDataType( poParent->GetRasterDataType() );
200 
201     // Optimization in common use case (#4488).
202     // This avoids triggering the block cache on this band, which helps
203     // reducing the global block cache consumption.
204     if (eBufType == GDT_Byte && eWrkDT == GDT_Byte)
205     {
206         const CPLErr eErr =
207             poParent->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize,
208                                 pData, nBufXSize, nBufYSize,
209                                 eBufType,
210                                 nPixelSpace, nLineSpace, psExtraArg );
211         if (eErr != CE_None)
212             return eErr;
213 
214         GByte* pabyData = static_cast<GByte*>( pData );
215         const GByte byNoData = static_cast<GByte>( dfNoDataValue );
216 
217         if( nPixelSpace == 1 && nLineSpace == nBufXSize )
218         {
219             const size_t nBufSize = static_cast<size_t>(nBufXSize) * nBufYSize;
220             for( size_t i = 0; i < nBufSize; ++i )
221             {
222                 pabyData[i] = pabyData[i] == byNoData ? 0 : 255;
223             }
224         }
225         else
226         {
227             for( int iY = 0; iY < nBufYSize; iY++ )
228             {
229                 GByte* pabyLine = pabyData + iY * nLineSpace;
230                 for( int iX = 0; iX < nBufXSize; iX++ )
231                 {
232                     *pabyLine = *pabyLine == byNoData ? 0 : 255;
233                     pabyLine += nPixelSpace;
234                 }
235             }
236         }
237         return CE_None;
238     }
239 
240     if( eBufType == GDT_Byte )
241     {
242         const int nWrkDTSize = GDALGetDataTypeSizeBytes(eWrkDT);
243         void *pTemp =
244             VSI_MALLOC3_VERBOSE( nWrkDTSize, nBufXSize, nBufYSize );
245         if (pTemp == nullptr)
246         {
247             return GDALRasterBand::IRasterIO(
248                                       eRWFlag, nXOff, nYOff, nXSize, nYSize,
249                                       pTemp, nBufXSize, nBufYSize,
250                                       eWrkDT,
251                                       nWrkDTSize, nBufXSize * nWrkDTSize,
252                                       psExtraArg );
253         }
254 
255         const CPLErr eErr =
256             poParent->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize,
257                                 pTemp, nBufXSize, nBufYSize,
258                                 eWrkDT,
259                                 nWrkDTSize, nBufXSize * nWrkDTSize, psExtraArg );
260         if (eErr != CE_None)
261         {
262             VSIFree(pTemp);
263             return eErr;
264         }
265 
266         const bool bIsNoDataNan = CPLIsNan(dfNoDataValue) != 0;
267         GByte* pabyDest = static_cast<GByte*>(pData);
268 
269 /* -------------------------------------------------------------------- */
270 /*      Process different cases.                                        */
271 /* -------------------------------------------------------------------- */
272         switch( eWrkDT )
273         {
274             case GDT_UInt32:
275             {
276                 const GUInt32 nNoData = static_cast<GUInt32>( dfNoDataValue );
277                 const GUInt32* panSrc = static_cast<const GUInt32 *>(pTemp);
278 
279                 size_t i = 0;
280                 for( int iY = 0; iY < nBufYSize; iY++ )
281                 {
282                     GByte* pabyLineDest = pabyDest + iY * nLineSpace;
283                     for( int iX = 0; iX < nBufXSize; iX++ )
284                     {
285                         *pabyLineDest = panSrc[i] == nNoData ? 0 : 255;
286                         ++i;
287                         pabyLineDest += nPixelSpace;
288                     }
289                 }
290             }
291             break;
292 
293             case GDT_Int32:
294             {
295                 const GInt32 nNoData = static_cast<GInt32>( dfNoDataValue );
296                 const GInt32* panSrc = static_cast<const GInt32 *>(pTemp);
297 
298                 size_t i = 0;
299                 for( int iY = 0; iY < nBufYSize; iY++ )
300                 {
301                     GByte* pabyLineDest = pabyDest + iY * nLineSpace;
302                     for( int iX = 0; iX < nBufXSize; iX++ )
303                     {
304                         *pabyLineDest = panSrc[i] == nNoData ? 0 : 255;
305                         ++i;
306                         pabyLineDest += nPixelSpace;
307                     }
308                 }
309             }
310             break;
311 
312             case GDT_Float32:
313             {
314                 const float fNoData = static_cast<float>( dfNoDataValue );
315                 const float* pafSrc = static_cast<const float *>(pTemp);
316 
317                 size_t i = 0;
318                 for( int iY = 0; iY < nBufYSize; iY++ )
319                 {
320                     GByte* pabyLineDest = pabyDest + iY * nLineSpace;
321                     for( int iX = 0; iX < nBufXSize; iX++ )
322                     {
323                         const float fVal = pafSrc[i];
324                         if( bIsNoDataNan && CPLIsNan(fVal))
325                             *pabyLineDest = 0;
326                         else if( ARE_REAL_EQUAL(fVal, fNoData) )
327                             *pabyLineDest = 0;
328                         else
329                             *pabyLineDest = 255;
330                         ++i;
331                         pabyLineDest += nPixelSpace;
332                     }
333                 }
334             }
335             break;
336 
337             case GDT_Float64:
338             {
339                 const double* padfSrc = static_cast<const double *>(pTemp);
340 
341                 size_t i = 0;
342                 for( int iY = 0; iY < nBufYSize; iY++ )
343                 {
344                     GByte* pabyLineDest = pabyDest + iY * nLineSpace;
345                     for( int iX = 0; iX < nBufXSize; iX++ )
346                     {
347                         const double dfVal = padfSrc[i];
348                         if( bIsNoDataNan && CPLIsNan(dfVal))
349                             *pabyLineDest = 0;
350                         else if( ARE_REAL_EQUAL(dfVal, dfNoDataValue) )
351                             *pabyLineDest = 0;
352                         else
353                             *pabyLineDest = 255;
354                         ++i;
355                         pabyLineDest += nPixelSpace;
356                     }
357                 }
358             }
359             break;
360 
361             default:
362                 CPLAssert( false );
363                 break;
364         }
365 
366         VSIFree(pTemp);
367         return CE_None;
368     }
369 
370     // Output buffer is non-Byte. Ask for Byte and expand to user requested
371     // type
372     GByte* pabyBuf = static_cast<GByte*>(
373         VSI_MALLOC2_VERBOSE( nBufXSize, nBufYSize ));
374     if( pabyBuf == nullptr )
375     {
376         return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
377                                         pData, nBufXSize, nBufYSize,
378                                         eBufType,
379                                         nPixelSpace, nLineSpace, psExtraArg );
380     }
381     const CPLErr eErr = IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
382                                    pabyBuf, nBufXSize, nBufYSize,
383                                    GDT_Byte,
384                                    1, nBufXSize, psExtraArg );
385     if( eErr != CE_None )
386     {
387         VSIFree(pabyBuf);
388         return eErr;
389     }
390 
391     for( int iY = 0; iY < nBufYSize; iY++ )
392     {
393         GDALCopyWords( pabyBuf + static_cast<size_t>(iY) * nBufXSize,
394                        GDT_Byte, 1,
395                        static_cast<GByte*>(pData) +
396                             iY * nLineSpace, eBufType,
397                        static_cast<int>(nPixelSpace),
398                        nBufXSize );
399     }
400     VSIFree(pabyBuf);
401     return CE_None;
402 }
403 //! @endcond
404