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