Line data Source code
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 :
43 : /** Class to have reasonably fast random pixel access to a raster band, when
44 : * accessing multiple pixels that are close to each other.
45 : *
46 : * This gives faster access than using GDALRasterBand::RasterIO() with
47 : * a 1x1 window.
48 : *
49 : * @since GDAL 3.5
50 : */
51 : template <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 :
83 : /** Assign the raster band if not known at construction time. */
84 10 : void SetBand(GDALRasterBand *poBand)
85 : {
86 10 : m_poBand = poBand;
87 10 : }
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 :
100 : /** Constructor.
101 : *
102 : * The template accepts the following parameters:
103 : * - Type: should be one of GByte, GUInt16, GInt16, GUInt32, GInt32, GUInt64,
104 : * GInt64, float or double
105 : * - TILE_SIZE: the tile size for the cache of GDALCachedPixelAccessor.
106 : * Use a power of two for faster computation.
107 : * It doesn't need to be the same of the underlying raster
108 : * - CACHED_TILE_COUNT: number of tiles to cache. Should be >= 1. Defaults to 4.
109 : *
110 : * @param poBand Raster band.
111 : */
112 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
113 21 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
114 : GDALCachedPixelAccessor(GDALRasterBand *poBand)
115 21 : : m_poBand(poBand)
116 : {
117 21 : }
118 :
119 : /************************************************************************/
120 : /* ~GDALCachedPixelAccessor() */
121 : /************************************************************************/
122 :
123 : /** Destructor.
124 : *
125 : * Will call FlushCache()
126 : */
127 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
128 21 : GDALCachedPixelAccessor<Type, TILE_SIZE,
129 : CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
130 : {
131 21 : FlushCache();
132 21 : }
133 :
134 : /************************************************************************/
135 : /* Get() */
136 : /************************************************************************/
137 :
138 : /** Get the value of a pixel.
139 : *
140 : * No bound checking of nX, nY is done.
141 : *
142 : * @param nX X coordinate (between 0 and GetXSize()-1)
143 : * @param nY Y coordinate (between 0 and GetYSize()-1)
144 : * @param[out] pbSuccess Optional pointer to a success flag
145 : * @return the pixel value
146 : */
147 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
148 16173083 : inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
149 : int nX, int nY, bool *pbSuccess)
150 : {
151 16173083 : const int nTileX = nX / TILE_SIZE;
152 16173083 : const int nTileY = nY / TILE_SIZE;
153 16173083 : const int nXInTile = nX % TILE_SIZE;
154 16173083 : const int nYInTile = nY % TILE_SIZE;
155 32343447 : if (m_aCachedTiles[0].m_nTileX == nTileX &&
156 16170374 : m_aCachedTiles[0].m_nTileY == nTileY)
157 : {
158 16170374 : if (pbSuccess)
159 0 : *pbSuccess = true;
160 16170374 : return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
161 : }
162 2713 : return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
163 : }
164 :
165 : /************************************************************************/
166 : /* GetSlowPath() */
167 : /************************************************************************/
168 :
169 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
170 2713 : Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
171 : int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
172 : {
173 3878 : for (int i = 1; i < m_nCachedTileCount; ++i)
174 : {
175 3667 : const auto &cachedTile = m_aCachedTiles[i];
176 3667 : if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
177 : {
178 2502 : const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
179 2502 : CachedTile tmp = std::move(m_aCachedTiles[i]);
180 5569 : for (int j = i; j >= 1; --j)
181 3067 : m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
182 2502 : m_aCachedTiles[0] = std::move(tmp);
183 2502 : if (pbSuccess)
184 0 : *pbSuccess = true;
185 2502 : return ret;
186 : }
187 : }
188 211 : if (!LoadTile(nTileX, nTileY))
189 : {
190 0 : if (pbSuccess)
191 0 : *pbSuccess = false;
192 0 : return 0;
193 : }
194 211 : if (pbSuccess)
195 0 : *pbSuccess = true;
196 211 : return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
197 : }
198 :
199 : /************************************************************************/
200 : /* Set() */
201 : /************************************************************************/
202 :
203 : /** Set the value of a pixel.
204 : *
205 : * The actual modification of the underlying raster is deferred until the tile
206 : * is implicit flushed while loading a new tile, or an explicit call to
207 : * FlushCache().
208 : *
209 : * The destructor of GDALCachedPixelAccessor will take care of calling
210 : * FlushCache(), if the user hasn't done it explicitly.
211 : *
212 : * No bound checking of nX, nY is done.
213 : *
214 : * @param nX X coordinate (between 0 and GetXSize()-1)
215 : * @param nY Y coordinate (between 0 and GetYSize()-1)
216 : * @param val pixel value
217 : * @return true if success
218 : */
219 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
220 : inline bool
221 1473423 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
222 : Type val)
223 : {
224 1473423 : const int nTileX = nX / TILE_SIZE;
225 1473423 : const int nTileY = nY / TILE_SIZE;
226 1473423 : const int nXInTile = nX % TILE_SIZE;
227 1473423 : const int nYInTile = nY % TILE_SIZE;
228 2945877 : if (m_aCachedTiles[0].m_nTileX == nTileX &&
229 1472454 : m_aCachedTiles[0].m_nTileY == nTileY)
230 : {
231 1472454 : m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
232 1472454 : m_aCachedTiles[0].m_bModified = true;
233 1472454 : return true;
234 : }
235 962 : return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
236 : }
237 :
238 : /************************************************************************/
239 : /* SetSlowPath() */
240 : /************************************************************************/
241 :
242 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
243 962 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
244 : int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
245 : {
246 2007 : for (int i = 1; i < m_nCachedTileCount; ++i)
247 : {
248 1803 : auto &cachedTile = m_aCachedTiles[i];
249 1803 : if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
250 : {
251 758 : cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
252 758 : cachedTile.m_bModified = true;
253 758 : if (i > 0)
254 : {
255 1516 : CachedTile tmp = std::move(m_aCachedTiles[i]);
256 2066 : for (int j = i; j >= 1; --j)
257 1308 : m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
258 758 : m_aCachedTiles[0] = std::move(tmp);
259 : }
260 758 : return true;
261 : }
262 : }
263 204 : if (!LoadTile(nTileX, nTileY))
264 : {
265 0 : return false;
266 : }
267 204 : m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
268 204 : m_aCachedTiles[0].m_bModified = true;
269 204 : return true;
270 : }
271 :
272 : /************************************************************************/
273 : /* FlushCache() */
274 : /************************************************************************/
275 :
276 : /** Flush content of modified tiles and drop caches
277 : *
278 : * @return true if success
279 : */
280 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
281 36 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
282 : {
283 36 : bool bRet = true;
284 149 : for (int i = 0; i < m_nCachedTileCount; ++i)
285 : {
286 113 : if (!FlushTile(i))
287 0 : bRet = false;
288 113 : m_aCachedTiles[i].m_nTileX = -1;
289 113 : m_aCachedTiles[i].m_nTileY = -1;
290 : }
291 36 : return bRet;
292 : }
293 :
294 : /************************************************************************/
295 : /* ResetModifiedFlag() */
296 : /************************************************************************/
297 :
298 : /** Reset the modified flag for cached tiles.
299 : */
300 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
301 10 : void GDALCachedPixelAccessor<Type, TILE_SIZE,
302 : CACHED_TILE_COUNT>::ResetModifiedFlag()
303 : {
304 29 : for (int i = 0; i < m_nCachedTileCount; ++i)
305 : {
306 19 : m_aCachedTiles[i].m_bModified = false;
307 : }
308 10 : }
309 :
310 : /************************************************************************/
311 : /* GDALCachedPixelAccessorGetDataType */
312 : /************************************************************************/
313 :
314 : /*! @cond Doxygen_Suppress */
315 : template <class T> struct GDALCachedPixelAccessorGetDataType
316 : {
317 : };
318 :
319 : template <> struct GDALCachedPixelAccessorGetDataType<GByte>
320 : {
321 : static constexpr GDALDataType DataType = GDT_Byte;
322 : };
323 :
324 : template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
325 : {
326 : static constexpr GDALDataType DataType = GDT_Int8;
327 : };
328 :
329 : template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
330 : {
331 : static constexpr GDALDataType DataType = GDT_UInt16;
332 : };
333 :
334 : template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
335 : {
336 : static constexpr GDALDataType DataType = GDT_Int16;
337 : };
338 :
339 : template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
340 : {
341 : static constexpr GDALDataType DataType = GDT_UInt32;
342 : };
343 :
344 : template <> 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
350 : template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
351 : {
352 : static constexpr GDALDataType DataType = GDT_UInt64;
353 : };
354 :
355 : template <> struct GDALCachedPixelAccessorGetDataType<long>
356 : {
357 : static constexpr GDALDataType DataType = GDT_Int64;
358 : };
359 : #endif
360 : template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
361 : {
362 : static constexpr GDALDataType DataType = GDT_UInt64;
363 : };
364 :
365 : template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
366 : {
367 : static constexpr GDALDataType DataType = GDT_Int64;
368 : };
369 :
370 : template <> struct GDALCachedPixelAccessorGetDataType<float>
371 : {
372 : static constexpr GDALDataType DataType = GDT_Float32;
373 : };
374 :
375 : template <> struct GDALCachedPixelAccessorGetDataType<double>
376 : {
377 : static constexpr GDALDataType DataType = GDT_Float64;
378 : };
379 :
380 : /*! @endcond */
381 :
382 : /************************************************************************/
383 : /* LoadTile() */
384 : /************************************************************************/
385 :
386 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
387 415 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
388 : int nTileX, int nTileY)
389 : {
390 415 : if (m_nCachedTileCount == CACHED_TILE_COUNT)
391 : {
392 352 : if (!FlushTile(CACHED_TILE_COUNT - 1))
393 0 : return false;
394 704 : CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
395 1408 : for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
396 1056 : m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
397 352 : m_aCachedTiles[0] = std::move(tmp);
398 : }
399 : else
400 : {
401 63 : if (m_nCachedTileCount > 0)
402 42 : std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
403 63 : m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
404 63 : 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 415 : CPLAssert(!m_aCachedTiles[0].m_bModified);
413 415 : const int nXOff = nTileX * TILE_SIZE;
414 415 : const int nYOff = nTileY * TILE_SIZE;
415 415 : const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
416 415 : const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
417 830 : if (m_poBand->RasterIO(
418 : GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
419 415 : m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
420 : GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
421 415 : TILE_SIZE * sizeof(Type), nullptr) != CE_None)
422 : {
423 0 : m_aCachedTiles[0].m_nTileX = -1;
424 0 : m_aCachedTiles[0].m_nTileY = -1;
425 0 : return false;
426 : }
427 415 : m_aCachedTiles[0].m_nTileX = nTileX;
428 415 : m_aCachedTiles[0].m_nTileY = nTileY;
429 415 : return true;
430 : }
431 :
432 : /************************************************************************/
433 : /* FlushTile() */
434 : /************************************************************************/
435 :
436 : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
437 465 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
438 : int iSlot)
439 : {
440 465 : if (!m_aCachedTiles[iSlot].m_bModified)
441 261 : return true;
442 :
443 204 : m_aCachedTiles[iSlot].m_bModified = false;
444 204 : const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
445 204 : const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
446 204 : const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
447 204 : const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
448 204 : return m_poBand->RasterIO(
449 : GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
450 204 : m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
451 : GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
452 204 : TILE_SIZE * sizeof(Type), nullptr) == CE_None;
453 : }
454 :
455 : #endif // GDAL_PIXEL_ACCESSOR_INCLUDED
|