Grok 15.1.0
SparseCanvas.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016-2025 Grok Image Compression Inc.
3 *
4 * This source code is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License, version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This source code is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Affero General Public License for more details.
12 *
13 * You should have received a copy of the GNU Affero General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <cstdint>
19#include <algorithm>
20
21// SparseCanvas stores blocks in the canvas coordinate system. It covers the active sub-bands for
22// all (reduced) resolutions
23
24/***
25 *
26 * SparseCanvas stores blocks of size LBW x LBH in canvase coordinate system (with offset)
27 * Blocks are only allocated for active sub-bands for reduced resolutions
28 *
29 * Data is pass in and out in a linear array, chunked either along the y axis
30 * or along the x axis, depending on whether we are working with a horizontal strip
31 * or a vertical strip of data.
32 *
33 *
34 */
35
36namespace grk
37{
39{
40public:
41 virtual ~ISparseCanvas() = default;
45 virtual bool read(uint8_t resno, grk_rect32 window, int32_t* dest, const uint32_t destChunkY,
46 const uint32_t destChunkX) = 0;
50 virtual bool write(uint8_t resno, grk_rect32 window, const int32_t* src, const uint32_t srcChunkY,
51 const uint32_t srcChunkX) = 0;
52
53 virtual bool alloc(grk_rect32 window, bool zeroOutBuffer) = 0;
54};
56{
57 SparseBlock(void) : data(nullptr) {}
59 {
60 delete[] data;
61 }
62 void alloc(uint32_t block_area, bool zeroOutBuffer)
63 {
64 data = new int32_t[block_area];
65 if(zeroOutBuffer)
66 memset(data, 0, block_area * sizeof(int32_t));
67 }
68 int32_t* data;
69};
70template<uint32_t LBW, uint32_t LBH>
72{
73public:
75 : blockWidth(1 << LBW), blockHeight(1 << LBH), blocks(nullptr), bounds(bds)
76 {
77 if(!bounds.width() || !bounds.height() || !LBW || !LBH)
78 throw std::runtime_error("invalid window for sparse canvas");
79 grid = bounds.scaleDownPow2(LBW, LBH);
80 auto blockCount = grid.area();
81 blocks = new SparseBlock*[blockCount];
82 for(uint64_t i = 0; i < blockCount; ++i)
83 blocks[i] = nullptr;
84 }
85 SparseCanvas(uint32_t width, uint32_t height) : SparseCanvas(grk_rect32(0, 0, width, height)) {}
87 {
88 if(blocks)
89 {
90 for(uint64_t i = 0; i < (uint64_t)grid.width() * grid.height(); i++)
91 {
92 delete(blocks[i]);
93 blocks[i] = nullptr;
94 }
95 delete[] blocks;
96 }
97 }
98 bool read(uint8_t resno, grk_rect32 window, int32_t* dest, const uint32_t destChunkY,
99 const uint32_t destChunkX)
100 {
101 return readWrite(resno, window, dest, destChunkY, destChunkX, true);
102 }
103 bool write(uint8_t resno, grk_rect32 window, const int32_t* src, const uint32_t srcChunkY,
104 const uint32_t srcChunkX)
105 {
106 return readWrite(resno, window, (int32_t*)src, srcChunkY, srcChunkX, false);
107 }
108 bool alloc(grk_rect32 win, bool zeroOutBuffer)
109 {
111 return true;
112 uint32_t blockWinHeight = 0;
113 uint32_t gridY = win.y0 >> LBH;
114 for(uint32_t y = win.y0; y < win.y1; gridY++, y += blockWinHeight)
115 {
116 blockWinHeight = (y == win.y0) ? blockHeight - (win.y0 & (blockHeight - 1)) : blockHeight;
117 blockWinHeight = (std::min<uint32_t>)(blockWinHeight, win.y1 - y);
118 uint32_t gridX = win.x0 >> LBW;
119 uint32_t blockWinWidth = 0;
120 for(uint32_t x = win.x0; x < win.x1; gridX++, x += blockWinWidth)
121 {
122 blockWinWidth = (x == win.x0) ? blockWidth - (win.x0 & (blockWidth - 1)) : blockWidth;
123 blockWinWidth = (std::min<uint32_t>)(blockWinWidth, win.x1 - x);
124 if(!grid.contains(gridX, gridY))
125 {
126 grklog.warn("sparse canvas : attempt to allocate a block (%u,%u) outside block "
127 "grid bounds (%u,%u,%u,%u)",
128 gridX, gridY, grid.x0, grid.y0, grid.x1, grid.y1);
129 return false;
130 }
131 auto srcBlock = getBlock(gridX, gridY);
132 if(!srcBlock)
133 {
134 auto b = new SparseBlock();
135 b->alloc(blockWidth * blockHeight, zeroOutBuffer);
136 assert(grid.contains(gridX, gridY));
137 assert(b->data);
138 uint64_t blockInd = (uint64_t)(gridY - grid.y0) * grid.width() + (gridX - grid.x0);
139 blocks[blockInd] = b;
140 }
141 }
142 }
143 return true;
144 }
145
146private:
147 inline SparseBlock* getBlock(uint32_t block_x, uint32_t block_y)
148 {
149 uint64_t index = (uint64_t)(block_y - grid.y0) * grid.width() + (block_x - grid.x0);
150 return blocks[index];
151 }
153 {
154 return !(win.x0 >= bounds.x1 || win.x1 <= win.x0 || win.x1 > bounds.x1 || win.y0 >= bounds.y1 ||
155 win.y1 <= win.y0 || win.y1 > bounds.y1);
156 }
157 bool readWrite(uint8_t resno, grk_rect32 win, int32_t* buf, const uint32_t spacingX,
158 const uint32_t spacingY, bool isReadOperation)
159 {
160 if(!win.valid())
161 return false;
162 assert(!isReadOperation || buf);
163
164 if(!isWindowValid(win))
165 {
166 grklog.warn("Sparse canvas @ res %u, attempt to read/write invalid window (%u,%u,%u,%u) "
167 "for bounds (%u,%u,%u,%u).",
168 resno, win.x0, win.y0, win.x1, win.y1, bounds.x0, bounds.y0, bounds.x1,
169 bounds.y1);
170 return false;
171 }
172 assert(spacingY != 0 || win.height() == 1);
173 assert((spacingY <= 1 && spacingX >= 1) || (spacingY >= 1 && spacingX == 1));
174
175 uint32_t gridY = win.y0 >> LBH;
176 uint32_t blockWinHeight = 0;
177 for(uint32_t y = win.y0; y < win.y1; gridY++, y += blockWinHeight)
178 {
179 blockWinHeight = (y == win.y0) ? blockHeight - (win.y0 & (blockHeight - 1)) : blockHeight;
180 uint32_t blockOffsetY = blockHeight - blockWinHeight;
181 blockWinHeight = (std::min<uint32_t>)(blockWinHeight, win.y1 - y);
182 uint32_t gridX = win.x0 >> LBW;
183 uint32_t blockWinWidth = 0;
184 for(uint32_t x = win.x0; x < win.x1; gridX++, x += blockWinWidth)
185 {
186 blockWinWidth = (x == win.x0) ? blockWidth - (win.x0 & (blockWidth - 1)) : blockWidth;
187 uint32_t blockOffsetX = blockWidth - blockWinWidth;
188 blockWinWidth = (std::min<uint32_t>)(blockWinWidth, win.x1 - x);
189 if(!grid.contains(gridX, gridY))
190 {
191 grklog.warn("sparse canvas @ resno %u, Attempt to access a block (%u,%u) outside "
192 "block grid bounds",
193 resno, gridX, gridY);
194 return false;
195 }
196 auto srcBlock = getBlock(gridX, gridY);
197 if(!srcBlock)
198 {
199 grklog.warn("sparse canvas @ resno %u, %s op: missing block (%u,%u,%u,%u) for %s "
200 "(%u,%u,%u,%u). Skipping.",
201 resno, isReadOperation ? "read" : "write", bounds.x0 + gridX * blockWidth,
202 bounds.y0 + gridY * blockHeight, bounds.x0 + (gridX + 1) * blockWidth,
203 bounds.y0 + (gridY + 1) * blockHeight, isReadOperation ? "read" : "write",
204 win.x0, win.y0, win.x1, win.y1);
205 continue;
206 }
207 if(isReadOperation)
208 {
209 auto src = srcBlock->data + ((uint64_t)blockOffsetY << LBW) + blockOffsetX;
210 auto dest = buf + (y - win.y0) * spacingY + (x - win.x0) * spacingX;
211 for(uint32_t blockY = 0; blockY < blockWinHeight; blockY++)
212 {
213 uint64_t destInd = 0;
214 for(uint32_t blockX = 0; blockX < blockWinWidth; blockX++)
215 {
216#ifdef GRK_DEBUG_VALGRIND
217 size_t val = grk_memcheck<int32_t>(src + blockX, 1);
218 if(val != grk_mem_ok)
219 grklog.error("sparse canvas @resno %u, read block(%u,%u) : "
220 "uninitialized at location (%u,%u)",
221 resno, gridX, gridY, x + blockX, y_);
222#endif
223 dest[destInd] = src[blockX];
224 destInd += spacingX;
225 }
226 dest += spacingY;
227 src += blockWidth;
228 }
229 }
230 else
231 {
232 const int32_t* src = nullptr;
233 if(buf)
234 src = buf + (y - win.y0) * spacingY + (x - win.x0) * spacingX;
235 auto dest = srcBlock->data + ((uint64_t)blockOffsetY << LBW) + blockOffsetX;
236 for(uint32_t blockY = 0; blockY < blockWinHeight; blockY++)
237 {
238 uint64_t srcInd = 0;
239 for(uint32_t blockX = 0; blockX < blockWinWidth; blockX++)
240 {
241#ifdef GRK_DEBUG_VALGRIND
242 if(src)
243 {
244 grk_pt32 pt((uint32_t)(x + blockX), y_);
245 size_t val = grk_memcheck<int32_t>(src + srcInd, 1);
246 if(val != grk_mem_ok)
247 grklog.error("sparse canvas @ resno %u, write block(%u,%u): "
248 "uninitialized at location (%u,%u)",
249 resno, gridX, gridY, x + blockX, y_);
250 }
251#endif
252 dest[blockX] = src ? src[srcInd] : 0;
253 srcInd += spacingX;
254 }
255 if(src)
256 src += spacingY;
257 dest += blockWidth;
258 }
259 }
260 }
261 }
262 return true;
263 }
264
265private:
266 const uint32_t blockWidth;
267 const uint32_t blockHeight;
269 grk_rect32 bounds; // canvas bounds
270 grk_rect32 grid; // block grid bounds
271};
272
273} // namespace grk
Definition SparseCanvas.h:39
virtual ~ISparseCanvas()=default
virtual bool alloc(grk_rect32 window, bool zeroOutBuffer)=0
virtual bool read(uint8_t resno, grk_rect32 window, int32_t *dest, const uint32_t destChunkY, const uint32_t destChunkX)=0
Read window of data into dest buffer.
virtual bool write(uint8_t resno, grk_rect32 window, const int32_t *src, const uint32_t srcChunkY, const uint32_t srcChunkX)=0
Write window of data from src buffer.
SparseBlock * getBlock(uint32_t block_x, uint32_t block_y)
Definition SparseCanvas.h:147
grk_rect32 bounds
Definition SparseCanvas.h:269
SparseCanvas(uint32_t width, uint32_t height)
Definition SparseCanvas.h:85
const uint32_t blockWidth
Definition SparseCanvas.h:266
bool alloc(grk_rect32 win, bool zeroOutBuffer)
Definition SparseCanvas.h:108
SparseBlock ** blocks
Definition SparseCanvas.h:268
bool readWrite(uint8_t resno, grk_rect32 win, int32_t *buf, const uint32_t spacingX, const uint32_t spacingY, bool isReadOperation)
Definition SparseCanvas.h:157
~SparseCanvas()
Definition SparseCanvas.h:86
bool read(uint8_t resno, grk_rect32 window, int32_t *dest, const uint32_t destChunkY, const uint32_t destChunkX)
Read window of data into dest buffer.
Definition SparseCanvas.h:98
SparseCanvas(grk_rect32 bds)
Definition SparseCanvas.h:74
bool write(uint8_t resno, grk_rect32 window, const int32_t *src, const uint32_t srcChunkY, const uint32_t srcChunkX)
Write window of data from src buffer.
Definition SparseCanvas.h:103
const uint32_t blockHeight
Definition SparseCanvas.h:267
grk_rect32 grid
Definition SparseCanvas.h:270
bool isWindowValid(grk_rect32 win)
Definition SparseCanvas.h:152
Copyright (C) 2016-2025 Grok Image Compression Inc.
Definition ICacheable.h:20
grk_rect< uint32_t > grk_rect32
Definition geometry.h:61
grk_pt< uint32_t > grk_pt32
Definition geometry.h:40
Logger & grklog
Definition Logger.cpp:20
Definition SparseCanvas.h:56
SparseBlock(void)
Definition SparseCanvas.h:57
void alloc(uint32_t block_area, bool zeroOutBuffer)
Definition SparseCanvas.h:62
int32_t * data
Definition SparseCanvas.h:68
~SparseBlock()
Definition SparseCanvas.h:58
T y1
Definition geometry.h:125
T x0
Definition geometry.h:125
T x1
Definition geometry.h:125
T height() const
Definition geometry.h:339
bool valid(void) const
Definition geometry.h:186
T y0
Definition geometry.h:125