MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PixelStorage.cpp
Go to the documentation of this file.
1#include "PixelStorage.hpp"
2
4
5namespace MayaFlux::Kakshya {
6
7namespace {
8
9 float decode_half(uint16_t h)
10 {
11 const uint32_t sign = static_cast<uint32_t>(h & 0x8000U) << 16;
12 const uint32_t exp = (h >> 10) & 0x1FU;
13 const uint32_t mant = h & 0x3FFU;
14
15 uint32_t bits {};
16
17 if (exp == 0) {
18 if (mant == 0) {
19 bits = sign;
20 } else {
21 uint32_t e = 0;
22 uint32_t m = mant;
23 while ((m & 0x400U) == 0) {
24 m <<= 1;
25 ++e;
26 }
27 m &= 0x3FFU;
28 bits = sign | ((113U - e) << 23) | (m << 13);
29 }
30 } else if (exp == 0x1FU) {
31 bits = sign | 0x7F800000U | (mant << 13);
32 } else {
33 bits = sign | ((exp - 15U + 127U) << 23) | (mant << 13);
34 }
35
36 float out {};
37 std::memcpy(&out, &bits, sizeof(out));
38 return out;
39 }
40
41 uint16_t encode_half(float f)
42 {
43 uint32_t bits {};
44 std::memcpy(&bits, &f, sizeof(bits));
45
46 const auto sign = static_cast<uint16_t>((bits >> 16) & 0x8000U);
47 const int32_t exp = static_cast<int32_t>((bits >> 23) & 0xFFU) - 127 + 15;
48 uint32_t mant = bits & 0x7FFFFFU;
49
50 if (exp >= 0x1F)
51 return static_cast<uint16_t>(sign | 0x7C00U);
52
53 if (exp <= 0) {
54 if (exp < -10)
55 return sign;
56 mant |= 0x800000U;
57 return static_cast<uint16_t>(sign | (mant >> static_cast<uint32_t>(14 - exp)));
58 }
59
60 return static_cast<uint16_t>(
61 sign | (static_cast<uint32_t>(exp) << 10) | (mant >> 13));
62 }
63
64} // namespace
65
67{
68 switch (format) {
69 case ImageFormat::R8:
78 return 1;
79
87 return 2;
88
93 return 4;
94
95 default:
96 return 1;
97 }
98}
99
101{
102 switch (format) {
110 return true;
111 default:
112 return false;
113 }
114}
115
116namespace {
117
118 template <typename T>
119 void blend_typed(std::span<const uint8_t> a, std::span<const uint8_t> b, uint8_t* out, double weight)
120 {
121 const auto* from = reinterpret_cast<const T*>(a.data());
122 const auto* to = reinterpret_cast<const T*>(b.data());
123 auto* dest = reinterpret_cast<T*>(out);
124 const size_t count = a.size() / sizeof(T);
125 const double rounding = std::is_integral_v<T> ? 0.5 : 0.0;
126
127 for (size_t i = 0; i < count; ++i) {
128 const auto x = static_cast<double>(from[i]);
129 dest[i] = static_cast<T>(x + (static_cast<double>(to[i]) - x) * weight + rounding);
130 }
131 }
132
133}
134
135bool blend_frames(ImageFormat format, std::span<const uint8_t> a, std::span<const uint8_t> b,
136 uint8_t* out, double weight)
137{
138 if (a.size() != b.size()) {
139 return false;
140 }
141
142 switch (storage_element_size(format)) {
143 case 1:
144 blend_typed<uint8_t>(a, b, out, weight);
145 return true;
146 case 4:
147 blend_typed<float>(a, b, out, weight);
148 return true;
149 default:
150 return false;
151 }
152}
153
155{
156 switch (storage_element_size(format)) {
157 case 2:
158 return std::vector<uint16_t>(element_count, 0U);
159 case 4:
160 return std::vector<float>(element_count, 0.0F);
161 default:
162 return std::vector<uint8_t>(element_count, 0U);
163 }
164}
165
168 const std::optional<DataDimension::ValueRange>& range,
169 size_t elem_index)
170{
171 return std::visit(
172 [&](const auto& vec) -> double {
173 using T = typename std::decay_t<decltype(vec)>::value_type;
174
175 if constexpr (std::is_same_v<T, uint8_t>
176 || std::is_same_v<T, uint16_t>
177 || std::is_same_v<T, float>) {
178
179 if (elem_index >= vec.size())
180 return 0.0;
181
182 double raw {};
183 double type_max = 1.0;
184
185 if constexpr (std::is_same_v<T, uint8_t>) {
186 raw = static_cast<double>(vec[elem_index]);
187 type_max = 255.0;
188 } else if constexpr (std::is_same_v<T, uint16_t>) {
189 if (is_float_format(format)) {
190 raw = static_cast<double>(decode_half(vec[elem_index]));
191 } else {
192 raw = static_cast<double>(vec[elem_index]);
193 type_max = 65535.0;
194 }
195 } else {
196 raw = static_cast<double>(vec[elem_index]);
197 }
198
199 if (!range)
200 return raw / type_max;
201
202 if (range->invalid && raw == *range->invalid)
203 return std::numeric_limits<double>::quiet_NaN();
204
205 const double span = range->max - range->min;
206 if (span <= 0.0)
207 return 0.0;
208
209 return std::clamp((raw - range->min) / span, 0.0, 1.0);
210 } else {
211 return 0.0;
212 }
213 },
214 v);
215}
216
217double read_normalized_at(const DataVariant& v, ImageFormat format, size_t elem_index)
218{
219 return read_normalized_at(v, format, std::nullopt, elem_index);
220}
221
224 const std::optional<DataDimension::ValueRange>& range,
225 size_t elem_index,
226 double value)
227{
228 std::visit(
229 [&](auto& vec) {
230 using T = typename std::decay_t<decltype(vec)>::value_type;
231
232 if constexpr (std::is_same_v<T, uint8_t>
233 || std::is_same_v<T, uint16_t>
234 || std::is_same_v<T, float>) {
235
236 if (elem_index >= vec.size())
237 return;
238
239 double raw {};
240
241 if (range) {
242 if (std::isnan(value)) {
243 if (!range->invalid)
244 return;
245 raw = *range->invalid;
246 } else {
247 raw = range->min
248 + std::clamp(value, 0.0, 1.0) * (range->max - range->min);
249 }
250 } else if constexpr (std::is_same_v<T, uint8_t>) {
251 raw = std::clamp(value * 255.0, 0.0, 255.0);
252 } else if constexpr (std::is_same_v<T, uint16_t>) {
254 ? value
255 : std::clamp(value * 65535.0, 0.0, 65535.0);
256 } else {
257 raw = value;
258 }
259
260 if constexpr (std::is_same_v<T, uint8_t>) {
261 vec[elem_index] = static_cast<uint8_t>(std::clamp(raw, 0.0, 255.0));
262 } else if constexpr (std::is_same_v<T, uint16_t>) {
263 if (is_float_format(format)) {
264 vec[elem_index] = encode_half(static_cast<float>(raw));
265 } else {
266 vec[elem_index] = static_cast<uint16_t>(std::clamp(raw, 0.0, 65535.0));
267 }
268 } else {
269 vec[elem_index] = static_cast<float>(raw);
270 }
271 }
272 },
273 v);
274}
275
277 size_t elem_index, double value)
278{
279 write_normalized_at(v, format, std::nullopt, elem_index, value);
280}
281
289
291{
292 switch (format) {
297 return true;
298 default:
299 return false;
300 }
301}
302
303} // namespace MayaFlux::Kakshya
bool format
uint32_t h
Definition InkPress.cpp:29
size_t a
double weight
size_t b
std::vector< float > * out
glm::vec3 value
size_t count
uint32_t channels
static size_t get_bytes_per_pixel(ImageFormat format)
Get bytes per pixel for a format.
static uint32_t get_channel_count(ImageFormat format)
Get the number of color channels for a given format.
bool is_depth_format(ImageFormat format)
True when the format's values are range measurements rather than colour.
ImageFormat
User-friendly image format enum.
Definition ImageData.hpp:12
@ DEPTH24_STENCIL8
24-bit depth + 8-bit stencil
@ RGB8
Three channel 8-bit.
@ DEPTH32F
32-bit float depth
@ RG16
Two channel 16-bit unsigned integer.
@ BGRA8
8-bit BGRA unsigned normalized
@ RGBA16
Four channel 16-bit unsigned integer.
@ RGBA32F
Four channel 32-bit float.
@ R16F
Single channel 16-bit float.
@ R16
Single channel 16-bit unsigned integer.
@ RGBA16F
Four channel 16-bit float.
@ RGBA8
Four channel 8-bit.
@ RG32F
Two channel 32-bit float.
@ R32F
Single channel 32-bit float.
@ R8
Single channel 8-bit.
@ RG16F
Two channel 16-bit float.
@ RGBA8_SRGB
Four channel 8-bit sRGB.
DataVariant make_empty_storage(ImageFormat format, size_t element_count)
Allocate a zeroed DataVariant of the alternative backing a format.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:70
size_t storage_element_size(ImageFormat format)
Byte width of the DataVariant element backing a given ImageFormat.
bool blend_frames(ImageFormat format, std::span< const uint8_t > a, std::span< const uint8_t > b, uint8_t *out, double weight)
Linearly blend two frames of the same size and format.
double read_normalized_at(const DataVariant &v, ImageFormat format, const std::optional< DataDimension::ValueRange > &range, size_t elem_index)
Read one element as a normalised double, with optional range remapping.
bool is_float_format(ImageFormat format)
True when the format's numeric interpretation is floating point.
void write_normalized_at(DataVariant &v, ImageFormat format, const std::optional< DataDimension::ValueRange > &range, size_t elem_index, double value)
Write one element from a normalised double, with optional range remapping.
bool format_has_variant_storage(ImageFormat format)
True when a format decomposes into whole DataVariant elements.