LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_unscaled.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 202 213 94.8 %
Date: 2026-04-15 22:10:00 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_unscaled.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDALMDArray::GetUnscaled() implementation
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_multidim.h"
      15             : #include "gdalmultidim_array_unscaled.h"
      16             : 
      17             : #include <cmath>
      18             : 
      19             : //! @cond Doxygen_Suppress
      20             : 
      21             : /************************************************************************/
      22             : /*                               IRead()                                */
      23             : /************************************************************************/
      24             : 
      25          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
      26             :                                 const size_t *count, const GInt64 *arrayStep,
      27             :                                 const GPtrDiff_t *bufferStride,
      28             :                                 const GDALExtendedDataType &bufferDataType,
      29             :                                 void *pDstBuffer) const
      30             : {
      31          16 :     const double dfScale = m_dfScale;
      32          16 :     const double dfOffset = m_dfOffset;
      33          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
      34             :     const auto dtDouble =
      35          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
      36          16 :     const size_t nDTSize = dtDouble.GetSize();
      37          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
      38             : 
      39          16 :     double adfSrcNoData[2] = {0, 0};
      40          16 :     if (m_bHasNoData)
      41             :     {
      42           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
      43           9 :                                         m_poParent->GetDataType(),
      44             :                                         &adfSrcNoData[0], dtDouble);
      45             :     }
      46             : 
      47          16 :     const auto nDims = GetDimensions().size();
      48          16 :     if (nDims == 0)
      49             :     {
      50             :         double adfVal[2];
      51           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
      52             :                               dtDouble, &adfVal[0]))
      53             :         {
      54           0 :             return false;
      55             :         }
      56           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
      57             :         {
      58           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
      59           6 :             if (bDTIsComplex)
      60             :             {
      61           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
      62             :             }
      63           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
      64             :                                             bufferDataType);
      65             :         }
      66             :         else
      67             :         {
      68           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
      69             :                                             pDstBuffer, bufferDataType);
      70             :         }
      71           9 :         return true;
      72             :     }
      73             : 
      74          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
      75           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
      76           7 :     void *pTempBuffer = pDstBuffer;
      77           7 :     if (bTempBufferNeeded)
      78             :     {
      79           2 :         size_t nElts = 1;
      80           2 :         actualBufferStrideVector.resize(nDims);
      81           7 :         for (size_t i = 0; i < nDims; i++)
      82           5 :             nElts *= count[i];
      83           2 :         actualBufferStrideVector.back() = 1;
      84           5 :         for (size_t i = nDims - 1; i > 0;)
      85             :         {
      86           3 :             --i;
      87           3 :             actualBufferStrideVector[i] =
      88           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
      89             :         }
      90           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
      91           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
      92           2 :         if (!pTempBuffer)
      93           0 :             return false;
      94             :     }
      95           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
      96             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
      97             :     {
      98           0 :         if (bTempBufferNeeded)
      99           0 :             VSIFree(pTempBuffer);
     100           0 :         return false;
     101             :     }
     102             : 
     103             :     struct Stack
     104             :     {
     105             :         size_t nIters = 0;
     106             :         double *src_ptr = nullptr;
     107             :         GByte *dst_ptr = nullptr;
     108             :         GPtrDiff_t src_inc_offset = 0;
     109             :         GPtrDiff_t dst_inc_offset = 0;
     110             :     };
     111             : 
     112           7 :     std::vector<Stack> stack(nDims);
     113           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
     114          23 :     for (size_t i = 0; i < nDims; i++)
     115             :     {
     116          32 :         stack[i].src_inc_offset =
     117          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
     118          16 :         stack[i].dst_inc_offset =
     119          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
     120             :     }
     121           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
     122           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
     123             : 
     124           7 :     size_t dimIdx = 0;
     125           7 :     const size_t nDimsMinus1 = nDims - 1;
     126             :     GByte abyDstNoData[16];
     127           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
     128           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
     129             :                                     bufferDataType);
     130             : 
     131          37 : lbl_next_depth:
     132          37 :     if (dimIdx == nDimsMinus1)
     133             :     {
     134          25 :         auto nIters = count[dimIdx];
     135          25 :         double *padfVal = stack[dimIdx].src_ptr;
     136          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
     137             :         while (true)
     138             :         {
     139          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
     140             :             {
     141          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
     142          88 :                 if (bDTIsComplex)
     143             :                 {
     144           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
     145             :                 }
     146          88 :                 if (bTempBufferNeeded)
     147             :                 {
     148          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
     149             :                                                     dst_ptr, bufferDataType);
     150             :                 }
     151             :             }
     152             :             else
     153             :             {
     154           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
     155             :             }
     156             : 
     157          92 :             if ((--nIters) == 0)
     158          25 :                 break;
     159          67 :             padfVal += stack[dimIdx].src_inc_offset;
     160          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
     161             :         }
     162             :     }
     163             :     else
     164             :     {
     165          12 :         stack[dimIdx].nIters = count[dimIdx];
     166             :         while (true)
     167             :         {
     168          30 :             dimIdx++;
     169          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     170          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     171          30 :             goto lbl_next_depth;
     172          30 :         lbl_return_to_caller:
     173          30 :             dimIdx--;
     174          30 :             if ((--stack[dimIdx].nIters) == 0)
     175          12 :                 break;
     176          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     177          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     178             :         }
     179             :     }
     180          37 :     if (dimIdx > 0)
     181          30 :         goto lbl_return_to_caller;
     182             : 
     183           7 :     if (bTempBufferNeeded)
     184           2 :         VSIFree(pTempBuffer);
     185           7 :     return true;
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                               IWrite()                               */
     190             : /************************************************************************/
     191             : 
     192          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
     193             :                                  const size_t *count, const GInt64 *arrayStep,
     194             :                                  const GPtrDiff_t *bufferStride,
     195             :                                  const GDALExtendedDataType &bufferDataType,
     196             :                                  const void *pSrcBuffer)
     197             : {
     198          16 :     const double dfScale = m_dfScale;
     199          16 :     const double dfOffset = m_dfOffset;
     200          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
     201             :     const auto dtDouble =
     202          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
     203          16 :     const size_t nDTSize = dtDouble.GetSize();
     204          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
     205             :     const bool bSelfAndParentHaveNoData =
     206          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
     207          16 :     double dfNoData = 0;
     208          16 :     if (m_bHasNoData)
     209             :     {
     210           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
     211             :                         &dfNoData, GDT_Float64, 0, 1);
     212             :     }
     213             : 
     214          16 :     double adfSrcNoData[2] = {0, 0};
     215          16 :     if (bSelfAndParentHaveNoData)
     216             :     {
     217           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
     218           7 :                                         m_poParent->GetDataType(),
     219             :                                         &adfSrcNoData[0], dtDouble);
     220             :     }
     221             : 
     222          16 :     const auto nDims = GetDimensions().size();
     223          16 :     if (nDims == 0)
     224             :     {
     225             :         double adfVal[2];
     226          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
     227             :                                         dtDouble);
     228          16 :         if (bSelfAndParentHaveNoData &&
     229           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
     230             :         {
     231           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
     232           2 :                                      bufferStride, m_poParent->GetDataType(),
     233           4 :                                      m_poParent->GetRawNoDataValue());
     234             :         }
     235             :         else
     236             :         {
     237           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
     238           8 :             if (bDTIsComplex)
     239             :             {
     240           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
     241             :             }
     242           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
     243           8 :                                      bufferStride, dtDouble, &adfVal[0]);
     244             :         }
     245             :     }
     246             : 
     247          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
     248           6 :     size_t nElts = 1;
     249           6 :     tmpBufferStrideVector.resize(nDims);
     250          20 :     for (size_t i = 0; i < nDims; i++)
     251          14 :         nElts *= count[i];
     252           6 :     tmpBufferStrideVector.back() = 1;
     253          14 :     for (size_t i = nDims - 1; i > 0;)
     254             :     {
     255           8 :         --i;
     256           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
     257             :     }
     258           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
     259           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
     260           6 :     if (!pTempBuffer)
     261           0 :         return false;
     262             : 
     263             :     struct Stack
     264             :     {
     265             :         size_t nIters = 0;
     266             :         double *dst_ptr = nullptr;
     267             :         const GByte *src_ptr = nullptr;
     268             :         GPtrDiff_t src_inc_offset = 0;
     269             :         GPtrDiff_t dst_inc_offset = 0;
     270             :     };
     271             : 
     272           6 :     std::vector<Stack> stack(nDims);
     273           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
     274          20 :     for (size_t i = 0; i < nDims; i++)
     275             :     {
     276          28 :         stack[i].dst_inc_offset =
     277          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
     278          14 :         stack[i].src_inc_offset =
     279          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
     280             :     }
     281           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
     282           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
     283             : 
     284           6 :     size_t dimIdx = 0;
     285           6 :     const size_t nDimsMinus1 = nDims - 1;
     286             : 
     287          34 : lbl_next_depth:
     288          34 :     if (dimIdx == nDimsMinus1)
     289             :     {
     290          23 :         auto nIters = count[dimIdx];
     291          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
     292          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
     293             :         while (true)
     294             :         {
     295             :             double adfVal[2];
     296             :             const double *padfSrcVal;
     297          86 :             if (bIsBufferDataTypeNativeDataType)
     298             :             {
     299          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
     300             :             }
     301             :             else
     302             :             {
     303          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
     304             :                                                 &adfVal[0], dtDouble);
     305          36 :                 padfSrcVal = adfVal;
     306             :             }
     307             : 
     308         148 :             if (bSelfAndParentHaveNoData &&
     309          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
     310             :             {
     311           3 :                 dst_ptr[0] = adfSrcNoData[0];
     312           3 :                 if (bDTIsComplex)
     313             :                 {
     314           1 :                     dst_ptr[1] = adfSrcNoData[1];
     315             :                 }
     316             :             }
     317             :             else
     318             :             {
     319          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
     320          83 :                 if (bDTIsComplex)
     321             :                 {
     322           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
     323             :                 }
     324             :             }
     325             : 
     326          86 :             if ((--nIters) == 0)
     327          23 :                 break;
     328          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
     329          63 :             src_ptr += stack[dimIdx].src_inc_offset;
     330          63 :         }
     331             :     }
     332             :     else
     333             :     {
     334          11 :         stack[dimIdx].nIters = count[dimIdx];
     335             :         while (true)
     336             :         {
     337          28 :             dimIdx++;
     338          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     339          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     340          28 :             goto lbl_next_depth;
     341          28 :         lbl_return_to_caller:
     342          28 :             dimIdx--;
     343          28 :             if ((--stack[dimIdx].nIters) == 0)
     344          11 :                 break;
     345          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     346          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     347             :         }
     348             :     }
     349          34 :     if (dimIdx > 0)
     350          28 :         goto lbl_return_to_caller;
     351             : 
     352             :     // If the parent array is not double/complex-double, then convert the
     353             :     // values to it, before calling Write(), as some implementations can be
     354             :     // very slow when doing the type conversion.
     355           6 :     const auto &eParentDT = m_poParent->GetDataType();
     356           6 :     const size_t nParentDTSize = eParentDT.GetSize();
     357           6 :     if (nParentDTSize <= nDTSize / 2)
     358             :     {
     359             :         // Copy in-place by making sure that source and target do not overlap
     360           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
     361           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
     362             : 
     363             :         // Copy first element
     364             :         {
     365           6 :             std::vector<GByte> abyTemp(nParentDTSize);
     366           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
     367           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
     368             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
     369             :                             1);
     370           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
     371             :         }
     372             :         // Remaining elements
     373          86 :         for (size_t i = 1; i < nElts; ++i)
     374             :         {
     375          80 :             GDALCopyWords64(
     376          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
     377          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
     378             :                 eParentNumericDT, 0, 1);
     379             :         }
     380             :     }
     381             : 
     382             :     const bool ret =
     383           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
     384             :                           eParentDT, pTempBuffer);
     385             : 
     386           6 :     VSIFree(pTempBuffer);
     387           6 :     return ret;
     388             : }
     389             : 
     390             : //! @endcond
     391             : 
     392             : /************************************************************************/
     393             : /*                            GetUnscaled()                             */
     394             : /************************************************************************/
     395             : 
     396             : /** Return an array that is the unscaled version of the current one.
     397             :  *
     398             :  * That is each value of the unscaled array will be
     399             :  * unscaled_value = raw_value * GetScale() + GetOffset()
     400             :  *
     401             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
     402             :  * from unscaled values to raw values.
     403             :  *
     404             :  * This is the same as the C function GDALMDArrayGetUnscaled().
     405             :  *
     406             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
     407             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
     408             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
     409             :  * @return a new array, that holds a reference to the original one, and thus is
     410             :  * a view of it (not a copy), or nullptr in case of error.
     411             :  */
     412             : std::shared_ptr<GDALMDArray>
     413          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
     414             :                          double dfOverriddenDstNodata) const
     415             : {
     416          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
     417          17 :     if (!self)
     418             :     {
     419           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     420             :                  "Driver implementation issue: m_pSelf not set !");
     421           0 :         return nullptr;
     422             :     }
     423          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
     424             :     {
     425           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     426             :                  "GetUnscaled() only supports numeric data type");
     427           0 :         return nullptr;
     428             :     }
     429             :     const double dfScale =
     430          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
     431             :     const double dfOffset =
     432          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
     433          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
     434           4 :         return self;
     435             : 
     436          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
     437          13 :                            ? GDT_CFloat64
     438          13 :                            : GDT_Float64;
     439          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
     440             :     {
     441           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
     442           0 :             eDT = GDT_Float16;
     443           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
     444           1 :             eDT = GDT_Float32;
     445             :     }
     446             : 
     447          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
     448          13 :                                        dfOverriddenDstNodata, eDT);
     449             : }

Generated by: LCOV version 1.14