summaryrefslogtreecommitdiff
path: root/kms++util/src/conv-yuv-semiplanar.h
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2025-02-02 11:26:23 +0200
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2025-03-26 15:44:00 +0200
commitf758e324e17b52116075bb9175a3dd03d223a424 (patch)
treeb18fba90e2b8498f8228f3eff9bc60adf3153cf2 /kms++util/src/conv-yuv-semiplanar.h
parent6c49fe5b811464f59e3a31b869734071da0ec7c1 (diff)
kms++util: New template based conversion & testpat code
New templated conversion and test pattern code. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Diffstat (limited to 'kms++util/src/conv-yuv-semiplanar.h')
-rw-r--r--kms++util/src/conv-yuv-semiplanar.h214
1 files changed, 214 insertions, 0 deletions
diff --git a/kms++util/src/conv-yuv-semiplanar.h b/kms++util/src/conv-yuv-semiplanar.h
new file mode 100644
index 0000000..fa7e0cb
--- /dev/null
+++ b/kms++util/src/conv-yuv-semiplanar.h
@@ -0,0 +1,214 @@
+#pragma once
+
+#include <vector>
+
+#include <kms++/framebuffer.h>
+#include <kms++util/color16.h>
+
+#include "conv-common.h"
+
+namespace kms
+{
+
+/* Semiplanar YUV */
+
+template<ComponentType C0, ComponentType C1, size_t HSub, size_t VSub>
+struct NV12_Family_Layout
+ : public FormatLayout<
+ PlaneLayout<uint8_t,
+ ComponentLayout<ComponentType::Y, 8, 0>
+ >,
+ PlaneLayout<uint16_t,
+ ComponentLayout<C0, 8, 0>,
+ ComponentLayout<C1, 8, 8>
+ >
+ >
+{
+ static constexpr size_t h_sub = HSub;
+ static constexpr size_t v_sub = VSub;
+};
+
+using NV12_Layout = NV12_Family_Layout<ComponentType::Cb, ComponentType::Cr, 2, 2>;
+using NV21_Layout = NV12_Family_Layout<ComponentType::Cr, ComponentType::Cb, 2, 2>;
+
+using NV16_Layout = NV12_Family_Layout<ComponentType::Cb, ComponentType::Cr, 2, 1>;
+using NV61_Layout = NV12_Family_Layout<ComponentType::Cr, ComponentType::Cb, 2, 1>;
+
+template<size_t HSub, size_t VSub>
+struct XV15_Family_Layout
+ : public FormatLayout<
+ PlaneLayout<uint32_t,
+ ComponentLayout<ComponentType::Y, 10, 0>,
+ ComponentLayout<ComponentType::Y, 10, 10>,
+ ComponentLayout<ComponentType::Y, 10, 20>
+ >,
+ PlaneLayout<uint64_t,
+ ComponentLayout<ComponentType::Cb, 10, 0>,
+ ComponentLayout<ComponentType::Cr, 10, 10>,
+ ComponentLayout<ComponentType::Cb, 10, 20>,
+ ComponentLayout<ComponentType::Cr, 10, 32>,
+ ComponentLayout<ComponentType::Cb, 10, 42>,
+ ComponentLayout<ComponentType::Cr, 10, 52>
+ >
+ >
+{
+ static constexpr size_t h_sub = HSub;
+ static constexpr size_t v_sub = VSub;
+};
+
+using XV15_Layout = XV15_Family_Layout<2, 2>;
+using XV20_Layout = XV15_Family_Layout<2, 1>;
+
+template<size_t HSub, size_t VSub>
+struct SubsampleHelper {
+ template<typename View> static constexpr auto subsample(const View& view, size_t group_idx)
+ {
+ uint32_t sum = 0;
+
+ static_for<0, HSub>([&](auto x) { sum += view(0, group_idx + x); });
+
+ if constexpr (VSub > 1) {
+ static_for<1, VSub>([&](auto y) {
+ static_for<0, HSub>([&](auto x) { sum += view(y, group_idx + x); });
+ });
+ }
+
+ return sum / (HSub * VSub);
+ }
+};
+
+template<typename Layout>
+class YUVSemiPlanarWriter
+{
+ static_assert(Layout::num_planes == 2);
+
+ static constexpr size_t h_sub = Layout::h_sub;
+ static constexpr size_t v_sub = Layout::v_sub;
+
+ using YLayout = Layout::template plane<0>;
+ using UVLayout = Layout::template plane<1>;
+
+ using TY = YLayout::storage_type;
+ using TCrCb = UVLayout::storage_type;
+
+ static constexpr size_t pixels_in_group = YLayout::template component_count<ComponentType::Y>();
+ static_assert(pixels_in_group == UVLayout::template component_count<ComponentType::Cb>());
+ static_assert(pixels_in_group == UVLayout::template component_count<ComponentType::Cr>());
+
+public:
+ static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y,
+ auto&& generate_line)
+ {
+ assert(start_y % v_sub == 0);
+ assert((end_y + 1) % v_sub == 0);
+ if (fb.width() % pixels_in_group != 0)
+ throw std::invalid_argument("FB width doesn't align to pixel format");
+
+ // Line buffers
+ std::vector<YUV16> linebuf_storage(fb.width() * v_sub);
+ auto linebuf = md::mdspan(linebuf_storage.data(), v_sub, fb.width());
+
+ // Views to the planes
+ auto y_view = make_strided_fb_view<TY>(fb.map(0), fb.height(),
+ fb.width() / pixels_in_group, fb.stride(0));
+
+ auto uv_view = make_strided_fb_view<TCrCb>(fb.map(1), fb.height() / v_sub,
+ fb.width() / pixels_in_group / h_sub,
+ fb.stride(1));
+
+ for (size_t y_src = start_y; y_src <= end_y; y_src++) {
+ size_t y_offset = y_src % v_sub;
+
+ if (y_offset == 0) {
+ // Fill line buffers
+ for (size_t y_offset = 0; y_offset < v_sub; y_offset++) {
+ auto line = md::submdspan(linebuf, y_offset, md::full_extent);
+ std::span<YUV16> span(line.data_handle(), line.size());
+ generate_line(y_src + y_offset, span);
+ }
+ }
+
+ // Write Y values from the line buffer
+ write_y_samples(md::submdspan(y_view, y_src, md::full_extent),
+ md::submdspan(linebuf, y_offset, md::full_extent));
+
+ if (y_offset == 0) {
+ // Write UV values from the line buffers
+ write_uv_samples(uv_view, linebuf, y_src);
+ }
+ }
+ }
+
+private:
+
+ template<typename YBuf>
+ static void write_y_samples(YBuf&& y_view, auto&& linebuf)
+ {
+ for (size_t x_src = 0; x_src < linebuf.extent(1); x_src += pixels_in_group) {
+ auto x_dst = x_src / pixels_in_group;
+
+ write_y_group(y_view, linebuf, x_src, x_dst,
+ std::make_index_sequence<pixels_in_group>{});
+ }
+ }
+
+ template<typename YBuf, size_t... I>
+ static void write_y_group(YBuf&& y_view, auto&& linebuf, size_t x_src,
+ size_t x_dst, std::index_sequence<I...>)
+ {
+ std::array<component_storage_type, YLayout::num_components> y_values{
+ static_cast<component_storage_type>((linebuf(x_src + I).y >> (16 - YLayout::template component_size<I>)))...
+ };
+
+ y_view(x_dst) = YLayout::pack(y_values);
+ }
+
+ template<typename UVBuf>
+ static void write_uv_samples(UVBuf& uv_view, auto& linebuf, size_t y_src)
+ {
+ for (size_t x_src = 0; x_src < linebuf.extent(1); x_src += pixels_in_group * h_sub) {
+ const size_t y_offset = 0;
+ auto y_dst = (y_src + y_offset) / v_sub;
+ auto x_dst = x_src / (pixels_in_group * h_sub);
+
+ auto group_view = md::submdspan(linebuf, std::tuple(y_offset, y_offset + v_sub),
+ std::tuple(x_src, x_src + h_sub * pixels_in_group));
+
+ write_uv_group(uv_view, group_view, y_dst, x_dst,
+ std::make_index_sequence<pixels_in_group>{});
+ }
+ }
+
+ template<typename UVBuf, size_t... I>
+ static void write_uv_group(UVBuf& uv_view, auto& group_view, size_t y_dst, size_t x_dst,
+ std::index_sequence<I...>)
+ {
+ std::array<component_storage_type, UVLayout::num_components> uv_values;
+
+ (
+ [&]<size_t i>() {
+ constexpr size_t group_idx = i * h_sub;
+
+ constexpr size_t u_idx = UVLayout::template find_nth_pos<ComponentType::Cb>(i);
+ constexpr size_t v_idx = UVLayout::template find_nth_pos<ComponentType::Cr>(i);
+
+ auto u = SubsampleHelper<h_sub, v_sub>::subsample(
+ [&group_view](size_t y, size_t x) { return group_view(y, x).u; },
+ group_idx);
+
+ auto v = SubsampleHelper<h_sub, v_sub>::subsample(
+ [&group_view](size_t y, size_t x) { return group_view(y, x).v; },
+ group_idx);
+
+ uv_values[u_idx] =
+ u >> (16 - UVLayout::template component_size<u_idx>);
+ uv_values[v_idx] =
+ v >> (16 - UVLayout::template component_size<v_idx>);
+ }.template operator()<I>(),
+ ...);
+
+ uv_view(y_dst, x_dst) = UVLayout::pack(uv_values);
+ }
+};
+
+} // namespace kms