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: 2024-05-03 15:49:35 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #ifndef GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      30             : #define GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      31             : 
      32             : #include "gdal_priv.h"
      33             : #include "cpl_error.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <array>
      37             : #include <vector>
      38             : 
      39             : /************************************************************************/
      40             : /*                      GDALCachedPixelAccessor                         */
      41             : /************************************************************************/
      42             : 
      43             : /** Class to have reasonably fast random pixel access to a raster band, when
      44             :  * accessing multiple pixels that are close to each other.
      45             :  *
      46             :  * This gives faster access than using GDALRasterBand::RasterIO() with
      47             :  * a 1x1 window.
      48             :  *
      49             :  * @since GDAL 3.5
      50             :  */
      51             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT = 4>
      52             : class GDALCachedPixelAccessor
      53             : {
      54             :     GDALRasterBand *m_poBand = nullptr;
      55             : 
      56             :     struct CachedTile
      57             :     {
      58             :         std::vector<Type> m_data{};
      59             :         int m_nTileX = -1;
      60             :         int m_nTileY = -1;
      61             :         bool m_bModified = false;
      62             :     };
      63             : 
      64             :     int m_nCachedTileCount = 0;
      65             :     std::array<CachedTile, CACHED_TILE_COUNT> m_aCachedTiles{};
      66             : 
      67             :     bool LoadTile(int nTileX, int nTileY);
      68             :     bool FlushTile(int iSlot);
      69             : 
      70             :     Type GetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      71             :                      bool *pbSuccess);
      72             :     bool SetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      73             :                      Type val);
      74             : 
      75             :     GDALCachedPixelAccessor(const GDALCachedPixelAccessor &) = delete;
      76             :     GDALCachedPixelAccessor &
      77             :     operator=(const GDALCachedPixelAccessor &) = delete;
      78             : 
      79             :   public:
      80             :     explicit GDALCachedPixelAccessor(GDALRasterBand *poBand);
      81             :     ~GDALCachedPixelAccessor();
      82             : 
      83             :     /** Assign the raster band if not known at construction time. */
      84          10 :     void SetBand(GDALRasterBand *poBand)
      85             :     {
      86          10 :         m_poBand = poBand;
      87          10 :     }
      88             : 
      89             :     Type Get(int nX, int nY, bool *pbSuccess = nullptr);
      90             :     bool Set(int nX, int nY, Type val);
      91             : 
      92             :     bool FlushCache();
      93             :     void ResetModifiedFlag();
      94             : };
      95             : 
      96             : /************************************************************************/
      97             : /*                      GDALCachedPixelAccessor()                       */
      98             : /************************************************************************/
      99             : 
     100             : /** Constructor.
     101             :  *
     102             :  * The template accepts the following parameters:
     103             :  * - Type: should be one of GByte, GUInt16, GInt16, GUInt32, GInt32, GUInt64,
     104             :  * GInt64, float or double
     105             :  * - TILE_SIZE: the tile size for the cache of GDALCachedPixelAccessor.
     106             :  *              Use a power of two for faster computation.
     107             :  *              It doesn't need to be the same of the underlying raster
     108             :  * - CACHED_TILE_COUNT: number of tiles to cache. Should be >= 1. Defaults to 4.
     109             :  *
     110             :  * @param poBand Raster band.
     111             :  */
     112             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     113          21 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
     114             :     GDALCachedPixelAccessor(GDALRasterBand *poBand)
     115          21 :     : m_poBand(poBand)
     116             : {
     117          21 : }
     118             : 
     119             : /************************************************************************/
     120             : /*                     ~GDALCachedPixelAccessor()                       */
     121             : /************************************************************************/
     122             : 
     123             : /** Destructor.
     124             :  *
     125             :  * Will call FlushCache()
     126             :  */
     127             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     128          21 : GDALCachedPixelAccessor<Type, TILE_SIZE,
     129             :                         CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
     130             : {
     131          21 :     FlushCache();
     132          21 : }
     133             : 
     134             : /************************************************************************/
     135             : /*                            Get()                                     */
     136             : /************************************************************************/
     137             : 
     138             : /** Get the value of a pixel.
     139             :  *
     140             :  * No bound checking of nX, nY is done.
     141             :  *
     142             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     143             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     144             :  * @param[out] pbSuccess Optional pointer to a success flag
     145             :  * @return the pixel value
     146             :  */
     147             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     148    16173083 : inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
     149             :     int nX, int nY, bool *pbSuccess)
     150             : {
     151    16173083 :     const int nTileX = nX / TILE_SIZE;
     152    16173083 :     const int nTileY = nY / TILE_SIZE;
     153    16173083 :     const int nXInTile = nX % TILE_SIZE;
     154    16173083 :     const int nYInTile = nY % TILE_SIZE;
     155    32343447 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     156    16170374 :         m_aCachedTiles[0].m_nTileY == nTileY)
     157             :     {
     158    16170374 :         if (pbSuccess)
     159           0 :             *pbSuccess = true;
     160    16170374 :         return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     161             :     }
     162        2713 :     return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                       GetSlowPath()                                  */
     167             : /************************************************************************/
     168             : 
     169             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     170        2713 : Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
     171             :     int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
     172             : {
     173        3878 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     174             :     {
     175        3667 :         const auto &cachedTile = m_aCachedTiles[i];
     176        3667 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     177             :         {
     178        2502 :             const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
     179        2502 :             CachedTile tmp = std::move(m_aCachedTiles[i]);
     180        5569 :             for (int j = i; j >= 1; --j)
     181        3067 :                 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     182        2502 :             m_aCachedTiles[0] = std::move(tmp);
     183        2502 :             if (pbSuccess)
     184           0 :                 *pbSuccess = true;
     185        2502 :             return ret;
     186             :         }
     187             :     }
     188         211 :     if (!LoadTile(nTileX, nTileY))
     189             :     {
     190           0 :         if (pbSuccess)
     191           0 :             *pbSuccess = false;
     192           0 :         return 0;
     193             :     }
     194         211 :     if (pbSuccess)
     195           0 :         *pbSuccess = true;
     196         211 :     return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                            Set()                                     */
     201             : /************************************************************************/
     202             : 
     203             : /** Set the value of a pixel.
     204             :  *
     205             :  * The actual modification of the underlying raster is deferred until the tile
     206             :  * is implicit flushed while loading a new tile, or an explicit call to
     207             :  * FlushCache().
     208             :  *
     209             :  * The destructor of GDALCachedPixelAccessor will take care of calling
     210             :  * FlushCache(), if the user hasn't done it explicitly.
     211             :  *
     212             :  * No bound checking of nX, nY is done.
     213             :  *
     214             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     215             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     216             :  * @param val pixel value
     217             :  * @return true if success
     218             :  */
     219             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     220             : inline bool
     221     1473423 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
     222             :                                                                  Type val)
     223             : {
     224     1473423 :     const int nTileX = nX / TILE_SIZE;
     225     1473423 :     const int nTileY = nY / TILE_SIZE;
     226     1473423 :     const int nXInTile = nX % TILE_SIZE;
     227     1473423 :     const int nYInTile = nY % TILE_SIZE;
     228     2945877 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     229     1472454 :         m_aCachedTiles[0].m_nTileY == nTileY)
     230             :     {
     231     1472454 :         m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     232     1472454 :         m_aCachedTiles[0].m_bModified = true;
     233     1472454 :         return true;
     234             :     }
     235         962 :     return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                         SetSlowPath()                                */
     240             : /************************************************************************/
     241             : 
     242             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     243         962 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
     244             :     int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
     245             : {
     246        2007 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     247             :     {
     248        1803 :         auto &cachedTile = m_aCachedTiles[i];
     249        1803 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     250             :         {
     251         758 :             cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     252         758 :             cachedTile.m_bModified = true;
     253         758 :             if (i > 0)
     254             :             {
     255        1516 :                 CachedTile tmp = std::move(m_aCachedTiles[i]);
     256        2066 :                 for (int j = i; j >= 1; --j)
     257        1308 :                     m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     258         758 :                 m_aCachedTiles[0] = std::move(tmp);
     259             :             }
     260         758 :             return true;
     261             :         }
     262             :     }
     263         204 :     if (!LoadTile(nTileX, nTileY))
     264             :     {
     265           0 :         return false;
     266             :     }
     267         204 :     m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     268         204 :     m_aCachedTiles[0].m_bModified = true;
     269         204 :     return true;
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                            FlushCache()                              */
     274             : /************************************************************************/
     275             : 
     276             : /** Flush content of modified tiles and drop caches
     277             :  *
     278             :  * @return true if success
     279             :  */
     280             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     281          36 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
     282             : {
     283          36 :     bool bRet = true;
     284         149 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     285             :     {
     286         113 :         if (!FlushTile(i))
     287           0 :             bRet = false;
     288         113 :         m_aCachedTiles[i].m_nTileX = -1;
     289         113 :         m_aCachedTiles[i].m_nTileY = -1;
     290             :     }
     291          36 :     return bRet;
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                      ResetModifiedFlag()                             */
     296             : /************************************************************************/
     297             : 
     298             : /** Reset the modified flag for cached tiles.
     299             :  */
     300             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     301          10 : void GDALCachedPixelAccessor<Type, TILE_SIZE,
     302             :                              CACHED_TILE_COUNT>::ResetModifiedFlag()
     303             : {
     304          29 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     305             :     {
     306          19 :         m_aCachedTiles[i].m_bModified = false;
     307             :     }
     308          10 : }
     309             : 
     310             : /************************************************************************/
     311             : /*                 GDALCachedPixelAccessorGetDataType                   */
     312             : /************************************************************************/
     313             : 
     314             : /*! @cond Doxygen_Suppress */
     315             : template <class T> struct GDALCachedPixelAccessorGetDataType
     316             : {
     317             : };
     318             : 
     319             : template <> struct GDALCachedPixelAccessorGetDataType<GByte>
     320             : {
     321             :     static constexpr GDALDataType DataType = GDT_Byte;
     322             : };
     323             : 
     324             : template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
     325             : {
     326             :     static constexpr GDALDataType DataType = GDT_Int8;
     327             : };
     328             : 
     329             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
     330             : {
     331             :     static constexpr GDALDataType DataType = GDT_UInt16;
     332             : };
     333             : 
     334             : template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
     335             : {
     336             :     static constexpr GDALDataType DataType = GDT_Int16;
     337             : };
     338             : 
     339             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
     340             : {
     341             :     static constexpr GDALDataType DataType = GDT_UInt32;
     342             : };
     343             : 
     344             : template <> struct GDALCachedPixelAccessorGetDataType<GInt32>
     345             : {
     346             :     static constexpr GDALDataType DataType = GDT_Int32;
     347             : };
     348             : #if SIZEOF_UNSIGNED_LONG == 8
     349             : // std::uint64_t on Linux 64-bit resolves as unsigned long
     350             : template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
     351             : {
     352             :     static constexpr GDALDataType DataType = GDT_UInt64;
     353             : };
     354             : 
     355             : template <> struct GDALCachedPixelAccessorGetDataType<long>
     356             : {
     357             :     static constexpr GDALDataType DataType = GDT_Int64;
     358             : };
     359             : #endif
     360             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
     361             : {
     362             :     static constexpr GDALDataType DataType = GDT_UInt64;
     363             : };
     364             : 
     365             : template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
     366             : {
     367             :     static constexpr GDALDataType DataType = GDT_Int64;
     368             : };
     369             : 
     370             : template <> struct GDALCachedPixelAccessorGetDataType<float>
     371             : {
     372             :     static constexpr GDALDataType DataType = GDT_Float32;
     373             : };
     374             : 
     375             : template <> struct GDALCachedPixelAccessorGetDataType<double>
     376             : {
     377             :     static constexpr GDALDataType DataType = GDT_Float64;
     378             : };
     379             : 
     380             : /*! @endcond */
     381             : 
     382             : /************************************************************************/
     383             : /*                          LoadTile()                                  */
     384             : /************************************************************************/
     385             : 
     386             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     387         415 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
     388             :     int nTileX, int nTileY)
     389             : {
     390         415 :     if (m_nCachedTileCount == CACHED_TILE_COUNT)
     391             :     {
     392         352 :         if (!FlushTile(CACHED_TILE_COUNT - 1))
     393           0 :             return false;
     394         704 :         CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
     395        1408 :         for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
     396        1056 :             m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
     397         352 :         m_aCachedTiles[0] = std::move(tmp);
     398             :     }
     399             :     else
     400             :     {
     401          63 :         if (m_nCachedTileCount > 0)
     402          42 :             std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
     403          63 :         m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
     404          63 :         m_nCachedTileCount++;
     405             :     }
     406             : 
     407             : #if 0
     408             :     CPLDebug("GDAL", "Load tile(%d, %d) of band %d of dataset %s",
     409             :               nTileX, nTileY, m_poBand->GetBand(),
     410             :               m_poBand->GetDataset() ? m_poBand->GetDataset()->GetDescription() : "(unknown)");
     411             : #endif
     412         415 :     CPLAssert(!m_aCachedTiles[0].m_bModified);
     413         415 :     const int nXOff = nTileX * TILE_SIZE;
     414         415 :     const int nYOff = nTileY * TILE_SIZE;
     415         415 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     416         415 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     417         830 :     if (m_poBand->RasterIO(
     418             :             GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
     419         415 :             m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
     420             :             GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     421         415 :             TILE_SIZE * sizeof(Type), nullptr) != CE_None)
     422             :     {
     423           0 :         m_aCachedTiles[0].m_nTileX = -1;
     424           0 :         m_aCachedTiles[0].m_nTileY = -1;
     425           0 :         return false;
     426             :     }
     427         415 :     m_aCachedTiles[0].m_nTileX = nTileX;
     428         415 :     m_aCachedTiles[0].m_nTileY = nTileY;
     429         415 :     return true;
     430             : }
     431             : 
     432             : /************************************************************************/
     433             : /*                          FlushTile()                                 */
     434             : /************************************************************************/
     435             : 
     436             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     437         465 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
     438             :     int iSlot)
     439             : {
     440         465 :     if (!m_aCachedTiles[iSlot].m_bModified)
     441         261 :         return true;
     442             : 
     443         204 :     m_aCachedTiles[iSlot].m_bModified = false;
     444         204 :     const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
     445         204 :     const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
     446         204 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     447         204 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     448         204 :     return m_poBand->RasterIO(
     449             :                GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
     450         204 :                m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
     451             :                GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     452         204 :                TILE_SIZE * sizeof(Type), nullptr) == CE_None;
     453             : }
     454             : 
     455             : #endif  // GDAL_PIXEL_ACCESSOR_INCLUDED

Generated by: LCOV version 1.14