GDAL
gdalcachedpixelaccessor.h
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
19#include <algorithm>
20#include <array>
21#include <vector>
22
23/************************************************************************/
24/* GDALCachedPixelAccessor */
25/************************************************************************/
26
35template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT = 4>
37{
38 GDALRasterBand *m_poBand = nullptr;
39
40 struct CachedTile
41 {
42 std::vector<Type> m_data{};
43 int m_nTileX = -1;
44 int m_nTileY = -1;
45 bool m_bModified = false;
46 };
47
48 int m_nCachedTileCount = 0;
49 std::array<CachedTile, CACHED_TILE_COUNT> m_aCachedTiles{};
50
51 bool LoadTile(int nTileX, int nTileY);
52 bool FlushTile(int iSlot);
53
54 Type GetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
55 bool *pbSuccess);
56 bool SetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
57 Type val);
58
61 operator=(const GDALCachedPixelAccessor &) = delete;
62
63 public:
66
68 void SetBand(GDALRasterBand *poBand)
69 {
70 m_poBand = poBand;
71 }
72
73 Type Get(int nX, int nY, bool *pbSuccess = nullptr);
74 bool Set(int nX, int nY, Type val);
75
76 bool FlushCache();
77 void ResetModifiedFlag();
78};
79
80/************************************************************************/
81/* GDALCachedPixelAccessor() */
82/************************************************************************/
83
96template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
99 : m_poBand(poBand)
100{
101}
102
103/************************************************************************/
104/* ~GDALCachedPixelAccessor() */
105/************************************************************************/
106
111template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
112GDALCachedPixelAccessor<Type, TILE_SIZE,
113 CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
114{
115 FlushCache();
116}
117
118/************************************************************************/
119/* Get() */
120/************************************************************************/
121
131template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
133 int nX, int nY, bool *pbSuccess)
134{
135 const int nTileX = nX / TILE_SIZE;
136 const int nTileY = nY / TILE_SIZE;
137 const int nXInTile = nX % TILE_SIZE;
138 const int nYInTile = nY % TILE_SIZE;
139 if (m_aCachedTiles[0].m_nTileX == nTileX &&
140 m_aCachedTiles[0].m_nTileY == nTileY)
141 {
142 if (pbSuccess)
143 *pbSuccess = true;
144 return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
145 }
146 return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
147}
148
149/************************************************************************/
150/* GetSlowPath() */
151/************************************************************************/
152
153template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
155 int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
156{
157 for (int i = 1; i < m_nCachedTileCount; ++i)
158 {
159 const auto &cachedTile = m_aCachedTiles[i];
160 if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
161 {
162 const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
163 CachedTile tmp = std::move(m_aCachedTiles[i]);
164 for (int j = i; j >= 1; --j)
165 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
166 m_aCachedTiles[0] = std::move(tmp);
167 if (pbSuccess)
168 *pbSuccess = true;
169 return ret;
170 }
171 }
172 if (!LoadTile(nTileX, nTileY))
173 {
174 if (pbSuccess)
175 *pbSuccess = false;
176 return 0;
177 }
178 if (pbSuccess)
179 *pbSuccess = true;
180 return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
181}
182
183/************************************************************************/
184/* Set() */
185/************************************************************************/
186
203template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
204inline bool
206 Type val)
207{
208 const int nTileX = nX / TILE_SIZE;
209 const int nTileY = nY / TILE_SIZE;
210 const int nXInTile = nX % TILE_SIZE;
211 const int nYInTile = nY % TILE_SIZE;
212 if (m_aCachedTiles[0].m_nTileX == nTileX &&
213 m_aCachedTiles[0].m_nTileY == nTileY)
214 {
215 m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
216 m_aCachedTiles[0].m_bModified = true;
217 return true;
218 }
219 return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
220}
221
222/************************************************************************/
223/* SetSlowPath() */
224/************************************************************************/
225
226template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
228 int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
229{
230 for (int i = 1; i < m_nCachedTileCount; ++i)
231 {
232 auto &cachedTile = m_aCachedTiles[i];
233 if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
234 {
235 cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
236 cachedTile.m_bModified = true;
237 if (i > 0)
238 {
239 CachedTile tmp = std::move(m_aCachedTiles[i]);
240 for (int j = i; j >= 1; --j)
241 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
242 m_aCachedTiles[0] = std::move(tmp);
243 }
244 return true;
245 }
246 }
247 if (!LoadTile(nTileX, nTileY))
248 {
249 return false;
250 }
251 m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
252 m_aCachedTiles[0].m_bModified = true;
253 return true;
254}
255
256/************************************************************************/
257/* FlushCache() */
258/************************************************************************/
259
264template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
266{
267 bool bRet = true;
268 for (int i = 0; i < m_nCachedTileCount; ++i)
269 {
270 if (!FlushTile(i))
271 bRet = false;
272 m_aCachedTiles[i].m_nTileX = -1;
273 m_aCachedTiles[i].m_nTileY = -1;
274 }
275 return bRet;
276}
277
278/************************************************************************/
279/* ResetModifiedFlag() */
280/************************************************************************/
281
284template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
285void GDALCachedPixelAccessor<Type, TILE_SIZE,
286 CACHED_TILE_COUNT>::ResetModifiedFlag()
287{
288 for (int i = 0; i < m_nCachedTileCount; ++i)
289 {
290 m_aCachedTiles[i].m_bModified = false;
291 }
292}
293
294/************************************************************************/
295/* GDALCachedPixelAccessorGetDataType */
296/************************************************************************/
297
299template <class T> struct GDALCachedPixelAccessorGetDataType
300{
301};
302
303template <> struct GDALCachedPixelAccessorGetDataType<GByte>
304{
305 static constexpr GDALDataType DataType = GDT_Byte;
306};
307
308template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
309{
310 static constexpr GDALDataType DataType = GDT_Int8;
311};
312
313template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
314{
315 static constexpr GDALDataType DataType = GDT_UInt16;
316};
317
318template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
319{
320 static constexpr GDALDataType DataType = GDT_Int16;
321};
322
323template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
324{
325 static constexpr GDALDataType DataType = GDT_UInt32;
326};
327
328template <> struct GDALCachedPixelAccessorGetDataType<GInt32>
329{
330 static constexpr GDALDataType DataType = GDT_Int32;
331};
332#if SIZEOF_UNSIGNED_LONG == 8
333// std::uint64_t on Linux 64-bit resolves as unsigned long
334template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
335{
336 static constexpr GDALDataType DataType = GDT_UInt64;
337};
338
339template <> struct GDALCachedPixelAccessorGetDataType<long>
340{
341 static constexpr GDALDataType DataType = GDT_Int64;
342};
343#endif
344template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
345{
346 static constexpr GDALDataType DataType = GDT_UInt64;
347};
348
349template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
350{
351 static constexpr GDALDataType DataType = GDT_Int64;
352};
353
354template <> struct GDALCachedPixelAccessorGetDataType<float>
355{
356 static constexpr GDALDataType DataType = GDT_Float32;
357};
358
359template <> struct GDALCachedPixelAccessorGetDataType<double>
360{
361 static constexpr GDALDataType DataType = GDT_Float64;
362};
363
366/************************************************************************/
367/* LoadTile() */
368/************************************************************************/
369
370template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
372 int nTileX, int nTileY)
373{
374 if (m_nCachedTileCount == CACHED_TILE_COUNT)
375 {
376 if (!FlushTile(CACHED_TILE_COUNT - 1))
377 return false;
378 CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
379 for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
380 m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
381 m_aCachedTiles[0] = std::move(tmp);
382 }
383 else
384 {
385 if (m_nCachedTileCount > 0)
386 std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
387 m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
388 m_nCachedTileCount++;
389 }
390
391#if 0
392 CPLDebug("GDAL", "Load tile(%d, %d) of band %d of dataset %s",
393 nTileX, nTileY, m_poBand->GetBand(),
394 m_poBand->GetDataset() ? m_poBand->GetDataset()->GetDescription() : "(unknown)");
395#endif
396 CPLAssert(!m_aCachedTiles[0].m_bModified);
397 const int nXOff = nTileX * TILE_SIZE;
398 const int nYOff = nTileY * TILE_SIZE;
399 const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
400 const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
401 if (m_poBand->RasterIO(
402 GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
403 m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
404 GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
405 TILE_SIZE * sizeof(Type), nullptr) != CE_None)
406 {
407 m_aCachedTiles[0].m_nTileX = -1;
408 m_aCachedTiles[0].m_nTileY = -1;
409 return false;
410 }
411 m_aCachedTiles[0].m_nTileX = nTileX;
412 m_aCachedTiles[0].m_nTileY = nTileY;
413 return true;
414}
415
416/************************************************************************/
417/* FlushTile() */
418/************************************************************************/
419
420template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
422 int iSlot)
423{
424 if (!m_aCachedTiles[iSlot].m_bModified)
425 return true;
426
427 m_aCachedTiles[iSlot].m_bModified = false;
428 const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
429 const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
430 const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
431 const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
432 return m_poBand->RasterIO(
433 GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
434 m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
435 GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
436 TILE_SIZE * sizeof(Type), nullptr) == CE_None;
437}
438
439#endif // GDAL_PIXEL_ACCESSOR_INCLUDED
Class to have reasonably fast random pixel access to a raster band, when accessing multiple pixels th...
Definition: gdalcachedpixelaccessor.h:37
bool Set(int nX, int nY, Type val)
Set the value of a pixel.
Definition: gdalcachedpixelaccessor.h:205
bool FlushCache()
Flush content of modified tiles and drop caches.
Definition: gdalcachedpixelaccessor.h:265
void SetBand(GDALRasterBand *poBand)
Assign the raster band if not known at construction time.
Definition: gdalcachedpixelaccessor.h:68
void ResetModifiedFlag()
Reset the modified flag for cached tiles.
Definition: gdalcachedpixelaccessor.h:286
Type Get(int nX, int nY, bool *pbSuccess=nullptr)
Get the value of a pixel.
Definition: gdalcachedpixelaccessor.h:132
~GDALCachedPixelAccessor()
Destructor.
Definition: gdalcachedpixelaccessor.h:113
A single raster band (or channel).
Definition: gdal_priv.h:1519
CPL error handling services.
#define CPLAssert(expr)
Assert on an expression.
Definition: cpl_error.h:209
void CPLDebug(const char *, const char *,...)
Display a debugging message.
Definition: cpl_error.cpp:731
short GInt16
Int16 type.
Definition: cpl_port.h:165
GIntBig GInt64
Signed 64 bit integer type.
Definition: cpl_port.h:220
unsigned int GUInt32
Unsigned int32 type.
Definition: cpl_port.h:161
GUIntBig GUInt64
Unsigned 64 bit integer type.
Definition: cpl_port.h:222
unsigned short GUInt16
Unsigned int16 type.
Definition: cpl_port.h:167
unsigned char GByte
Unsigned byte type.
Definition: cpl_port.h:169
int GInt32
Int32 type.
Definition: cpl_port.h:159
signed char GInt8
Signed int8 type.
Definition: cpl_port.h:171
GDALDataType
Definition: gdal.h:48
@ GDT_UInt32
Definition: gdal.h:54
@ GDT_UInt64
Definition: gdal.h:56
@ GDT_Int64
Definition: gdal.h:57
@ GDT_Byte
Definition: gdal.h:50
@ GDT_Int8
Definition: gdal.h:51
@ GDT_Float64
Definition: gdal.h:59
@ GDT_UInt16
Definition: gdal.h:52
@ GDT_Int16
Definition: gdal.h:53
@ GDT_Int32
Definition: gdal.h:55
@ GDT_Float32
Definition: gdal.h:58
@ GF_Write
Definition: gdal.h:119
@ GF_Read
Definition: gdal.h:118
C++ GDAL entry points.