summaryrefslogtreecommitdiff
path: root/kms++util/src/conv-common.h
blob: 837668fe111030931fdc64373f32b6c50d1b3978 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#pragma once

#include <algorithm>
#include <array>

#define MDSPAN_IMPL_STANDARD_NAMESPACE md
#define MDSPAN_IMPL_PROPOSED_NAMESPACE exp

#include <mdspan/mdspan.hpp>

namespace kms
{

/*
 * Helpers
 */

template<class T>
constexpr auto make_strided_view(T* data, size_t rows, size_t cols, size_t row_stride_bytes)
{
	assert(row_stride_bytes % sizeof(T) == 0 && "Row stride must be aligned to element size");

	size_t row_stride = row_stride_bytes / sizeof(T);
	std::array<size_t, 2> strides = { row_stride, 1 };

	auto layout = md::layout_stride::mapping(md::dextents<size_t, 2>(rows, cols), strides);

	return md::mdspan<T, md::dextents<size_t, 2>, md::layout_stride>(data, layout);
}

template<class T>
constexpr auto make_strided_fb_view(void* data, size_t rows, size_t cols, size_t row_stride_bytes)
{
	return make_strided_view(reinterpret_cast<T*>(data), rows, cols, row_stride_bytes);
}

// Helper for static loop unrolling
template<size_t Begin, size_t End, typename F>
constexpr void static_for(F&& f)
{
	[&]<size_t... I>(std::index_sequence<I...>) {
		(f(std::integral_constant<size_t, Begin + I>{}), ...);
	}(std::make_index_sequence<End - Begin>{});
}

template<typename T, typename TElement>
concept HasIndexOperatorReturning = requires(T t) {
	{ t[0] } -> std::convertible_to<const TElement&>;
};

template<typename T, typename TElement>
concept Is2Dspan = std::convertible_to<T, md::mdspan<TElement, md::dextents<size_t, 2>>>;

/*
 * Packing and Layouts
 */

/* This type must be big enough to hold any of the components' value */
using component_storage_type = uint16_t;

enum class ComponentType {
	X, A,
	R, G, B,
	Y, Cb, Cr,
	Y0, Y1, Y2,
	Cb0, Cb1, Cb2,
	Cr0, Cr1, Cr2,
};

// Describes a single component's bit layout
template<ComponentType Type, size_t BitSize, size_t BitOffset>
struct ComponentLayout {
	static constexpr size_t size = BitSize;
	static constexpr size_t offset = BitOffset;
	static constexpr ComponentType type = Type;

	static_assert(sizeof(component_storage_type) * 8 >= size);

	static constexpr auto get_mask() { return ((1ull << BitSize) - 1) << BitOffset; }

	template<typename TStorage>
	static constexpr TStorage pack_value(TStorage value)
	{
		return (value & ((1ull << BitSize) - 1)) << BitOffset;
	}

	template<typename TStorage>
	static constexpr component_storage_type unpack_value(TStorage value)
	{
		return (value >> BitOffset) & ((1ull << BitSize) - 1);
	}
};

// Describes N components packed into a storage type
template<typename TStorage, typename... Components>
struct PlaneLayout {
	using components_tuple = std::tuple<Components...>;

	static constexpr size_t num_components = sizeof...(Components);

	static constexpr size_t total_bits = (Components::size + ...);
	static constexpr size_t storage_bits = sizeof(TStorage) * 8;
	static_assert(total_bits <= storage_bits, "Components don't fit in storage type");

	template<size_t N>
	static constexpr size_t component_size = std::tuple_element_t<N, components_tuple>::size;

	using storage_type = TStorage;

	static constexpr std::array<ComponentType, sizeof...(Components)> order = {
		Components::type...
	};

	template<ComponentType C>
	static constexpr size_t component_count()
	{
		return std::ranges::count(order, C);
	}

	template<ComponentType C>
	static constexpr size_t find_pos()
	{
		return std::ranges::find(order, C) - order.begin();
	}

	template<ComponentType C>
	static constexpr size_t find_nth_pos(size_t n = 0)
	{
		auto it = std::ranges::find_if(
			order, [&n](ComponentType type) mutable {
				return type == C && n-- == 0;
			});
		return it - order.begin();
	}

	// Pack values into storage type (separate parameters version)
	template<typename... Values>
	static constexpr TStorage pack(Values... values)
	{
		static_assert(sizeof...(values) == num_components,
			      "Number of values must match number of components");
		return (Components::template pack_value<TStorage>(values) | ...);
	}

	// Pack values into storage type (std:array version)
	template<typename T, size_t N>
	static constexpr TStorage pack(const std::array<T, N>& values)
	{
		static_assert(N == num_components,
			      "Number of values must match number of components");
		return [&]<size_t... I>(std::index_sequence<I...>) {
			return (Components::template pack_value<TStorage>(values[I]) | ...);
		}(std::make_index_sequence<N>{});
	}

	static constexpr std::array<component_storage_type, num_components>
	unpack(TStorage value)
	{
		return [&]<size_t... I>(std::index_sequence<I...>) {
			return std::array{
				std::tuple_element_t<I, components_tuple>::template unpack_value<
					TStorage>(value)...
			};
		}(std::make_index_sequence<num_components>{});
	}
};

template<typename... Planes>
class FormatLayout
{
public:
	static constexpr size_t num_planes = sizeof...(Planes);

	template<size_t N> using plane = std::tuple_element_t<N, std::tuple<Planes...>>;
};

} // namespace kms