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