DYT/Tool/OpenSceneGraph-3.6.5/include/gdalcachedpixelaccessor.h
2024-12-25 07:49:36 +08:00

440 lines
15 KiB
C++

/******************************************************************************
*
* Project: GDAL
* Purpose: Fast access to individual pixels in a GDALRasterBand
* Author: Even Rouault <even dot rouault at spatialys.com>
*
******************************************************************************
* Copyright (c) 2022, Planet Labs
*
* SPDX-License-Identifier: MIT
****************************************************************************/
#ifndef GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
#define GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
#include "gdal_priv.h"
#include "cpl_error.h"
#include <algorithm>
#include <array>
#include <vector>
/************************************************************************/
/* GDALCachedPixelAccessor */
/************************************************************************/
/** Class to have reasonably fast random pixel access to a raster band, when
* accessing multiple pixels that are close to each other.
*
* This gives faster access than using GDALRasterBand::RasterIO() with
* a 1x1 window.
*
* @since GDAL 3.5
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT = 4>
class GDALCachedPixelAccessor
{
GDALRasterBand *m_poBand = nullptr;
struct CachedTile
{
std::vector<Type> m_data{};
int m_nTileX = -1;
int m_nTileY = -1;
bool m_bModified = false;
};
int m_nCachedTileCount = 0;
std::array<CachedTile, CACHED_TILE_COUNT> m_aCachedTiles{};
bool LoadTile(int nTileX, int nTileY);
bool FlushTile(int iSlot);
Type GetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
bool *pbSuccess);
bool SetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
Type val);
GDALCachedPixelAccessor(const GDALCachedPixelAccessor &) = delete;
GDALCachedPixelAccessor &
operator=(const GDALCachedPixelAccessor &) = delete;
public:
explicit GDALCachedPixelAccessor(GDALRasterBand *poBand);
~GDALCachedPixelAccessor();
/** Assign the raster band if not known at construction time. */
void SetBand(GDALRasterBand *poBand)
{
m_poBand = poBand;
}
Type Get(int nX, int nY, bool *pbSuccess = nullptr);
bool Set(int nX, int nY, Type val);
bool FlushCache();
void ResetModifiedFlag();
};
/************************************************************************/
/* GDALCachedPixelAccessor() */
/************************************************************************/
/** Constructor.
*
* The template accepts the following parameters:
* - Type: should be one of GByte, GUInt16, GInt16, GUInt32, GInt32, GUInt64,
* GInt64, float or double
* - TILE_SIZE: the tile size for the cache of GDALCachedPixelAccessor.
* Use a power of two for faster computation.
* It doesn't need to be the same of the underlying raster
* - CACHED_TILE_COUNT: number of tiles to cache. Should be >= 1. Defaults to 4.
*
* @param poBand Raster band.
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
GDALCachedPixelAccessor(GDALRasterBand *poBand)
: m_poBand(poBand)
{
}
/************************************************************************/
/* ~GDALCachedPixelAccessor() */
/************************************************************************/
/** Destructor.
*
* Will call FlushCache()
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
GDALCachedPixelAccessor<Type, TILE_SIZE,
CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
{
FlushCache();
}
/************************************************************************/
/* Get() */
/************************************************************************/
/** Get the value of a pixel.
*
* No bound checking of nX, nY is done.
*
* @param nX X coordinate (between 0 and GetXSize()-1)
* @param nY Y coordinate (between 0 and GetYSize()-1)
* @param[out] pbSuccess Optional pointer to a success flag
* @return the pixel value
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
int nX, int nY, bool *pbSuccess)
{
const int nTileX = nX / TILE_SIZE;
const int nTileY = nY / TILE_SIZE;
const int nXInTile = nX % TILE_SIZE;
const int nYInTile = nY % TILE_SIZE;
if (m_aCachedTiles[0].m_nTileX == nTileX &&
m_aCachedTiles[0].m_nTileY == nTileY)
{
if (pbSuccess)
*pbSuccess = true;
return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
}
return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
}
/************************************************************************/
/* GetSlowPath() */
/************************************************************************/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
{
for (int i = 1; i < m_nCachedTileCount; ++i)
{
const auto &cachedTile = m_aCachedTiles[i];
if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
{
const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
CachedTile tmp = std::move(m_aCachedTiles[i]);
for (int j = i; j >= 1; --j)
m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
m_aCachedTiles[0] = std::move(tmp);
if (pbSuccess)
*pbSuccess = true;
return ret;
}
}
if (!LoadTile(nTileX, nTileY))
{
if (pbSuccess)
*pbSuccess = false;
return 0;
}
if (pbSuccess)
*pbSuccess = true;
return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
}
/************************************************************************/
/* Set() */
/************************************************************************/
/** Set the value of a pixel.
*
* The actual modification of the underlying raster is deferred until the tile
* is implicit flushed while loading a new tile, or an explicit call to
* FlushCache().
*
* The destructor of GDALCachedPixelAccessor will take care of calling
* FlushCache(), if the user hasn't done it explicitly.
*
* No bound checking of nX, nY is done.
*
* @param nX X coordinate (between 0 and GetXSize()-1)
* @param nY Y coordinate (between 0 and GetYSize()-1)
* @param val pixel value
* @return true if success
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
inline bool
GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
Type val)
{
const int nTileX = nX / TILE_SIZE;
const int nTileY = nY / TILE_SIZE;
const int nXInTile = nX % TILE_SIZE;
const int nYInTile = nY % TILE_SIZE;
if (m_aCachedTiles[0].m_nTileX == nTileX &&
m_aCachedTiles[0].m_nTileY == nTileY)
{
m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
m_aCachedTiles[0].m_bModified = true;
return true;
}
return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
}
/************************************************************************/
/* SetSlowPath() */
/************************************************************************/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
{
for (int i = 1; i < m_nCachedTileCount; ++i)
{
auto &cachedTile = m_aCachedTiles[i];
if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
{
cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
cachedTile.m_bModified = true;
if (i > 0)
{
CachedTile tmp = std::move(m_aCachedTiles[i]);
for (int j = i; j >= 1; --j)
m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
m_aCachedTiles[0] = std::move(tmp);
}
return true;
}
}
if (!LoadTile(nTileX, nTileY))
{
return false;
}
m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
m_aCachedTiles[0].m_bModified = true;
return true;
}
/************************************************************************/
/* FlushCache() */
/************************************************************************/
/** Flush content of modified tiles and drop caches
*
* @return true if success
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
{
bool bRet = true;
for (int i = 0; i < m_nCachedTileCount; ++i)
{
if (!FlushTile(i))
bRet = false;
m_aCachedTiles[i].m_nTileX = -1;
m_aCachedTiles[i].m_nTileY = -1;
}
return bRet;
}
/************************************************************************/
/* ResetModifiedFlag() */
/************************************************************************/
/** Reset the modified flag for cached tiles.
*/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
void GDALCachedPixelAccessor<Type, TILE_SIZE,
CACHED_TILE_COUNT>::ResetModifiedFlag()
{
for (int i = 0; i < m_nCachedTileCount; ++i)
{
m_aCachedTiles[i].m_bModified = false;
}
}
/************************************************************************/
/* GDALCachedPixelAccessorGetDataType */
/************************************************************************/
/*! @cond Doxygen_Suppress */
template <class T> struct GDALCachedPixelAccessorGetDataType
{
};
template <> struct GDALCachedPixelAccessorGetDataType<GByte>
{
static constexpr GDALDataType DataType = GDT_Byte;
};
template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
{
static constexpr GDALDataType DataType = GDT_Int8;
};
template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
{
static constexpr GDALDataType DataType = GDT_UInt16;
};
template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
{
static constexpr GDALDataType DataType = GDT_Int16;
};
template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
{
static constexpr GDALDataType DataType = GDT_UInt32;
};
template <> struct GDALCachedPixelAccessorGetDataType<GInt32>
{
static constexpr GDALDataType DataType = GDT_Int32;
};
#if SIZEOF_UNSIGNED_LONG == 8
// std::uint64_t on Linux 64-bit resolves as unsigned long
template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
{
static constexpr GDALDataType DataType = GDT_UInt64;
};
template <> struct GDALCachedPixelAccessorGetDataType<long>
{
static constexpr GDALDataType DataType = GDT_Int64;
};
#endif
template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
{
static constexpr GDALDataType DataType = GDT_UInt64;
};
template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
{
static constexpr GDALDataType DataType = GDT_Int64;
};
template <> struct GDALCachedPixelAccessorGetDataType<float>
{
static constexpr GDALDataType DataType = GDT_Float32;
};
template <> struct GDALCachedPixelAccessorGetDataType<double>
{
static constexpr GDALDataType DataType = GDT_Float64;
};
/*! @endcond */
/************************************************************************/
/* LoadTile() */
/************************************************************************/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
int nTileX, int nTileY)
{
if (m_nCachedTileCount == CACHED_TILE_COUNT)
{
if (!FlushTile(CACHED_TILE_COUNT - 1))
return false;
CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
m_aCachedTiles[0] = std::move(tmp);
}
else
{
if (m_nCachedTileCount > 0)
std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
m_nCachedTileCount++;
}
#if 0
CPLDebug("GDAL", "Load tile(%d, %d) of band %d of dataset %s",
nTileX, nTileY, m_poBand->GetBand(),
m_poBand->GetDataset() ? m_poBand->GetDataset()->GetDescription() : "(unknown)");
#endif
CPLAssert(!m_aCachedTiles[0].m_bModified);
const int nXOff = nTileX * TILE_SIZE;
const int nYOff = nTileY * TILE_SIZE;
const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
if (m_poBand->RasterIO(
GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
TILE_SIZE * sizeof(Type), nullptr) != CE_None)
{
m_aCachedTiles[0].m_nTileX = -1;
m_aCachedTiles[0].m_nTileY = -1;
return false;
}
m_aCachedTiles[0].m_nTileX = nTileX;
m_aCachedTiles[0].m_nTileY = nTileY;
return true;
}
/************************************************************************/
/* FlushTile() */
/************************************************************************/
template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
int iSlot)
{
if (!m_aCachedTiles[iSlot].m_bModified)
return true;
m_aCachedTiles[iSlot].m_bModified = false;
const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
return m_poBand->RasterIO(
GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
TILE_SIZE * sizeof(Type), nullptr) == CE_None;
}
#endif // GDAL_PIXEL_ACCESSOR_INCLUDED