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 * 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
51template <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
84 void SetBand(GDALRasterBand *poBand)
85 {
86 m_poBand = poBand;
87 }
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
112template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
113 GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
114 GDALCachedPixelAccessor(GDALRasterBand *poBand)
115 : m_poBand(poBand)
116{
117}
118
119/************************************************************************/
120/* ~GDALCachedPixelAccessor() */
121/************************************************************************/
122
127template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
128 GDALCachedPixelAccessor<Type, TILE_SIZE,
129 CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
130{
131 FlushCache();
132}
133
134/************************************************************************/
135/* Get() */
136/************************************************************************/
137
147template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
148 inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
149 int nX, int nY, bool *pbSuccess)
150{
151 const int nTileX = nX / TILE_SIZE;
152 const int nTileY = nY / TILE_SIZE;
153 const int nXInTile = nX % TILE_SIZE;
154 const int nYInTile = nY % TILE_SIZE;
155 if (m_aCachedTiles[0].m_nTileX == nTileX &&
156 m_aCachedTiles[0].m_nTileY == nTileY)
157 {
158 if (pbSuccess)
159 *pbSuccess = true;
160 return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
161 }
162 return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
163}
164
165/************************************************************************/
166/* GetSlowPath() */
167/************************************************************************/
168
169template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
170Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
171 int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
172{
173 for (int i = 1; i < m_nCachedTileCount; ++i)
174 {
175 const auto &cachedTile = m_aCachedTiles[i];
176 if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
177 {
178 const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
179 CachedTile tmp = std::move(m_aCachedTiles[i]);
180 for (int j = i; j >= 1; --j)
181 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
182 m_aCachedTiles[0] = std::move(tmp);
183 if (pbSuccess)
184 *pbSuccess = true;
185 return ret;
186 }
187 }
188 if (!LoadTile(nTileX, nTileY))
189 {
190 if (pbSuccess)
191 *pbSuccess = false;
192 return 0;
193 }
194 if (pbSuccess)
195 *pbSuccess = true;
196 return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
197}
198
199/************************************************************************/
200/* Set() */
201/************************************************************************/
202
219template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
220inline bool
221GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
222 Type val)
223{
224 const int nTileX = nX / TILE_SIZE;
225 const int nTileY = nY / TILE_SIZE;
226 const int nXInTile = nX % TILE_SIZE;
227 const int nYInTile = nY % TILE_SIZE;
228 if (m_aCachedTiles[0].m_nTileX == nTileX &&
229 m_aCachedTiles[0].m_nTileY == nTileY)
230 {
231 m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
232 m_aCachedTiles[0].m_bModified = true;
233 return true;
234 }
235 return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
236}
237
238/************************************************************************/
239/* SetSlowPath() */
240/************************************************************************/
241
242template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
243bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
244 int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
245{
246 for (int i = 1; i < m_nCachedTileCount; ++i)
247 {
248 auto &cachedTile = m_aCachedTiles[i];
249 if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
250 {
251 cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
252 cachedTile.m_bModified = true;
253 if (i > 0)
254 {
255 CachedTile tmp = std::move(m_aCachedTiles[i]);
256 for (int j = i; j >= 1; --j)
257 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
258 m_aCachedTiles[0] = std::move(tmp);
259 }
260 return true;
261 }
262 }
263 if (!LoadTile(nTileX, nTileY))
264 {
265 return false;
266 }
267 m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
268 m_aCachedTiles[0].m_bModified = true;
269 return true;
270}
271
272/************************************************************************/
273/* FlushCache() */
274/************************************************************************/
275
280template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
281 bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
282{
283 bool bRet = true;
284 for (int i = 0; i < m_nCachedTileCount; ++i)
285 {
286 if (!FlushTile(i))
287 bRet = false;
288 m_aCachedTiles[i].m_nTileX = -1;
289 m_aCachedTiles[i].m_nTileY = -1;
290 }
291 return bRet;
292}
293
294/************************************************************************/
295/* ResetModifiedFlag() */
296/************************************************************************/
297
300template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
301void GDALCachedPixelAccessor<Type, TILE_SIZE,
302 CACHED_TILE_COUNT>::ResetModifiedFlag()
303{
304 for (int i = 0; i < m_nCachedTileCount; ++i)
305 {
306 m_aCachedTiles[i].m_bModified = false;
307 }
308}
309
310/************************************************************************/
311/* GDALCachedPixelAccessorGetDataType */
312/************************************************************************/
313
315template <class T> struct GDALCachedPixelAccessorGetDataType
316{
317};
318
319template <> struct GDALCachedPixelAccessorGetDataType<GByte>
320{
321 static constexpr GDALDataType DataType = GDT_Byte;
322};
323
324template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
325{
326 static constexpr GDALDataType DataType = GDT_Int8;
327};
328
329template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
330{
331 static constexpr GDALDataType DataType = GDT_UInt16;
332};
333
334template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
335{
336 static constexpr GDALDataType DataType = GDT_Int16;
337};
338
339template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
340{
341 static constexpr GDALDataType DataType = GDT_UInt32;
342};
343
344template <> 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
350template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
351{
352 static constexpr GDALDataType DataType = GDT_UInt64;
353};
354
355template <> struct GDALCachedPixelAccessorGetDataType<long>
356{
357 static constexpr GDALDataType DataType = GDT_Int64;
358};
359#endif
360template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
361{
362 static constexpr GDALDataType DataType = GDT_UInt64;
363};
364
365template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
366{
367 static constexpr GDALDataType DataType = GDT_Int64;
368};
369
370template <> struct GDALCachedPixelAccessorGetDataType<float>
371{
372 static constexpr GDALDataType DataType = GDT_Float32;
373};
374
375template <> struct GDALCachedPixelAccessorGetDataType<double>
376{
377 static constexpr GDALDataType DataType = GDT_Float64;
378};
379
382/************************************************************************/
383/* LoadTile() */
384/************************************************************************/
385
386template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
387bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
388 int nTileX, int nTileY)
389{
390 if (m_nCachedTileCount == CACHED_TILE_COUNT)
391 {
392 if (!FlushTile(CACHED_TILE_COUNT - 1))
393 return false;
394 CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
395 for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
396 m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
397 m_aCachedTiles[0] = std::move(tmp);
398 }
399 else
400 {
401 if (m_nCachedTileCount > 0)
402 std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
403 m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
404 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 CPLAssert(!m_aCachedTiles[0].m_bModified);
413 const int nXOff = nTileX * TILE_SIZE;
414 const int nYOff = nTileY * TILE_SIZE;
415 const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
416 const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
417 if (m_poBand->RasterIO(
418 GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
419 m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
420 GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
421 TILE_SIZE * sizeof(Type), nullptr) != CE_None)
422 {
423 m_aCachedTiles[0].m_nTileX = -1;
424 m_aCachedTiles[0].m_nTileY = -1;
425 return false;
426 }
427 m_aCachedTiles[0].m_nTileX = nTileX;
428 m_aCachedTiles[0].m_nTileY = nTileY;
429 return true;
430}
431
432/************************************************************************/
433/* FlushTile() */
434/************************************************************************/
435
436template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
437bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
438 int iSlot)
439{
440 if (!m_aCachedTiles[iSlot].m_bModified)
441 return true;
442
443 m_aCachedTiles[iSlot].m_bModified = false;
444 const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
445 const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
446 const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
447 const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
448 return m_poBand->RasterIO(
449 GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
450 m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
451 GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
452 TILE_SIZE * sizeof(Type), nullptr) == CE_None;
453}
454
455#endif // GDAL_PIXEL_ACCESSOR_INCLUDED
GDALCachedPixelAccessor
Class to have reasonably fast random pixel access to a raster band, when accessing multiple pixels th...
Definition: gdalcachedpixelaccessor.h:53
GDALCachedPixelAccessor::Set
bool Set(int nX, int nY, Type val)
Set the value of a pixel.
Definition: gdalcachedpixelaccessor.h:221
GDALCachedPixelAccessor::FlushCache
bool FlushCache()
Flush content of modified tiles and drop caches.
Definition: gdalcachedpixelaccessor.h:281
GDALCachedPixelAccessor::SetBand
void SetBand(GDALRasterBand *poBand)
Assign the raster band if not known at construction time.
Definition: gdalcachedpixelaccessor.h:84
GDALCachedPixelAccessor::ResetModifiedFlag
void ResetModifiedFlag()
Reset the modified flag for cached tiles.
Definition: gdalcachedpixelaccessor.h:302
GDALCachedPixelAccessor::Get
Type Get(int nX, int nY, bool *pbSuccess=nullptr)
Get the value of a pixel.
Definition: gdalcachedpixelaccessor.h:148
GDALCachedPixelAccessor::~GDALCachedPixelAccessor
~GDALCachedPixelAccessor()
Destructor.
Definition: gdalcachedpixelaccessor.h:129
GDALRasterBand
A single raster band (or channel).
Definition: gdal_priv.h:1468
cpl_error.h
CPL error handling services.
CPLAssert
#define CPLAssert(expr)
Assert on an expression.
Definition: cpl_error.h:216
CPLDebug
void CPLDebug(const char *, const char *,...)
Display a debugging message.
Definition: cpl_error.cpp:747
GInt16
short GInt16
Int16 type.
Definition: cpl_port.h:181
GInt64
GIntBig GInt64
Signed 64 bit integer type.
Definition: cpl_port.h:236
GUInt32
unsigned int GUInt32
Unsigned int32 type.
Definition: cpl_port.h:177
GUInt64
GUIntBig GUInt64
Unsigned 64 bit integer type.
Definition: cpl_port.h:238
GUInt16
unsigned short GUInt16
Unsigned int16 type.
Definition: cpl_port.h:183
GByte
unsigned char GByte
Unsigned byte type.
Definition: cpl_port.h:185
GInt32
int GInt32
Int32 type.
Definition: cpl_port.h:175
GInt8
signed char GInt8
Signed int8 type.
Definition: cpl_port.h:187
GDALDataType
GDALDataType
Definition: gdal.h:64
GDT_UInt32
@ GDT_UInt32
Definition: gdal.h:70
GDT_UInt64
@ GDT_UInt64
Definition: gdal.h:72
GDT_Int64
@ GDT_Int64
Definition: gdal.h:73
GDT_Byte
@ GDT_Byte
Definition: gdal.h:66
GDT_Int8
@ GDT_Int8
Definition: gdal.h:67
GDT_Float64
@ GDT_Float64
Definition: gdal.h:75
GDT_UInt16
@ GDT_UInt16
Definition: gdal.h:68
GDT_Int16
@ GDT_Int16
Definition: gdal.h:69
GDT_Int32
@ GDT_Int32
Definition: gdal.h:71
GDT_Float32
@ GDT_Float32
Definition: gdal.h:74
GF_Write
@ GF_Write
Definition: gdal.h:134
GF_Read
@ GF_Read
Definition: gdal.h:133
gdal_priv.h
C++ GDAL entry points.