Grok 20.3.2
SparseCanvas.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016-2026 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 */
17
18#pragma once
19
20#include <cstdint>
21#include <algorithm>
22#include <unordered_map>
23
24// SparseCanvas stores blocks in the canvas coordinate system. It covers the active sub-bands for
25// all (reduced) resolutions
26
27/***
28 *
29 * SparseCanvas stores blocks of size LBW x LBH in canvas coordinate system (with offset)
30 * Blocks are only allocated for active sub-bands for reduced resolutions
31 *
32 * Data is passed in and out in a linear array, chunked either along the y axis
33 * or along the x axis, depending on whether we are working with a horizontal strip
34 * or a vertical strip of data.
35 *
36 *
37 */
38
39#include "ISparseCanvas.h"
40
41namespace grk
42{
43
44template<typename T, uint32_t LBW, uint32_t LBH>
45class SparseCanvas : public ISparseCanvas<T>
46{
47public:
48 SparseCanvas(Rect32 bds) : blockWidth(1 << LBW), blockHeight(1 << LBH), bounds(bds)
49 {
50 if(!bounds.width() || !bounds.height() || !LBW || !LBH)
51 throw std::runtime_error("invalid window for sparse canvas");
52 grid = bounds.scaleDownPow2(LBW, LBH);
53 }
54 SparseCanvas(uint32_t width, uint32_t height) : SparseCanvas(Rect32(0, 0, width, height)) {}
56 {
57 for(auto& pair : blocks)
58 delete pair.second;
59 }
60 bool read(uint8_t resno, Rect32 window, T* dest, const uint32_t destChunkY,
61 const uint32_t destChunkX)
62 {
63 return readWrite(resno, window, dest, destChunkY, destChunkX, true);
64 }
65 bool write(uint8_t resno, Rect32 window, const T* src, const uint32_t srcChunkY,
66 const uint32_t srcChunkX)
67 {
68 return readWrite(resno, window, (T*)src, srcChunkY, srcChunkX, false);
69 }
70 bool alloc(Rect32 win, bool zeroOutBuffer)
71 {
73 return true;
74 uint32_t blockWinHeight = 0;
75 uint32_t gridY = win.y0 >> LBH;
76 for(uint32_t y = win.y0; y < win.y1; gridY++, y += blockWinHeight)
77 {
78 blockWinHeight = (y == win.y0) ? blockHeight - (win.y0 & (blockHeight - 1)) : blockHeight;
79 blockWinHeight = (std::min<uint32_t>)(blockWinHeight, win.y1 - y);
80 uint32_t gridX = win.x0 >> LBW;
81 uint32_t blockWinWidth = 0;
82 for(uint32_t x = win.x0; x < win.x1; gridX++, x += blockWinWidth)
83 {
84 blockWinWidth = (x == win.x0) ? blockWidth - (win.x0 & (blockWidth - 1)) : blockWidth;
85 blockWinWidth = (std::min<uint32_t>)(blockWinWidth, win.x1 - x);
86 if(!grid.contains(gridX, gridY))
87 {
88 grklog.warn("sparse canvas : attempt to allocate a block (%u,%u) outside block "
89 "grid bounds (%u,%u,%u,%u)",
90 gridX, gridY, grid.x0, grid.y0, grid.x1, grid.y1);
91 return false;
92 }
93 uint64_t blockInd = (uint64_t)(gridY - grid.y0) * grid.width() + (gridX - grid.x0);
94 if(blocks.find(blockInd) == blocks.end())
95 {
96 auto b = new SparseBlock<T>();
97 b->alloc(blockWidth * blockHeight, zeroOutBuffer);
98 assert(grid.contains(gridX, gridY));
99 assert(b->data);
100 blocks[blockInd] = b;
101 }
102 }
103 }
104 return true;
105 }
106
107private:
108 inline SparseBlock<T>* getBlock(uint32_t block_x, uint32_t block_y)
109 {
110 uint64_t index = (uint64_t)(block_y - grid.y0) * grid.width() + (block_x - grid.x0);
111 auto it = blocks.find(index);
112 return it != blocks.end() ? it->second : nullptr;
113 }
115 {
116 return !(win.x0 >= bounds.x1 || win.x1 <= win.x0 || win.x1 > bounds.x1 || win.y0 >= bounds.y1 ||
117 win.y1 <= win.y0 || win.y1 > bounds.y1);
118 }
119 bool readWrite(uint8_t resno, Rect32 win, T* buf, const uint32_t spacingX,
120 const uint32_t spacingY, bool isReadOperation)
121 {
122 if(!win.valid())
123 return false;
124 assert(!isReadOperation || buf);
125
126 if(!isWindowValid(win))
127 {
128 grklog.warn("Sparse canvas @ res %u, attempt to read/write invalid window (%u,%u,%u,%u) "
129 "for bounds (%u,%u,%u,%u).",
130 resno, win.x0, win.y0, win.x1, win.y1, bounds.x0, bounds.y0, bounds.x1,
131 bounds.y1);
132 return false;
133 }
134 assert(spacingY != 0 || win.height() == 1);
135 assert((spacingY <= 1 && spacingX >= 1) || (spacingY >= 1 && spacingX == 1));
136
137 uint32_t gridY = win.y0 >> LBH;
138 uint32_t blockWinHeight = 0;
139 for(uint32_t y = win.y0; y < win.y1; gridY++, y += blockWinHeight)
140 {
141 blockWinHeight = (y == win.y0) ? blockHeight - (win.y0 & (blockHeight - 1)) : blockHeight;
142 uint32_t blockOffsetY = blockHeight - blockWinHeight;
143 blockWinHeight = (std::min<uint32_t>)(blockWinHeight, win.y1 - y);
144 uint32_t gridX = win.x0 >> LBW;
145 uint32_t blockWinWidth = 0;
146 for(uint32_t x = win.x0; x < win.x1; gridX++, x += blockWinWidth)
147 {
148 blockWinWidth = (x == win.x0) ? blockWidth - (win.x0 & (blockWidth - 1)) : blockWidth;
149 uint32_t blockOffsetX = blockWidth - blockWinWidth;
150 blockWinWidth = (std::min<uint32_t>)(blockWinWidth, win.x1 - x);
151 if(!grid.contains(gridX, gridY))
152 {
153 grklog.warn("sparse canvas @ resno %u, Attempt to access a block (%u,%u) outside "
154 "block grid bounds",
155 resno, gridX, gridY);
156 return false;
157 }
158 auto srcBlock = getBlock(gridX, gridY);
159 if(!srcBlock)
160 {
161 grklog.warn("sparse canvas @ resno %u, %s op: missing block (%u,%u,%u,%u) for %s "
162 "(%u,%u,%u,%u). Skipping.",
163 resno, isReadOperation ? "read" : "write", bounds.x0 + gridX * blockWidth,
164 bounds.y0 + gridY * blockHeight, bounds.x0 + (gridX + 1) * blockWidth,
165 bounds.y0 + (gridY + 1) * blockHeight, isReadOperation ? "read" : "write",
166 win.x0, win.y0, win.x1, win.y1);
167 continue;
168 }
169 if(isReadOperation)
170 {
171 auto src = srcBlock->data + ((uint64_t)blockOffsetY << LBW) + blockOffsetX;
172 auto dest = buf + (y - win.y0) * spacingY + (x - win.x0) * spacingX;
173 for(uint32_t blockY = 0; blockY < blockWinHeight; blockY++)
174 {
175 uint64_t destInd = 0;
176 for(uint32_t blockX = 0; blockX < blockWinWidth; blockX++)
177 {
178#ifdef GRK_DEBUG_VALGRIND
179 size_t val = grk_memcheck<int32_t>(src + blockX, 1);
180 if(val != grk_mem_ok)
181 grklog.error("sparse canvas @resno %u, read block(%u,%u) : "
182 "uninitialized at location (%u,%u)",
183 resno, gridX, gridY, x + blockX, y_);
184#endif
185 dest[destInd] = src[blockX];
186 destInd += spacingX;
187 }
188 dest += spacingY;
189 src += blockWidth;
190 }
191 }
192 else
193 {
194 const T* src = nullptr;
195 if(buf)
196 src = buf + (y - win.y0) * spacingY + (x - win.x0) * spacingX;
197 auto dest = srcBlock->data + ((uint64_t)blockOffsetY << LBW) + blockOffsetX;
198 for(uint32_t blockY = 0; blockY < blockWinHeight; blockY++)
199 {
200 uint64_t srcInd = 0;
201 for(uint32_t blockX = 0; blockX < blockWinWidth; blockX++)
202 {
203#ifdef GRK_DEBUG_VALGRIND
204 if(src)
205 {
206 Point32 pt((uint32_t)(x + blockX), y_);
207 size_t val = grk_memcheck<int32_t>(src + srcInd, 1);
208 if(val != grk_mem_ok)
209 grklog.error("sparse canvas @ resno %u, write block(%u,%u): "
210 "uninitialized at location (%u,%u)",
211 resno, gridX, gridY, x + blockX, y_);
212 }
213#endif
214 dest[blockX] = src ? src[srcInd] : 0;
215 srcInd += spacingX;
216 }
217 if(src)
218 src += spacingY;
219 dest += blockWidth;
220 }
221 }
222 }
223 }
224 return true;
225 }
226
227private:
228 const uint32_t blockWidth;
229 const uint32_t blockHeight;
230 std::unordered_map<uint64_t, SparseBlock<T>*> blocks;
231 Rect32 bounds; // canvas bounds
232 Rect32 grid; // block grid bounds
233};
234
235} // namespace grk
Definition ISparseCanvas.h:42
bool readWrite(uint8_t resno, Rect32 win, T *buf, const uint32_t spacingX, const uint32_t spacingY, bool isReadOperation)
Definition SparseCanvas.h:119
SparseBlock< T > * getBlock(uint32_t block_x, uint32_t block_y)
Definition SparseCanvas.h:108
const uint32_t blockWidth
Definition SparseCanvas.h:228
SparseCanvas(Rect32 bds)
Definition SparseCanvas.h:48
bool isWindowValid(Rect32 win)
Definition SparseCanvas.h:114
Rect32 bounds
Definition SparseCanvas.h:231
Rect32 grid
Definition SparseCanvas.h:232
const uint32_t blockHeight
Definition SparseCanvas.h:229
std::unordered_map< uint64_t, SparseBlock< T > * > blocks
Definition SparseCanvas.h:230
bool alloc(Rect32 win, bool zeroOutBuffer)
Definition SparseCanvas.h:70
SparseCanvas(uint32_t width, uint32_t height)
Definition SparseCanvas.h:54
bool read(uint8_t resno, Rect32 window, T *dest, const uint32_t destChunkY, const uint32_t destChunkX)
Read window of data into dest buffer.
Definition SparseCanvas.h:60
~SparseCanvas()
Definition SparseCanvas.h:55
bool write(uint8_t resno, Rect32 window, const T *src, const uint32_t srcChunkY, const uint32_t srcChunkX)
Write window of data from src buffer.
Definition SparseCanvas.h:65
ResWindow.
Definition CompressedChunkCache.h:36
ILogger & grklog
Definition Logger.cpp:24
Rect< uint32_t > Rect32
Definition geometry.h:64
Point< uint32_t > Point32
Definition geometry.h:42
bool valid(void) const
Definition geometry.h:246
T x1
Definition geometry.h:192
T height() const
Definition geometry.h:415
T x0
Definition geometry.h:192
T y0
Definition geometry.h:192
T y1
Definition geometry.h:192
Definition ISparseCanvas.h:60