LCOV - code coverage report
Current view: top level - gcore - gdalcachedpixelaccessor.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 104 116 89.7 %
Date: 2025-05-31 00:00:17 Functions: 119 119 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Fast access to individual pixels in a GDALRasterBand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifndef GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      14             : #define GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      15             : 
      16             : #include "gdal_priv.h"
      17             : #include "cpl_error.h"
      18             : #include "cpl_float.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <array>
      22             : #include <vector>
      23             : 
      24             : /************************************************************************/
      25             : /*                      GDALCachedPixelAccessor                         */
      26             : /************************************************************************/
      27             : 
      28             : /** Class to have reasonably fast random pixel access to a raster band, when
      29             :  * accessing multiple pixels that are close to each other.
      30             :  *
      31             :  * This gives faster access than using GDALRasterBand::RasterIO() with
      32             :  * a 1x1 window.
      33             :  *
      34             :  * @since GDAL 3.5
      35             :  */
      36             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT = 4>
      37             : class GDALCachedPixelAccessor
      38             : {
      39             :     GDALRasterBand *m_poBand = nullptr;
      40             : 
      41             :     struct CachedTile
      42             :     {
      43             :         std::vector<Type> m_data{};
      44             :         int m_nTileX = -1;
      45             :         int m_nTileY = -1;
      46             :         bool m_bModified = false;
      47             :     };
      48             : 
      49             :     int m_nCachedTileCount = 0;
      50             :     std::array<CachedTile, CACHED_TILE_COUNT> m_aCachedTiles{};
      51             : 
      52             :     bool LoadTile(int nTileX, int nTileY);
      53             :     bool FlushTile(int iSlot);
      54             : 
      55             :     Type GetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      56             :                      bool *pbSuccess);
      57             :     bool SetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      58             :                      Type val);
      59             : 
      60             :     GDALCachedPixelAccessor(const GDALCachedPixelAccessor &) = delete;
      61             :     GDALCachedPixelAccessor &
      62             :     operator=(const GDALCachedPixelAccessor &) = delete;
      63             : 
      64             :   public:
      65             :     explicit GDALCachedPixelAccessor(GDALRasterBand *poBand);
      66             :     ~GDALCachedPixelAccessor();
      67             : 
      68             :     /** Assign the raster band if not known at construction time. */
      69          10 :     void SetBand(GDALRasterBand *poBand)
      70             :     {
      71          10 :         m_poBand = poBand;
      72          10 :     }
      73             : 
      74             :     Type Get(int nX, int nY, bool *pbSuccess = nullptr);
      75             :     bool Set(int nX, int nY, Type val);
      76             : 
      77             :     bool FlushCache();
      78             :     void ResetModifiedFlag();
      79             : };
      80             : 
      81             : /************************************************************************/
      82             : /*                      GDALCachedPixelAccessor()                       */
      83             : /************************************************************************/
      84             : 
      85             : /** Constructor.
      86             :  *
      87             :  * The template accepts the following parameters:
      88             :  * - Type: should be one of GByte, GUInt16, GInt16, GUInt32, GInt32, GUInt64,
      89             :  * GInt64, float or double
      90             :  * - TILE_SIZE: the tile size for the cache of GDALCachedPixelAccessor.
      91             :  *              Use a power of two for faster computation.
      92             :  *              It doesn't need to be the same of the underlying raster
      93             :  * - CACHED_TILE_COUNT: number of tiles to cache. Should be >= 1. Defaults to 4.
      94             :  *
      95             :  * @param poBand Raster band.
      96             :  */
      97             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
      98          21 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
      99             :     GDALCachedPixelAccessor(GDALRasterBand *poBand)
     100          21 :     : m_poBand(poBand)
     101             : {
     102          21 : }
     103             : 
     104             : /************************************************************************/
     105             : /*                     ~GDALCachedPixelAccessor()                       */
     106             : /************************************************************************/
     107             : 
     108             : /** Destructor.
     109             :  *
     110             :  * Will call FlushCache()
     111             :  */
     112             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     113          21 : GDALCachedPixelAccessor<Type, TILE_SIZE,
     114             :                         CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
     115             : {
     116          21 :     FlushCache();
     117          21 : }
     118             : 
     119             : /************************************************************************/
     120             : /*                            Get()                                     */
     121             : /************************************************************************/
     122             : 
     123             : /** Get the value of a pixel.
     124             :  *
     125             :  * No bound checking of nX, nY is done.
     126             :  *
     127             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     128             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     129             :  * @param[out] pbSuccess Optional pointer to a success flag
     130             :  * @return the pixel value
     131             :  */
     132             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     133    16171043 : inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
     134             :     int nX, int nY, bool *pbSuccess)
     135             : {
     136    16171043 :     const int nTileX = nX / TILE_SIZE;
     137    16171043 :     const int nTileY = nY / TILE_SIZE;
     138    16171043 :     const int nXInTile = nX % TILE_SIZE;
     139    16171043 :     const int nYInTile = nY % TILE_SIZE;
     140    32320967 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     141    16149924 :         m_aCachedTiles[0].m_nTileY == nTileY)
     142             :     {
     143    16135124 :         if (pbSuccess)
     144           0 :             *pbSuccess = true;
     145    16135124 :         return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     146             :     }
     147       35874 :     return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
     148             : }
     149             : 
     150             : /************************************************************************/
     151             : /*                       GetSlowPath()                                  */
     152             : /************************************************************************/
     153             : 
     154             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     155       35874 : Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
     156             :     int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
     157             : {
     158       49576 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     159             :     {
     160       49335 :         const auto &cachedTile = m_aCachedTiles[i];
     161       49335 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     162             :         {
     163       35633 :             const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
     164       35633 :             CachedTile tmp = std::move(m_aCachedTiles[i]);
     165       84194 :             for (int j = i; j >= 1; --j)
     166       48561 :                 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     167       35633 :             m_aCachedTiles[0] = std::move(tmp);
     168       35633 :             if (pbSuccess)
     169           0 :                 *pbSuccess = true;
     170       35633 :             return ret;
     171             :         }
     172             :     }
     173         241 :     if (!LoadTile(nTileX, nTileY))
     174             :     {
     175           0 :         if (pbSuccess)
     176           0 :             *pbSuccess = false;
     177           0 :         return 0;
     178             :     }
     179         241 :     if (pbSuccess)
     180           0 :         *pbSuccess = true;
     181         241 :     return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     182             : }
     183             : 
     184             : /************************************************************************/
     185             : /*                            Set()                                     */
     186             : /************************************************************************/
     187             : 
     188             : /** Set the value of a pixel.
     189             :  *
     190             :  * The actual modification of the underlying raster is deferred until the tile
     191             :  * is implicit flushed while loading a new tile, or an explicit call to
     192             :  * FlushCache().
     193             :  *
     194             :  * The destructor of GDALCachedPixelAccessor will take care of calling
     195             :  * FlushCache(), if the user hasn't done it explicitly.
     196             :  *
     197             :  * No bound checking of nX, nY is done.
     198             :  *
     199             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     200             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     201             :  * @param val pixel value
     202             :  * @return true if success
     203             :  */
     204             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     205             : inline bool
     206     1475403 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
     207             :                                                                  Type val)
     208             : {
     209     1475403 :     const int nTileX = nX / TILE_SIZE;
     210     1475403 :     const int nTileY = nY / TILE_SIZE;
     211     1475403 :     const int nXInTile = nX % TILE_SIZE;
     212     1475403 :     const int nYInTile = nY % TILE_SIZE;
     213     2948607 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     214     1473194 :         m_aCachedTiles[0].m_nTileY == nTileY)
     215             :     {
     216     1473194 :         m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     217     1473194 :         m_aCachedTiles[0].m_bModified = true;
     218     1473194 :         return true;
     219             :     }
     220        2209 :     return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
     221             : }
     222             : 
     223             : /************************************************************************/
     224             : /*                         SetSlowPath()                                */
     225             : /************************************************************************/
     226             : 
     227             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     228        2209 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
     229             :     int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
     230             : {
     231        4792 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     232             :     {
     233        4579 :         auto &cachedTile = m_aCachedTiles[i];
     234        4579 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     235             :         {
     236        1996 :             cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     237        1996 :             cachedTile.m_bModified = true;
     238        1996 :             if (i > 0)
     239             :             {
     240        3992 :                 CachedTile tmp = std::move(m_aCachedTiles[i]);
     241        6068 :                 for (int j = i; j >= 1; --j)
     242        4072 :                     m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     243        1996 :                 m_aCachedTiles[0] = std::move(tmp);
     244             :             }
     245        1996 :             return true;
     246             :         }
     247             :     }
     248         213 :     if (!LoadTile(nTileX, nTileY))
     249             :     {
     250           0 :         return false;
     251             :     }
     252         213 :     m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     253         213 :     m_aCachedTiles[0].m_bModified = true;
     254         213 :     return true;
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                            FlushCache()                              */
     259             : /************************************************************************/
     260             : 
     261             : /** Flush content of modified tiles and drop caches
     262             :  *
     263             :  * @return true if success
     264             :  */
     265             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     266          36 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
     267             : {
     268          36 :     bool bRet = true;
     269         202 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     270             :     {
     271         166 :         if (!FlushTile(i))
     272           0 :             bRet = false;
     273         166 :         m_aCachedTiles[i].m_nTileX = -1;
     274         166 :         m_aCachedTiles[i].m_nTileY = -1;
     275             :     }
     276          36 :     return bRet;
     277             : }
     278             : 
     279             : /************************************************************************/
     280             : /*                      ResetModifiedFlag()                             */
     281             : /************************************************************************/
     282             : 
     283             : /** Reset the modified flag for cached tiles.
     284             :  */
     285             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     286          10 : void GDALCachedPixelAccessor<Type, TILE_SIZE,
     287             :                              CACHED_TILE_COUNT>::ResetModifiedFlag()
     288             : {
     289          68 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     290             :     {
     291          58 :         m_aCachedTiles[i].m_bModified = false;
     292             :     }
     293          10 : }
     294             : 
     295             : /************************************************************************/
     296             : /*                 GDALCachedPixelAccessorGetDataType                   */
     297             : /************************************************************************/
     298             : 
     299             : /*! @cond Doxygen_Suppress */
     300             : template <class T> struct GDALCachedPixelAccessorGetDataType
     301             : {
     302             : };
     303             : 
     304             : template <> struct GDALCachedPixelAccessorGetDataType<GByte>
     305             : {
     306             :     static constexpr GDALDataType DataType = GDT_Byte;
     307             : };
     308             : 
     309             : template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
     310             : {
     311             :     static constexpr GDALDataType DataType = GDT_Int8;
     312             : };
     313             : 
     314             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
     315             : {
     316             :     static constexpr GDALDataType DataType = GDT_UInt16;
     317             : };
     318             : 
     319             : template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
     320             : {
     321             :     static constexpr GDALDataType DataType = GDT_Int16;
     322             : };
     323             : 
     324             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
     325             : {
     326             :     static constexpr GDALDataType DataType = GDT_UInt32;
     327             : };
     328             : 
     329             : template <> struct GDALCachedPixelAccessorGetDataType<GInt32>
     330             : {
     331             :     static constexpr GDALDataType DataType = GDT_Int32;
     332             : };
     333             : #if SIZEOF_UNSIGNED_LONG == 8
     334             : // std::uint64_t on Linux 64-bit resolves as unsigned long
     335             : template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
     336             : {
     337             :     static constexpr GDALDataType DataType = GDT_UInt64;
     338             : };
     339             : 
     340             : template <> struct GDALCachedPixelAccessorGetDataType<long>
     341             : {
     342             :     static constexpr GDALDataType DataType = GDT_Int64;
     343             : };
     344             : #endif
     345             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
     346             : {
     347             :     static constexpr GDALDataType DataType = GDT_UInt64;
     348             : };
     349             : 
     350             : template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
     351             : {
     352             :     static constexpr GDALDataType DataType = GDT_Int64;
     353             : };
     354             : 
     355             : template <> struct GDALCachedPixelAccessorGetDataType<GFloat16>
     356             : {
     357             :     static constexpr GDALDataType DataType = GDT_Float16;
     358             : };
     359             : 
     360             : template <> struct GDALCachedPixelAccessorGetDataType<float>
     361             : {
     362             :     static constexpr GDALDataType DataType = GDT_Float32;
     363             : };
     364             : 
     365             : template <> struct GDALCachedPixelAccessorGetDataType<double>
     366             : {
     367             :     static constexpr GDALDataType DataType = GDT_Float64;
     368             : };
     369             : 
     370             : /*! @endcond */
     371             : 
     372             : /************************************************************************/
     373             : /*                          LoadTile()                                  */
     374             : /************************************************************************/
     375             : 
     376             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     377         454 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
     378             :     int nTileX, int nTileY)
     379             : {
     380         454 :     if (m_nCachedTileCount == CACHED_TILE_COUNT)
     381             :     {
     382         352 :         if (!FlushTile(CACHED_TILE_COUNT - 1))
     383           0 :             return false;
     384         704 :         CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
     385        1408 :         for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
     386        1056 :             m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
     387         352 :         m_aCachedTiles[0] = std::move(tmp);
     388             :     }
     389             :     else
     390             :     {
     391         102 :         if (m_nCachedTileCount > 0)
     392          81 :             std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
     393         102 :         m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
     394         102 :         m_nCachedTileCount++;
     395             :     }
     396             : 
     397             : #if 0
     398             :     CPLDebug("GDAL", "Load tile(%d, %d) of band %d of dataset %s",
     399             :               nTileX, nTileY, m_poBand->GetBand(),
     400             :               m_poBand->GetDataset() ? m_poBand->GetDataset()->GetDescription() : "(unknown)");
     401             : #endif
     402         454 :     CPLAssert(!m_aCachedTiles[0].m_bModified);
     403         454 :     const int nXOff = nTileX * TILE_SIZE;
     404         454 :     const int nYOff = nTileY * TILE_SIZE;
     405         454 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     406         454 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     407         908 :     if (m_poBand->RasterIO(
     408             :             GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
     409         454 :             m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
     410             :             GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     411         454 :             TILE_SIZE * sizeof(Type), nullptr) != CE_None)
     412             :     {
     413           0 :         m_aCachedTiles[0].m_nTileX = -1;
     414           0 :         m_aCachedTiles[0].m_nTileY = -1;
     415           0 :         return false;
     416             :     }
     417         454 :     m_aCachedTiles[0].m_nTileX = nTileX;
     418         454 :     m_aCachedTiles[0].m_nTileY = nTileY;
     419         454 :     return true;
     420             : }
     421             : 
     422             : /************************************************************************/
     423             : /*                          FlushTile()                                 */
     424             : /************************************************************************/
     425             : 
     426             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     427         518 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
     428             :     int iSlot)
     429             : {
     430         518 :     if (!m_aCachedTiles[iSlot].m_bModified)
     431         300 :         return true;
     432             : 
     433         218 :     m_aCachedTiles[iSlot].m_bModified = false;
     434         218 :     const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
     435         218 :     const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
     436         218 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     437         218 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     438         218 :     return m_poBand->RasterIO(
     439             :                GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
     440         218 :                m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
     441             :                GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     442         218 :                TILE_SIZE * sizeof(Type), nullptr) == CE_None;
     443             : }
     444             : 
     445             : #endif  // GDAL_PIXEL_ACCESSOR_INCLUDED

Generated by: LCOV version 1.14