9 float decode_half(uint16_t
h)
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;
23 while ((m & 0x400U) == 0) {
28 bits = sign | ((113U - e) << 23) | (m << 13);
30 }
else if (exp == 0x1FU) {
31 bits = sign | 0x7F800000U | (mant << 13);
33 bits = sign | ((exp - 15U + 127U) << 23) | (mant << 13);
37 std::memcpy(&
out, &bits,
sizeof(
out));
41 uint16_t encode_half(
float f)
44 std::memcpy(&bits, &f,
sizeof(bits));
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;
51 return static_cast<uint16_t
>(sign | 0x7C00U);
57 return static_cast<uint16_t
>(sign | (mant >>
static_cast<uint32_t
>(14 - exp)));
60 return static_cast<uint16_t
>(
61 sign | (
static_cast<uint32_t
>(exp) << 10) | (mant >> 13));
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)
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;
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);
138 if (
a.size() !=
b.size()) {
158 return std::vector<uint16_t>(element_count, 0U);
160 return std::vector<float>(element_count, 0.0F);
162 return std::vector<uint8_t>(element_count, 0U);
168 const std::optional<DataDimension::ValueRange>& range,
172 [&](
const auto& vec) ->
double {
173 using T =
typename std::decay_t<
decltype(vec)>::value_type;
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>) {
179 if (elem_index >= vec.size())
183 double type_max = 1.0;
185 if constexpr (std::is_same_v<T, uint8_t>) {
186 raw =
static_cast<double>(vec[elem_index]);
188 }
else if constexpr (std::is_same_v<T, uint16_t>) {
190 raw =
static_cast<double>(decode_half(vec[elem_index]));
192 raw =
static_cast<double>(vec[elem_index]);
196 raw =
static_cast<double>(vec[elem_index]);
200 return raw / type_max;
202 if (range->invalid && raw == *range->invalid)
203 return std::numeric_limits<double>::quiet_NaN();
205 const double span = range->max - range->min;
209 return std::clamp((raw - range->min) / span, 0.0, 1.0);
224 const std::optional<DataDimension::ValueRange>& range,
230 using T =
typename std::decay_t<
decltype(vec)>::value_type;
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>) {
236 if (elem_index >= vec.size())
242 if (std::isnan(
value)) {
245 raw = *range->invalid;
248 + std::clamp(
value, 0.0, 1.0) * (range->max - range->min);
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>) {
255 : std::clamp(
value * 65535.0, 0.0, 65535.0);
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>) {
264 vec[elem_index] = encode_half(
static_cast<float>(raw));
266 vec[elem_index] =
static_cast<uint16_t
>(std::clamp(raw, 0.0, 65535.0));
269 vec[elem_index] =
static_cast<float>(raw);
277 size_t elem_index,
double value)
std::vector< float > * out
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.
@ 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.
@ BGRA8_SRGB
8-bit BGRA 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.
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.