From 549c347d6feb2e94a810a720c97a8bf0f57317a1 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 4 May 2026 16:19:04 +0300 Subject: kms++util: Replace test-pattern generator with pixpat Switch draw_test_pattern() to call libpixpat (linked statically into libkms++util.so) instead of the in-tree pattern generator. Pixpat covers every pattern (kmstest, smpte, solid colors) and every pixel format the previous generator handled, so behavior is unchanged for all callers. Drop the now-unused machinery: conv.h and conv-*.h template writers, color16.h (RGB16/YUV16 plus conversions), the *_old / _single / _multi declarations that had no definitions or callers, and the c_draw_test_pattern C ABI which had no callers anywhere in the tree. RecStandard and ColorRange move from color16.h directly into kms++util.h, since they are still part of the public TestPatternOptions struct. --- kms++util/inc/kms++util/color16.h | 184 ---------- kms++util/inc/kms++util/kms++util.h | 30 +- kms++util/meson.build | 9 +- kms++util/src/conv-common.h | 177 ---------- kms++util/src/conv-gray.h | 130 ------- kms++util/src/conv-raw-packed.h | 230 ------------- kms++util/src/conv-raw.h | 202 ----------- kms++util/src/conv-rgb.h | 299 ----------------- kms++util/src/conv-yuv-packed.h | 83 ----- kms++util/src/conv-yuv-planar-packed.h | 125 ------- kms++util/src/conv-yuv-planar.h | 214 ------------ kms++util/src/conv-yuv-semiplanar.h | 214 ------------ kms++util/src/conv-yuv.h | 147 -------- kms++util/src/conv.h | 11 - kms++util/src/testpat.cpp | 598 +++------------------------------ 15 files changed, 54 insertions(+), 2599 deletions(-) delete mode 100644 kms++util/inc/kms++util/color16.h delete mode 100644 kms++util/src/conv-common.h delete mode 100644 kms++util/src/conv-gray.h delete mode 100644 kms++util/src/conv-raw-packed.h delete mode 100644 kms++util/src/conv-raw.h delete mode 100644 kms++util/src/conv-rgb.h delete mode 100644 kms++util/src/conv-yuv-packed.h delete mode 100644 kms++util/src/conv-yuv-planar-packed.h delete mode 100644 kms++util/src/conv-yuv-planar.h delete mode 100644 kms++util/src/conv-yuv-semiplanar.h delete mode 100644 kms++util/src/conv-yuv.h delete mode 100644 kms++util/src/conv.h (limited to 'kms++util') diff --git a/kms++util/inc/kms++util/color16.h b/kms++util/inc/kms++util/color16.h deleted file mode 100644 index 340ce6a..0000000 --- a/kms++util/inc/kms++util/color16.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace kms -{ - -enum class ColorRange { Limited, Full }; - -enum class RecStandard { BT601, BT709, BT2020 }; - -class YUV16; -class RGB16; - -// Conversion coefficients for different standards -struct ConversionCoefficients { - double kr; - double kg; - double kb; - - static constexpr ConversionCoefficients get(RecStandard rec) noexcept - { - switch (rec) { - case RecStandard::BT601: - return { 0.299, 0.587, 0.114 }; - default: // Default to BT709 - case RecStandard::BT709: - return { 0.2126, 0.7152, 0.0722 }; - case RecStandard::BT2020: - return { 0.2627, 0.6780, 0.0593 }; - } - } -}; - -// Range scaling factors -struct RangeScaling { - double y_min; - double y_max; - double c_min; - double c_max; - - static constexpr RangeScaling get(ColorRange range) noexcept - { - switch (range) { - case ColorRange::Limited: - return { - 16.0 / 255.0, // y_min - 235.0 / 255.0, // y_max - 16.0 / 255.0, // c_min - 240.0 / 255.0 // c_max - }; - case ColorRange::Full: - default: - return { 0.0, 1.0, 0.0, 1.0 }; - } - } -}; - -class RGB16 -{ -public: - uint16_t r = 0; - uint16_t g = 0; - uint16_t b = 0; - uint16_t a = 0; - - constexpr RGB16() noexcept = default; - constexpr RGB16(uint16_t r, uint16_t g, uint16_t b) noexcept : r(r), g(g), b(b), a(max_value) {} - constexpr RGB16(uint16_t r, uint16_t g, uint16_t b, uint16_t a) noexcept : r(r), g(g), b(b), a(a) {} - - static constexpr RGB16 from_8(uint8_t r, uint8_t g, uint8_t b) - { - return RGB16 { - static_cast((r << 8) | r), - static_cast((g << 8) | g), - static_cast((b << 8) | b), - }; - } - - [[nodiscard]] - constexpr YUV16 to_yuv(RecStandard rec = RecStandard::BT709, - ColorRange range = ColorRange::Limited) const noexcept; - - static constexpr uint16_t max_value = 0xffff; -}; - -class YUV16 -{ -public: - uint16_t y = 0; - uint16_t u = 0; - uint16_t v = 0; - uint16_t a = 0; - - constexpr YUV16() noexcept = default; - constexpr YUV16(uint16_t y, uint16_t u, uint16_t v) noexcept : y(y), u(u), v(v), a(max_value) {} - constexpr YUV16(uint16_t y, uint16_t u, uint16_t v, uint16_t a) noexcept : y(y), u(u), v(v), a(a) {} - - [[nodiscard]] - constexpr RGB16 to_rgb(RecStandard rec = RecStandard::BT709, - ColorRange range = ColorRange::Limited) const noexcept; - - - static constexpr YUV16 from_10(uint16_t r, uint16_t g, uint16_t b) - { - return YUV16 { - static_cast((r << 6) | (r >> 4)), - static_cast((g << 6) | (g >> 4)), - static_cast((b << 6) | (b >> 4)), - }; - } - - static constexpr YUV16 from_12(uint16_t r, uint16_t g, uint16_t b) - { - return YUV16 { - static_cast((r << 4) | (r >> 8)), - static_cast((g << 4) | (g >> 8)), - static_cast((b << 4) | (b >> 8)), - }; - } - - static constexpr uint16_t max_value = 0xffff; -}; - -constexpr YUV16 RGB16::to_yuv(RecStandard rec, ColorRange range) const noexcept -{ - const auto coeff = ConversionCoefficients::get(rec); - const auto scaling = RangeScaling::get(range); - - // Normalize RGB values to [0,1] - const double r_norm = static_cast(r) / max_value; - const double g_norm = static_cast(g) / max_value; - const double b_norm = static_cast(b) / max_value; - - // Calculate Y (unscaled) - double y_unscaled = coeff.kr * r_norm + coeff.kg * g_norm + coeff.kb * b_norm; - - // Calculate U and V using unscaled Y - double u = (b_norm - y_unscaled) / (2.0 * (1.0 - coeff.kb)); - double v = (r_norm - y_unscaled) / (2.0 * (1.0 - coeff.kr)); - - // Scale Y to target range - double y = y_unscaled * (scaling.y_max - scaling.y_min) + scaling.y_min; - - // Scale U and V to target range - u = u * (scaling.c_max - scaling.c_min) + (scaling.c_max + scaling.c_min) / 2.0; - v = v * (scaling.c_max - scaling.c_min) + (scaling.c_max + scaling.c_min) / 2.0; - - // Convert back to 16-bit values - return YUV16(static_cast(std::round(y * max_value)), - static_cast(std::round(u * max_value)), - static_cast(std::round(v * max_value))); -} - -constexpr RGB16 YUV16::to_rgb(RecStandard rec, ColorRange range) const noexcept -{ - const auto coeff = ConversionCoefficients::get(rec); - const auto scaling = RangeScaling::get(range); - - // Normalize YUV values to [0,1] - double y_norm = static_cast(y) / max_value; - double u_norm = static_cast(u) / max_value; - double v_norm = static_cast(v) / max_value; - - // Rescale from target range to [0,1] - y_norm = (y_norm - scaling.y_min) / (scaling.y_max - scaling.y_min); - u_norm = (u_norm - (scaling.c_max + scaling.c_min) / 2.0) / (scaling.c_max - scaling.c_min); - v_norm = (v_norm - (scaling.c_max + scaling.c_min) / 2.0) / (scaling.c_max - scaling.c_min); - - // Convert to RGB - const double r = y_norm + 2.0 * v_norm * (1.0 - coeff.kr); - const double g = y_norm - 2.0 * u_norm * (1.0 - coeff.kb) * coeff.kb / coeff.kg - - 2.0 * v_norm * (1.0 - coeff.kr) * coeff.kr / coeff.kg; - const double b = y_norm + 2.0 * u_norm * (1.0 - coeff.kb); - - // Clamp and convert back to 16-bit values - return RGB16(static_cast(std::round(std::clamp(r, 0.0, 1.0) * max_value)), - static_cast(std::round(std::clamp(g, 0.0, 1.0) * max_value)), - static_cast(std::round(std::clamp(b, 0.0, 1.0) * max_value))); -} - -} // namespace kms diff --git a/kms++util/inc/kms++util/kms++util.h b/kms++util/inc/kms++util/kms++util.h index 396d18c..be06cab 100644 --- a/kms++util/inc/kms++util/kms++util.h +++ b/kms++util/inc/kms++util/kms++util.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -18,6 +17,9 @@ namespace kms { class IFramebuffer; +enum class ColorRange { Limited, Full }; +enum class RecStandard { BT601, BT709, BT2020 }; + void draw_rgb_pixel(IFramebuffer& buf, unsigned x, unsigned y, RGB color); void draw_yuv444_pixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv); void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2); @@ -29,10 +31,6 @@ void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const std::string& str void draw_color_bar(IFramebuffer& buf, int old_xpos, int xpos, int width); -void draw_test_pattern_old(IFramebuffer& fb, YUVType yuvt = YUVType::BT601_Lim); -void draw_test_pattern_single_old(IFramebuffer& fb, YUVType yuvt = YUVType::BT601_Lim); -void draw_test_pattern_multi_old(IFramebuffer& fb, YUVType yuvt = YUVType::BT601_Lim); - struct TestPatternOptions { std::string pattern; RecStandard rec = RecStandard::BT709; @@ -40,8 +38,6 @@ struct TestPatternOptions { }; void draw_test_pattern(IFramebuffer& fb, const TestPatternOptions& options = {}); -void draw_test_pattern_single(IFramebuffer& fb, const TestPatternOptions& options = {}); -void draw_test_pattern_multi(IFramebuffer& fb, const TestPatternOptions& options = {}); } // namespace kms #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -84,23 +80,3 @@ void draw_test_pattern_multi(IFramebuffer& fb, const TestPatternOptions& options fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ exit(-1); \ } - -extern "C" { - -struct CDrawTestPatternParameters { - uint32_t width; - uint32_t height; - const char* format_name; - uint8_t* buffers[4]; - uint32_t sizes[4]; - uint32_t pitches[4]; - uint32_t offsets[4]; - - const char* pattern; - uint32_t rec_standard; - bool full_range; -}; - -int c_draw_test_pattern(struct CDrawTestPatternParameters* params); - -} diff --git a/kms++util/meson.build b/kms++util/meson.build index e2beed6..dcdae8f 100644 --- a/kms++util/meson.build +++ b/kms++util/meson.build @@ -19,7 +19,6 @@ libkmsxxutil_sources = files([ public_headers = [ 'inc/kms++util/color.h', - 'inc/kms++util/color16.h', 'inc/kms++util/kms++util.h', 'inc/kms++util/stopwatch.h', 'inc/kms++util/cpuframebuffer.h', @@ -29,7 +28,7 @@ public_headers = [ 'inc/kms++util/resourcemanager.h', ] -private_includes = include_directories('src', 'inc', '../ext/mdspan/include') +private_includes = include_directories('src', 'inc') public_includes = include_directories('inc') thread_dep = dependency('threads', required : false) @@ -40,7 +39,11 @@ if thread_dep.found() libkmsxxutil_args += [ '-DHAS_PTHREAD' ] endif -libkmsxxutil_deps = [ libkmsxx_dep, thread_dep ] +pixpat_proj = subproject('pixpat', + default_options : ['config=pixpat-native/profiles/pattern_only.toml']) +libpixpat_dep = pixpat_proj.get_variable('libpixpat_static_dep') + +libkmsxxutil_deps = [ libkmsxx_dep, thread_dep, libpixpat_dep ] libkmsxxutil = library('kms++util', libkmsxxutil_sources, diff --git a/kms++util/src/conv-common.h b/kms++util/src/conv-common.h deleted file mode 100644 index 837668f..0000000 --- a/kms++util/src/conv-common.h +++ /dev/null @@ -1,177 +0,0 @@ -#pragma once - -#include -#include - -#define MDSPAN_IMPL_STANDARD_NAMESPACE md -#define MDSPAN_IMPL_PROPOSED_NAMESPACE exp - -#include - -namespace kms -{ - -/* - * Helpers - */ - -template -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 strides = { row_stride, 1 }; - - auto layout = md::layout_stride::mapping(md::dextents(rows, cols), strides); - - return md::mdspan, md::layout_stride>(data, layout); -} - -template -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(data), rows, cols, row_stride_bytes); -} - -// Helper for static loop unrolling -template -constexpr void static_for(F&& f) -{ - [&](std::index_sequence) { - (f(std::integral_constant{}), ...); - }(std::make_index_sequence{}); -} - -template -concept HasIndexOperatorReturning = requires(T t) { - { t[0] } -> std::convertible_to; -}; - -template -concept Is2Dspan = std::convertible_to>>; - -/* - * 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 -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 - static constexpr TStorage pack_value(TStorage value) - { - return (value & ((1ull << BitSize) - 1)) << BitOffset; - } - - template - static constexpr component_storage_type unpack_value(TStorage value) - { - return (value >> BitOffset) & ((1ull << BitSize) - 1); - } -}; - -// Describes N components packed into a storage type -template -struct PlaneLayout { - using components_tuple = std::tuple; - - 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 - static constexpr size_t component_size = std::tuple_element_t::size; - - using storage_type = TStorage; - - static constexpr std::array order = { - Components::type... - }; - - template - static constexpr size_t component_count() - { - return std::ranges::count(order, C); - } - - template - static constexpr size_t find_pos() - { - return std::ranges::find(order, C) - order.begin(); - } - - template - 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 - 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(values) | ...); - } - - // Pack values into storage type (std:array version) - template - static constexpr TStorage pack(const std::array& values) - { - static_assert(N == num_components, - "Number of values must match number of components"); - return [&](std::index_sequence) { - return (Components::template pack_value(values[I]) | ...); - }(std::make_index_sequence{}); - } - - static constexpr std::array - unpack(TStorage value) - { - return [&](std::index_sequence) { - return std::array{ - std::tuple_element_t::template unpack_value< - TStorage>(value)... - }; - }(std::make_index_sequence{}); - } -}; - -template -class FormatLayout -{ -public: - static constexpr size_t num_planes = sizeof...(Planes); - - template using plane = std::tuple_element_t>; -}; - -} // namespace kms diff --git a/kms++util/src/conv-gray.h b/kms++util/src/conv-gray.h deleted file mode 100644 index 7fead46..0000000 --- a/kms++util/src/conv-gray.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -/* - * Grayscale formats - */ - -using Y8_Layout = FormatLayout>>; - -using Y10_Layout = FormatLayout, - ComponentLayout>>; - -using Y12_Layout = FormatLayout, - ComponentLayout>>; - -using Y16_Layout = FormatLayout>>; - -using XYYY2101010_Layout = FormatLayout, // Y0 - ComponentLayout, // Y1 - ComponentLayout, // Y2 - ComponentLayout ->>; - -template class Y_Writer -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static_assert(Layout::num_planes == 1); - static_assert(Plane::template component_count() >= 1); - - static constexpr bool has_padding = - Plane::template component_count(); - - static constexpr size_t y_idx = Plane::template find_pos(); - static constexpr size_t x_idx = - has_padding ? Plane::template find_pos() : 0; - - static constexpr size_t y_shift = 16 - Plane::template component_size; - - static constexpr size_t pixels_in_group = - Plane::template component_count(); - static constexpr bool is_packed_format = pixels_in_group > 1; - -public: - // Pack and write num_pixels pixels from src_line to dst_line - static void pack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels) - { - if constexpr (!is_packed_format) { - // Simple case: one Y component per storage unit - for (size_t x = 0; x < num_pixels; x++) { - const YUV16& pix = src_line[x]; - - std::array - components; - - if constexpr (has_padding) - components[x_idx] = 0; - - components[y_idx] = pix.y >> y_shift; - - dst_line[x] = Plane::pack(components); - } - } else { - write_y_samples(dst_line, src_line, num_pixels); - } - } - - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - // View to the plane - auto view = make_strided_fb_view(fb.map(0), fb.height(), - fb.width() / pixels_in_group, - fb.stride(0)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - auto dst = md::submdspan(view, y_src, md::full_extent); - - pack_line(dst, linebuf, fb.width()); - } - } - -private: - template - static void write_y_samples(YBuf&& y_view, auto&& linebuf, size_t num_pixels) - { - for (size_t x_src = 0; x_src < num_pixels; 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{}); - } - } - - template - static void write_y_group(YBuf&& y_view, auto&& linebuf, size_t x_src, - size_t x_dst, std::index_sequence) - { - std::array y_values{ - static_cast( - (linebuf[x_src + I].y >> - (16 - Plane::template component_size)))... - }; - - y_view[x_dst] = Plane::pack(y_values); - } -}; - -} // namespace kms \ No newline at end of file diff --git a/kms++util/src/conv-raw-packed.h b/kms++util/src/conv-raw-packed.h deleted file mode 100644 index 7465c6b..0000000 --- a/kms++util/src/conv-raw-packed.h +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" -#include "conv-raw.h" - -namespace kms -{ - -/* - * Raw Bayer Packed formats (MIPI CSI-2) - */ - -template -struct BayerPacked_Layout; - -// 10-bit packed bayer formats - 4 pixels (40 bits) in 5 bytes -template -struct BayerPacked_Layout - : public FormatLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 10; - static constexpr size_t pixels_per_group = 4; - static constexpr size_t bytes_per_group = 5; -}; - -// 12-bit packed bayer formats - 2 pixels (24 bits) in 3 bytes -template -struct BayerPacked_Layout - : public FormatLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 12; - static constexpr size_t pixels_per_group = 2; - static constexpr size_t bytes_per_group = 3; -}; - -// Convenient aliases for different bit depths -template using BayerPacked10_Layout = BayerPacked_Layout; -template using BayerPacked12_Layout = BayerPacked_Layout; - -// Format-specific type aliases -using SRGGB10P_Layout = BayerPacked10_Layout; -using SGBRG10P_Layout = BayerPacked10_Layout; -using SGRBG10P_Layout = BayerPacked10_Layout; -using SBGGR10P_Layout = BayerPacked10_Layout; - -using SRGGB12P_Layout = BayerPacked12_Layout; -using SGBRG12P_Layout = BayerPacked12_Layout; -using SGRBG12P_Layout = BayerPacked12_Layout; -using SBGGR12P_Layout = BayerPacked12_Layout; - -template -class BayerPacked_Writer -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static_assert(Layout::num_planes == 1); - static_assert(std::is_same_v); - - static constexpr BayerOrder bayer_order = Layout::bayer_order; - static constexpr size_t bit_depth = Layout::bit_depth; - static constexpr size_t pixels_per_group = Layout::pixels_per_group; - static constexpr size_t bytes_per_group = Layout::bytes_per_group; - - static constexpr ComponentType get_bayer_component(size_t x, size_t y) - { - const bool x_even = (x % 2) == 0; - const bool y_even = (y % 2) == 0; - - switch (bayer_order) { - case BayerOrder::RGGB: - if (y_even && x_even) return ComponentType::R; - if (y_even && !x_even) return ComponentType::G; - if (!y_even && x_even) return ComponentType::G; - return ComponentType::B; - - case BayerOrder::BGGR: - if (y_even && x_even) return ComponentType::B; - if (y_even && !x_even) return ComponentType::G; - if (!y_even && x_even) return ComponentType::G; - return ComponentType::R; - - case BayerOrder::GRBG: - if (y_even && x_even) return ComponentType::G; - if (y_even && !x_even) return ComponentType::R; - if (!y_even && x_even) return ComponentType::B; - return ComponentType::G; - - case BayerOrder::GBRG: - if (y_even && x_even) return ComponentType::G; - if (y_even && !x_even) return ComponentType::B; - if (!y_even && x_even) return ComponentType::R; - return ComponentType::G; - } - - return ComponentType::Y; // fallback - } - - static uint16_t extract_component(const RGB16& pix, ComponentType component) - { - switch (component) { - case ComponentType::R: - return pix.r; - case ComponentType::G: - return pix.g; - case ComponentType::B: - return pix.b; - default: - return 0; - } - } - - // Pack 10-bit pixels: 4 pixels (40 bits) into 5 bytes - static void pack_10bit_group(uint8_t* dst, const std::array& values) - { - // Convert from 16-bit to 10-bit values - const uint16_t p0 = values[0] >> 6; - const uint16_t p1 = values[1] >> 6; - const uint16_t p2 = values[2] >> 6; - const uint16_t p3 = values[3] >> 6; - - // Pack MSB 8 bits of each pixel - dst[0] = (p0 >> 2) & 0xFF; - dst[1] = (p1 >> 2) & 0xFF; - dst[2] = (p2 >> 2) & 0xFF; - dst[3] = (p3 >> 2) & 0xFF; - - // Pack LSB 2 bits of each pixel into 5th byte - dst[4] = ((p0 & 0x03) << 6) | - ((p1 & 0x03) << 4) | - ((p2 & 0x03) << 2) | - ((p3 & 0x03) << 0); - } - - // Pack 12-bit pixels: 2 pixels (24 bits) into 3 bytes - static void pack_12bit_group(uint8_t* dst, const std::array& values) - { - // Convert from 16-bit to 12-bit values - const uint16_t p0 = values[0] >> 4; - const uint16_t p1 = values[1] >> 4; - - // Pack MSB 8 bits of each pixel - dst[0] = (p0 >> 4) & 0xFF; - dst[1] = (p1 >> 4) & 0xFF; - - // Pack LSB 4 bits of each pixel into 3rd byte - dst[2] = ((p0 & 0x0F) << 4) | - ((p1 & 0x0F) << 0); - } - -public: - static void pack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels, size_t y) - { - if constexpr (bit_depth == 10) { - // Process 4 pixels at a time for 10-bit packed - for (size_t x = 0; x < num_pixels; x += pixels_per_group) { - std::array pixel_values; - - for (size_t i = 0; i < pixels_per_group && (x + i) < num_pixels; i++) { - const RGB16& pix = src_line[x + i]; - const ComponentType component = get_bayer_component(x + i, y); - pixel_values[i] = extract_component(pix, component); - } - - // Fill remaining pixels with 0 if at end of line - for (size_t i = num_pixels - x; i < pixels_per_group; i++) { - pixel_values[i] = 0; - } - - const size_t group_idx = x / pixels_per_group; - uint8_t* dst_bytes = reinterpret_cast(&dst_line[0]); - pack_10bit_group(dst_bytes + group_idx * bytes_per_group, pixel_values); - } - } else if constexpr (bit_depth == 12) { - // Process 2 pixels at a time for 12-bit packed - for (size_t x = 0; x < num_pixels; x += pixels_per_group) { - std::array pixel_values; - - for (size_t i = 0; i < pixels_per_group && (x + i) < num_pixels; i++) { - const RGB16& pix = src_line[x + i]; - const ComponentType component = get_bayer_component(x + i, y); - pixel_values[i] = extract_component(pix, component); - } - - // Fill remaining pixels with 0 if at end of line - for (size_t i = num_pixels - x; i < pixels_per_group; i++) { - pixel_values[i] = 0; - } - - const size_t group_idx = x / pixels_per_group; - uint8_t* dst_bytes = reinterpret_cast(&dst_line[0]); - pack_12bit_group(dst_bytes + group_idx * bytes_per_group, pixel_values); - } - } - } - - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - // For packed formats, the stride represents bytes per line - // We need to calculate the correct width in bytes for the view - const size_t bytes_per_line = fb.stride(0); - - auto view = make_strided_fb_view(fb.map(0), fb.height(), - bytes_per_line, fb.stride(0)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - auto dst = md::submdspan(view, y_src, md::full_extent); - - pack_line(dst, linebuf, fb.width(), y_src); - } - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-raw.h b/kms++util/src/conv-raw.h deleted file mode 100644 index a0752d3..0000000 --- a/kms++util/src/conv-raw.h +++ /dev/null @@ -1,202 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -/* - * Raw Bayer formats - */ - -enum class BayerOrder { - RGGB, - BGGR, - GRBG, - GBRG -}; - -template -struct Bayer_Layout; - -// 8-bit bayer formats (uint8_t storage) -template -struct Bayer_Layout - : public FormatLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 8; -}; - -// 10-bit bayer formats (uint16_t storage) -template -struct Bayer_Layout - : public FormatLayout, - ComponentLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 10; -}; - -// 12-bit bayer formats (uint16_t storage) -template -struct Bayer_Layout - : public FormatLayout, - ComponentLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 12; -}; - -// 16-bit bayer formats (uint16_t storage) -template -struct Bayer_Layout - : public FormatLayout>> -{ - static constexpr BayerOrder bayer_order = Order; - static constexpr size_t bit_depth = 16; -}; - -// Convenient aliases for different bit depths -template using Bayer8_Layout = Bayer_Layout; -template using Bayer10_Layout = Bayer_Layout; -template using Bayer12_Layout = Bayer_Layout; -template using Bayer16_Layout = Bayer_Layout; - -// Format-specific type aliases -using SRGGB8_Layout = Bayer8_Layout; -using SGBRG8_Layout = Bayer8_Layout; -using SGRBG8_Layout = Bayer8_Layout; -using SBGGR8_Layout = Bayer8_Layout; - -using SRGGB10_Layout = Bayer10_Layout; -using SGBRG10_Layout = Bayer10_Layout; -using SGRBG10_Layout = Bayer10_Layout; -using SBGGR10_Layout = Bayer10_Layout; - -using SRGGB12_Layout = Bayer12_Layout; -using SGBRG12_Layout = Bayer12_Layout; -using SGRBG12_Layout = Bayer12_Layout; -using SBGGR12_Layout = Bayer12_Layout; - -using SRGGB16_Layout = Bayer16_Layout; -using SGBRG16_Layout = Bayer16_Layout; -using SGRBG16_Layout = Bayer16_Layout; -using SBGGR16_Layout = Bayer16_Layout; - -template -class Bayer_Writer -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static_assert(Layout::num_planes == 1); - static_assert(Plane::template component_count() == 1); - - static constexpr BayerOrder bayer_order = Layout::bayer_order; - static constexpr size_t bit_depth = Layout::bit_depth; - static constexpr bool has_padding = Plane::template component_count(); - - static constexpr size_t y_idx = Plane::template find_pos(); - static constexpr size_t x_idx = has_padding ? Plane::template find_pos() : 0; - - static constexpr size_t y_shift = 16 - Plane::template component_size; - - static constexpr ComponentType get_bayer_component(size_t x, size_t y) - { - const bool x_even = (x % 2) == 0; - const bool y_even = (y % 2) == 0; - - switch (bayer_order) { - case BayerOrder::RGGB: - if (y_even && x_even) return ComponentType::R; - if (y_even && !x_even) return ComponentType::G; - if (!y_even && x_even) return ComponentType::G; - return ComponentType::B; - - case BayerOrder::BGGR: - if (y_even && x_even) return ComponentType::B; - if (y_even && !x_even) return ComponentType::G; - if (!y_even && x_even) return ComponentType::G; - return ComponentType::R; - - case BayerOrder::GRBG: - if (y_even && x_even) return ComponentType::G; - if (y_even && !x_even) return ComponentType::R; - if (!y_even && x_even) return ComponentType::B; - return ComponentType::G; - - case BayerOrder::GBRG: - if (y_even && x_even) return ComponentType::G; - if (y_even && !x_even) return ComponentType::B; - if (!y_even && x_even) return ComponentType::R; - return ComponentType::G; - } - - return ComponentType::Y; // fallback - } - - static uint16_t extract_component(const RGB16& pix, ComponentType component) - { - switch (component) { - case ComponentType::R: - return pix.r; - case ComponentType::G: - return pix.g; - case ComponentType::B: - return pix.b; - default: - return 0; - } - } - -public: - static void pack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels, size_t y) - { - for (size_t x = 0; x < num_pixels; x++) { - const RGB16& pix = src_line[x]; - - const ComponentType component = get_bayer_component(x, y); - const uint16_t value = extract_component(pix, component); - - std::array components; - - if constexpr (has_padding) - components[x_idx] = 0; - - components[y_idx] = value >> y_shift; - - dst_line[x] = Plane::pack(components); - } - } - - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - auto view = make_strided_fb_view(fb.map(0), fb.height(), fb.width(), - fb.stride(0)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - auto dst = md::submdspan(view, y_src, md::full_extent); - - pack_line(dst, linebuf, fb.width(), y_src); - } - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-rgb.h b/kms++util/src/conv-rgb.h deleted file mode 100644 index 6993247..0000000 --- a/kms++util/src/conv-rgb.h +++ /dev/null @@ -1,299 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -/* - * RGB - */ - -template -struct RGB_16_Layout - : public FormatLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout>> -{ -}; - -using XRGB4444_Layout = RGB_16_Layout< - ComponentType::X, 4, 12, - ComponentType::R, 4, 8, - ComponentType::G, 4, 4, - ComponentType::B, 4, 0 ->; - -using ARGB4444_Layout = RGB_16_Layout< - ComponentType::A, 4, 12, - ComponentType::R, 4, 8, - ComponentType::G, 4, 4, - ComponentType::B, 4, 0 ->; - -using XRGB1555_Layout = RGB_16_Layout< - ComponentType::X, 1, 15, - ComponentType::R, 5, 10, - ComponentType::G, 5, 5, - ComponentType::B, 5, 0 ->; - -using ARGB1555_Layout = RGB_16_Layout< - ComponentType::A, 1, 15, - ComponentType::R, 5, 10, - ComponentType::G, 5, 5, - ComponentType::B, 5, 0 ->; - -using RGB565_Layout = FormatLayout, - ComponentLayout, - ComponentLayout> ->; - -using BGR565_Layout = FormatLayout, - ComponentLayout, - ComponentLayout> ->; - -using RGB888_Layout = FormatLayout, - ComponentLayout, - ComponentLayout> ->; - -using BGR888_Layout = FormatLayout, - ComponentLayout, - ComponentLayout> ->; - -template -struct RGB_32_Layout - : public FormatLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout>> -{ -}; - -template -struct RGB_8_32_Layout - : public RGB_32_Layout< - C0, 8, 0, - C1, 8, 8, - C2, 8, 16, - C3, 8, 24> -{ -}; - -using XRGB8888_Layout = RGB_8_32_Layout; -using ARGB8888_Layout = RGB_8_32_Layout; - -using XBGR8888_Layout = RGB_8_32_Layout; -using ABGR8888_Layout = RGB_8_32_Layout; - -using RGBX8888_Layout = RGB_8_32_Layout; -using RGBA8888_Layout = RGB_8_32_Layout; - -using BGRX8888_Layout = RGB_8_32_Layout; -using BGRA8888_Layout = RGB_8_32_Layout; - -using XRGB2101010_Layout = RGB_32_Layout< - ComponentType::X, 2, 30, - ComponentType::R, 10, 20, - ComponentType::G, 10, 10, - ComponentType::B, 10, 0 ->; - -using ARGB2101010_Layout = RGB_32_Layout< - ComponentType::A, 2, 30, - ComponentType::R, 10, 20, - ComponentType::G, 10, 10, - ComponentType::B, 10, 0 ->; - -using XBGR2101010_Layout = RGB_32_Layout< - ComponentType::X, 2, 30, - ComponentType::B, 10, 20, - ComponentType::G, 10, 10, - ComponentType::R, 10, 0 ->; - -using ABGR2101010_Layout = RGB_32_Layout< - ComponentType::A, 2, 30, - ComponentType::B, 10, 20, - ComponentType::G, 10, 10, - ComponentType::R, 10, 0 ->; - -using RGBX1010102_Layout = RGB_32_Layout< - ComponentType::R, 10, 22, - ComponentType::G, 10, 12, - ComponentType::B, 10, 2, - ComponentType::X, 2, 0 ->; - -using RGBA1010102_Layout = RGB_32_Layout< - ComponentType::R, 10, 22, - ComponentType::G, 10, 12, - ComponentType::B, 10, 2, - ComponentType::A, 2, 0 ->; - -using BGRX1010102_Layout = RGB_32_Layout< - ComponentType::B, 10, 22, - ComponentType::G, 10, 12, - ComponentType::R, 10, 2, - ComponentType::X, 2, 0 ->; - -using BGRA1010102_Layout = RGB_32_Layout< - ComponentType::B, 10, 22, - ComponentType::G, 10, 12, - ComponentType::R, 10, 2, - ComponentType::A, 2, 0 ->; - -template -class ARGB_Writer -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static_assert(Layout::num_planes == 1); - static_assert(Plane::num_components == 3 || Plane::num_components == 4); - - static_assert(Plane::template component_count() == 1); - static_assert(Plane::template component_count() == 1); - static_assert(Plane::template component_count() == 1); - - static constexpr bool has_alpha = Plane::template component_count(); - static constexpr bool has_padding = Plane::template component_count(); - - static constexpr bool needs_packed_access = Plane::total_bits != Plane::storage_bits; - - static constexpr size_t a_idx = Plane::template find_pos(); - static constexpr size_t x_idx = Plane::template find_pos(); - static constexpr size_t r_idx = Plane::template find_pos(); - static constexpr size_t g_idx = Plane::template find_pos(); - static constexpr size_t b_idx = Plane::template find_pos(); - - static constexpr size_t a_shift = has_alpha ? 16 - Plane::template component_size : 0; - static constexpr size_t r_shift = 16 - Plane::template component_size; - static constexpr size_t g_shift = 16 - Plane::template component_size; - static constexpr size_t b_shift = 16 - Plane::template component_size; - - static_assert(Plane::total_bits % 8 == 0); - static constexpr size_t bytes_per_pixel = Plane::total_bits / 8; - -public: - // Pack and write num_pixels pixels from src_line to dst_line - static void pack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels) - { - for (size_t x = 0; x < num_pixels; x++) { - const RGB16& pix = src_line[x]; - - std::array - components; - - if constexpr (has_alpha) - components[a_idx] = pix.a >> a_shift; - - if constexpr (has_padding) - components[x_idx] = 0; - - components[r_idx] = pix.r >> r_shift; - components[g_idx] = pix.g >> g_shift; - components[b_idx] = pix.b >> b_shift; - - if constexpr (!needs_packed_access) { - dst_line[x] = Plane::pack(components); - } else { - auto dst_bytes = reinterpret_cast(&dst_line[0]); - - TStorage packed = Plane::pack(components); - - memcpy(dst_bytes + x * bytes_per_pixel, &packed, - bytes_per_pixel); - } - } - } - - // Read and unpack num_pixels pixels from src_line to dst_line - static void unpack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels) - { - for (size_t x = 0; x < num_pixels; x++) { - decltype(Plane::unpack(src_line[x])) components; - - if constexpr (!needs_packed_access) { - components = Plane::unpack(src_line[x]); - } else { - auto src_bytes = - reinterpret_cast(&src_line[0]); - TStorage packed; - - memcpy(&packed, src_bytes + x * bytes_per_pixel, - bytes_per_pixel); - - components = Plane::unpack(packed); - } - - dst_line[x] = RGB16 { - static_cast(components[r_idx] << r_shift), - static_cast(components[g_idx] << g_shift), - static_cast(components[b_idx] << b_shift), - static_cast(has_alpha ? components[a_idx] << a_shift : 0), - }; - } - } - - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - // View to the plane - auto view = make_strided_fb_view(fb.map(0), fb.height(), fb.width(), - fb.stride(0)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - auto dst = md::submdspan(view, y_src, md::full_extent); - - pack_line(dst, linebuf, fb.width()); - } - } - - static void get_line(IFramebuffer& fb, size_t w, size_t h, size_t row, std::span linebuf) - { - auto view = make_strided_fb_view(fb.map(0), fb.height(), fb.width(), - fb.stride(0)); - - auto src = md::submdspan(view, row, md::full_extent); - - unpack_line(linebuf, src); - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-yuv-packed.h b/kms++util/src/conv-yuv-packed.h deleted file mode 100644 index 01278ef..0000000 --- a/kms++util/src/conv-yuv-packed.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -/* YUV Packed */ - -template -struct YUV_Packed_Format - : public FormatLayout< - PlaneLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout - > - > -{ -}; - -// Define common packed YUV formats -using YUYV_Layout = YUV_Packed_Format; - -using YVYU_Layout = YUV_Packed_Format; - -using UYVY_Layout = YUV_Packed_Format; - -using VYUY_Layout = YUV_Packed_Format; - -template -class YUVPackedWriter -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static constexpr size_t y0_pos = Plane::template find_pos(); - static constexpr size_t y1_pos = Plane::template find_pos(); - static constexpr size_t cb_pos = Plane::template find_pos(); - static constexpr size_t cr_pos = Plane::template find_pos(); - -public: - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - auto view = make_strided_fb_view(fb.map(0), fb.height(), - fb.width() / 2, // Two pixels per storage unit - fb.stride(0)); - - for (size_t y = start_y; y <= end_y; y++) { - generate_line(y, linebuf); - - for (size_t x = 0; x < fb.width(); x += 2) { - // Get two pixels - const YUV16& pix0 = linebuf[x]; - const YUV16& pix1 = linebuf[x + 1]; - - std::array components; - - components[y0_pos] = pix0.y >> 8; - components[y1_pos] = pix1.y >> 8; - components[cb_pos] = ((pix0.u + pix1.u) / 2) >> 8; - components[cr_pos] = ((pix0.v + pix1.v) / 2) >> 8; - - view(y, x / 2) = Plane::pack(components); - } - } - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-yuv-planar-packed.h b/kms++util/src/conv-yuv-planar-packed.h deleted file mode 100644 index 481b594..0000000 --- a/kms++util/src/conv-yuv-planar-packed.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ -/* YUV Planar Packed (only T430 for now) */ - -struct T430_Layout : FormatLayout < - PlaneLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout>, - PlaneLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout>, - PlaneLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout> - > -{ - static constexpr size_t y_plane = 0; - static constexpr size_t cb_plane = 1; - static constexpr size_t cr_plane = 2; - -}; - -template -class YUVPlanarPackedWriter -{ - using YLayout = typename Format::template plane; - using CbLayout = typename Format::template plane; - using CrLayout = typename Format::template plane; - - using TY = typename YLayout::storage_type; - using TCb = typename CbLayout::storage_type; - using TCr = typename CrLayout::storage_type; - - static constexpr size_t pixels_in_group = 3; - - // Helper to extract the correct component based on component type - template - static uint16_t extract_component(const YUV16& pixel) - { - if constexpr (CType == ComponentType::Y) - return pixel.y; - else if constexpr (CType == ComponentType::Cb) - return pixel.u; - else if constexpr (CType == ComponentType::Cr) - return pixel.v; - else - return 0; // For padding or other component types - } - -public: - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - // Line buffers - std::vector linebuf(fb.width()); - - // Views to all planes - auto y_buf = make_strided_fb_view(fb.map(Format::y_plane), - fb.height(), fb.width(), - fb.stride(Format::y_plane)); - - auto cb_buf = make_strided_fb_view(fb.map(Format::cb_plane), - fb.height(), fb.width(), - fb.stride(Format::cb_plane)); - - auto cr_buf = make_strided_fb_view(fb.map(Format::cr_plane), - fb.height(), fb.width(), - fb.stride(Format::cr_plane)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - write_samples(md::submdspan(y_buf, y_src, md::full_extent), linebuf, fb.width()); - write_samples(md::submdspan(cb_buf, y_src, md::full_extent), linebuf, fb.width()); - write_samples(md::submdspan(cr_buf, y_src, md::full_extent), linebuf, fb.width()); - } - } - - -private: - template - static void write_samples(Buf&& view, auto&& linebuf, size_t num_pixels) - { - for (size_t x_src = 0; x_src < num_pixels; x_src += pixels_in_group) { - auto x_dst = x_src / pixels_in_group; - - write_group(view, linebuf, x_src, x_dst, - std::make_index_sequence{}); - } - } - - template - static void write_group(YBuf&& view, auto&& linebuf, size_t x_src, - size_t x_dst, std::index_sequence) - { - std::array values{ - static_cast( - (extract_component(linebuf[x_src + I]) >> - (16 - Plane::template component_size)))... - }; - - // Set padding bits to 0 - if constexpr (Plane::template component_count()) - values[Plane::template find_pos()] = 0; - - view[x_dst] = Plane::pack(values); - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-yuv-planar.h b/kms++util/src/conv-yuv-planar.h deleted file mode 100644 index b8e9247..0000000 --- a/kms++util/src/conv-yuv-planar.h +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ -/* YUV Planar */ - -template -class YUV_Planar_Layout - : public FormatLayout< - PlaneLayout>, - PlaneLayout>, - PlaneLayout> - > -{ -public: - static constexpr std::array uv_order = { P1, P2 }; - static constexpr size_t h_sub = HSubUV; - static constexpr size_t v_sub = VSubUV; - - template - static constexpr size_t find_plane() - { - return 1 + (std::ranges::find(uv_order, P) - uv_order.begin()); - } - - static constexpr size_t y_plane = 0; // Y is always plane 0 - static constexpr size_t cb_plane = find_plane(); - static constexpr size_t cr_plane = find_plane(); -}; - -using YUV444_Layout = YUV_Planar_Layout; -using YVU444_Layout = YUV_Planar_Layout; -using YUV422_Layout = YUV_Planar_Layout; -using YVU422_Layout = YUV_Planar_Layout; -using YUV420_Layout = YUV_Planar_Layout; -using YVU420_Layout = YUV_Planar_Layout; - -template -class YUVPlanarWriter -{ - using YLayout = typename Format::template plane; - using CbLayout = typename Format::template plane; - using CrLayout = typename Format::template plane; - - using TY = typename YLayout::storage_type; - using TCb = typename CbLayout::storage_type; - using TCr = typename CrLayout::storage_type; -public: - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - assert(start_y % Format::v_sub == 0); - assert((end_y + 1) % Format::v_sub == 0); - - // Line buffers - std::vector linebuf_storage(fb.width() * Format::v_sub); - auto linebuf = md::mdspan(linebuf_storage.data(), Format::v_sub, fb.width()); - - // Views to all planes - auto y_buf = make_strided_fb_view(fb.map(Format::y_plane), fb.height(), - fb.width(), fb.stride(Format::y_plane)); - - auto cb_buf = make_strided_fb_view(fb.map(Format::cb_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cb_plane)); - - auto cr_buf = make_strided_fb_view(fb.map(Format::cr_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cr_plane)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - size_t y_offset = y_src % Format::v_sub; - - if (y_offset == 0) { - // Fill line buffers - for (size_t buf_y = 0; buf_y < Format::v_sub; buf_y++) { - auto line = md::submdspan(linebuf, buf_y, md::full_extent); - std::span span(line.data_handle(), line.size()); - generate_line(y_src + buf_y, span); - } - } - - // Write Y plane - write_y_line(y_buf, y_src, linebuf, y_offset); - - // Write Cb/Cr planes if we're at a subsampling boundary - if (y_offset == 0) - write_uv_line(cb_buf, cr_buf, linebuf, y_src); - } - } - - static void write_lines(IFramebuffer& fb, size_t start_y, size_t end_y, - Is2Dspan auto&& lines) - { - const size_t height = end_y - start_y + 1; - - if (lines.extent(0) < height || lines.extent(1) < fb.width()) - throw std::invalid_argument("Source line buffer too small"); - - assert(start_y % Format::v_sub == 0); - assert((end_y + 1) % Format::v_sub == 0); - - // Views to all planes - auto y_buf = make_strided_fb_view(fb.map(Format::y_plane), fb.height(), - fb.width(), fb.stride(Format::y_plane)); - - auto cb_buf = make_strided_fb_view(fb.map(Format::cb_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cb_plane)); - - auto cr_buf = make_strided_fb_view(fb.map(Format::cr_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cr_plane)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - size_t y_offset = y_src % Format::v_sub; - - // Write Y plane - write_y_line(y_buf, y_src, lines, y_offset); - - // Write Cb/Cr planes if we're at a subsampling boundary - if (y_offset == 0) - write_uv_line(cb_buf, cr_buf, lines, y_src); - } - } - -private: - template - static void write_y_line(YBuf& y_buf, size_t y_src, auto& linebuf, size_t y_offset) - { - for (size_t x = 0; x < linebuf.extent(1); x++) - y_buf(y_src, x) = YLayout::pack(linebuf(y_offset, x).y >> 8); - } - - template - static void write_uv_line(UVBuf& cb_buf, UVBuf& cr_buf, auto& linebuf, size_t y_src) - { - const size_t uv_y = y_src / Format::v_sub; - - for (size_t x = 0; x < linebuf.extent(1); x += Format::h_sub) { - // Average subsampled region - uint32_t u_sum = 0, v_sum = 0; - for (size_t y = 0; y < Format::v_sub; y++) { - for (size_t x_off = 0; x_off < Format::h_sub; x_off++) { - u_sum += linebuf(y, x + x_off).u; - v_sum += linebuf(y, x + x_off).v; - } - } - - const size_t total_samples = Format::h_sub * Format::v_sub; - const size_t uv_x = x / Format::h_sub; - - cb_buf(uv_y, uv_x) = CbLayout::pack(u_sum / total_samples >> 8); - cr_buf(uv_y, uv_x) = CrLayout::pack(v_sum / total_samples >> 8); - } - } - -public: - static void read_lines(IFramebuffer& fb, size_t start_y, size_t end_y, - Is2Dspan auto&& dest) - { - const size_t height = end_y - start_y + 1; - - if (dest.extent(0) < height || dest.extent(1) < fb.width()) - throw std::invalid_argument("Destination line buffer too small"); - - assert(start_y % Format::v_sub == 0); - assert((end_y + 1) % Format::v_sub == 0); - - auto y_buf = make_strided_fb_view(fb.map(Format::y_plane), fb.height(), - fb.width(), fb.stride(Format::y_plane)); - - auto cb_buf = make_strided_fb_view(fb.map(Format::cb_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cb_plane)); - - auto cr_buf = make_strided_fb_view(fb.map(Format::cr_plane), - fb.height() / Format::v_sub, - fb.width() / Format::h_sub, - fb.stride(Format::cr_plane)); - - for (size_t y = start_y; y <= end_y; y++) { - for (size_t x = 0; x < fb.width(); x++) { - const auto y_val = YLayout::unpack(y_buf(y, x))[0]; - - const size_t uv_y = y / Format::v_sub; - const size_t uv_x = x / Format::h_sub; - - const auto u_val = CbLayout::unpack(cb_buf(uv_y, uv_x))[0]; - const auto v_val = CrLayout::unpack(cr_buf(uv_y, uv_x))[0]; - - dest(y - start_y, x) = YUV16 { - static_cast(y_val << 8), - static_cast(u_val << 8), - static_cast(v_val << 8) - }; - } - } - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-yuv-semiplanar.h b/kms++util/src/conv-yuv-semiplanar.h deleted file mode 100644 index 81117c1..0000000 --- a/kms++util/src/conv-yuv-semiplanar.h +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -/* Semiplanar YUV */ - -template -struct NV12_Family_Layout - : public FormatLayout< - PlaneLayout - >, - PlaneLayout, - ComponentLayout - > - > -{ - static constexpr size_t h_sub = HSub; - static constexpr size_t v_sub = VSub; -}; - -using NV12_Layout = NV12_Family_Layout; -using NV21_Layout = NV12_Family_Layout; - -using NV16_Layout = NV12_Family_Layout; -using NV61_Layout = NV12_Family_Layout; - -template -struct P030_Family_Layout - : public FormatLayout< - PlaneLayout, - ComponentLayout, - ComponentLayout - >, - PlaneLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout - > - > -{ - static constexpr size_t h_sub = HSub; - static constexpr size_t v_sub = VSub; -}; - -using P030_Layout = P030_Family_Layout<2, 2>; -using P230_Layout = P030_Family_Layout<2, 1>; - -template -struct SubsampleHelper { - template 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 -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 = typename Layout::template plane<0>; - using UVLayout = typename Layout::template plane<1>; - - using TY = typename YLayout::storage_type; - using TCrCb = typename UVLayout::storage_type; - - static constexpr size_t pixels_in_group = YLayout::template component_count(); - static_assert(pixels_in_group == UVLayout::template component_count()); - static_assert(pixels_in_group == UVLayout::template component_count()); - -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 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(fb.map(0), fb.height(), - fb.width() / pixels_in_group, fb.stride(0)); - - auto uv_view = make_strided_fb_view(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_off = 0; y_off < v_sub; y_off++) { - auto line = md::submdspan(linebuf, y_off, md::full_extent); - std::span span(line.data_handle(), line.size()); - generate_line(y_src + y_off, 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 - 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{}); - } - } - - template - static void write_y_group(YBuf&& y_view, auto&& linebuf, size_t x_src, - size_t x_dst, std::index_sequence) - { - std::array y_values{ - static_cast((linebuf(x_src + I).y >> (16 - YLayout::template component_size)))... - }; - - y_view(x_dst) = YLayout::pack(y_values); - } - - template - 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{}); - } - } - - template - static void write_uv_group(UVBuf& uv_view, auto& group_view, size_t y_dst, size_t x_dst, - std::index_sequence) - { - std::array uv_values; - - ( - [&]() { - constexpr size_t group_idx = i * h_sub; - - constexpr size_t u_idx = UVLayout::template find_nth_pos(i); - constexpr size_t v_idx = UVLayout::template find_nth_pos(i); - - auto u = SubsampleHelper::subsample( - [&group_view](size_t y, size_t x) { return group_view(y, x).u; }, - group_idx); - - auto v = SubsampleHelper::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); - uv_values[v_idx] = - v >> (16 - UVLayout::template component_size); - }.template operator()(), - ...); - - uv_view(y_dst, x_dst) = UVLayout::pack(uv_values); - } -}; - -} // namespace kms diff --git a/kms++util/src/conv-yuv.h b/kms++util/src/conv-yuv.h deleted file mode 100644 index 1695129..0000000 --- a/kms++util/src/conv-yuv.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "conv-common.h" - -namespace kms -{ - -using XVUY2101010_Layout = - FormatLayout, - ComponentLayout, - ComponentLayout, - ComponentLayout>>; - -template -class YUV_Writer -{ - using Plane = typename Layout::template plane<0>; - using TStorage = typename Plane::storage_type; - - static_assert(Layout::num_planes == 1); - static_assert(Plane::num_components == 3 || Plane::num_components == 4); - - static_assert(Plane::template component_count() == 1); - static_assert(Plane::template component_count() == 1); - static_assert(Plane::template component_count() == 1); - - static constexpr bool has_alpha = Plane::template component_count(); - static constexpr bool has_padding = Plane::template component_count(); - - static constexpr bool needs_packed_access = Plane::total_bits != Plane::storage_bits; - - static constexpr size_t a_idx = Plane::template find_pos(); - static constexpr size_t x_idx = Plane::template find_pos(); - static constexpr size_t y_idx = Plane::template find_pos(); - static constexpr size_t cb_idx = Plane::template find_pos(); - static constexpr size_t cr_idx = Plane::template find_pos(); - - static constexpr size_t a_shift = has_alpha ? 16 - Plane::template component_size : 0; - static constexpr size_t x_shift = has_padding ? 16 - Plane::template component_size : 0; - static constexpr size_t y_shift = 16 - Plane::template component_size; - static constexpr size_t cb_shift = 16 - Plane::template component_size; - static constexpr size_t cr_shift = 16 - Plane::template component_size; - - static_assert(Plane::total_bits % 8 == 0); - static constexpr size_t bytes_per_pixel = Plane::total_bits / 8; - -public: - // Pack and write num_pixels pixels from src_line to dst_line - static void pack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels) - { - for (size_t x = 0; x < num_pixels; x++) { - const YUV16& pix = src_line[x]; - - std::array - components; - - if constexpr (has_alpha) - components[a_idx] = pix.a >> a_shift; - - if constexpr (has_padding) - components[x_idx] = 0; - - components[y_idx] = pix.y >> y_shift; - components[cb_idx] = pix.u >> cb_shift; - components[cr_idx] = pix.v >> cr_shift; - - if constexpr (!needs_packed_access) { - dst_line[x] = Plane::pack(components); - } else { - auto dst_bytes = reinterpret_cast(&dst_line[0]); - - TStorage packed = Plane::pack(components); - - memcpy(dst_bytes + x * bytes_per_pixel, &packed, - bytes_per_pixel); - } - } - } - - // Read and unpack num_pixels pixels from src_line to dst_line - static void unpack_line(HasIndexOperatorReturning auto&& dst_line, - HasIndexOperatorReturning auto&& src_line, - size_t num_pixels) - { - for (size_t x = 0; x < num_pixels; x++) { - decltype(Plane::unpack(src_line[x])) components; - - if constexpr (!needs_packed_access) { - components = Plane::unpack(src_line[x]); - } else { - auto src_bytes = - reinterpret_cast(&src_line[0]); - TStorage packed; - - memcpy(&packed, src_bytes + x * bytes_per_pixel, - bytes_per_pixel); - - components = Plane::unpack(packed); - } - - dst_line[x] = YUV16 { - static_cast(components[y_idx] << y_shift), - static_cast(components[cb_idx] << cb_shift), - static_cast(components[cr_idx] << cr_shift), - static_cast(has_alpha ? components[a_idx] << a_shift : 0), - }; - } - } - - static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, - auto&& generate_line) - { - std::vector linebuf(fb.width()); - - // View to the plane - auto view = make_strided_fb_view(fb.map(0), fb.height(), fb.width(), - fb.stride(0)); - - for (size_t y_src = start_y; y_src <= end_y; y_src++) { - generate_line(y_src, linebuf); - - auto dst = md::submdspan(view, y_src, md::full_extent); - - pack_line(dst, linebuf, fb.width()); - } - } - - static void get_line(IFramebuffer& fb, size_t w, size_t h, size_t row, std::span linebuf) - { - auto view = make_strided_fb_view(fb.map(0), fb.height(), fb.width(), - fb.stride(0)); - - auto src = md::submdspan(view, row, md::full_extent); - - unpack_line(linebuf, src); - } -}; - -} // namespace kms diff --git a/kms++util/src/conv.h b/kms++util/src/conv.h deleted file mode 100644 index 82608dd..0000000 --- a/kms++util/src/conv.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "conv-rgb.h" -#include "conv-yuv.h" -#include "conv-yuv-packed.h" -#include "conv-yuv-semiplanar.h" -#include "conv-yuv-planar.h" -#include "conv-yuv-planar-packed.h" -#include "conv-gray.h" -#include "conv-raw.h" -#include "conv-raw-packed.h" diff --git a/kms++util/src/testpat.cpp b/kms++util/src/testpat.cpp index b9f1e56..bd0aeb8 100644 --- a/kms++util/src/testpat.cpp +++ b/kms++util/src/testpat.cpp @@ -1,577 +1,69 @@ - -#include -#include -#include -#include #include -#include - -#ifdef HAS_PTHREAD -#include -#endif +#include #include #include -#include "conv.h" - -using namespace std; +#include namespace kms { -static RGB16 get_test_pattern_pixel_16(size_t w, size_t h, size_t x, size_t y) -{ - const unsigned mw = 20; - - const unsigned xm1 = mw; - const unsigned xm2 = w - mw - 1; - const unsigned ym1 = mw; - const unsigned ym2 = h - mw - 1; - - // white margin lines - if (x == xm1 || x == xm2 || y == ym1 || y == ym2) - return RGB16::from_8(255, 255, 255); - // white box in top left corner - else if (x < xm1 && y < ym1) - return RGB16::from_8(255, 255, 255); - // white box outlines to corners - else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) - return RGB16::from_8(255, 255, 255); - // white box outlines to corners - else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) - return RGB16::from_8(255, 255, 255); - // blue bar on the left - else if (x < xm1 && (y > ym1 && y < ym2)) - return RGB16::from_8(0, 0, 255); - // blue bar on the top - else if (y < ym1 && (x > xm1 && x < xm2)) - return RGB16::from_8(0, 0, 255); - // red bar on the right - else if (x > xm2 && (y > ym1 && y < ym2)) - return RGB16::from_8(255, 0, 0); - // red bar on the bottom - else if (y > ym2 && (x > xm1 && x < xm2)) - return RGB16::from_8(255, 0, 0); - // inside the margins - else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { - // diagonal line - if (x == y || w - x == h - y) - return RGB16::from_8(255, 255, 255); - // diagonal line - else if (w - x - 1 == y || x == h - y - 1) - return RGB16::from_8(255, 255, 255); - else { - int t = (x - xm1 - 1) * 8 / (xm2 - xm1 - 1); - unsigned r = 0, g = 0, b = 0; - - unsigned c = (y - ym1 - 1) % 256; - - switch (t) { - case 0: - r = c; - break; - case 1: - g = c; - break; - case 2: - b = c; - break; - case 3: - g = b = c; - break; - case 4: - r = b = c; - break; - case 5: - r = g = c; - break; - case 6: - r = g = b = c; - break; - case 7: - break; - } - - return RGB16::from_8(r, g, b); - } - } else { - // black corners - return RGB16::from_8(0, 0, 0); - } -} - -static void get_test_pattern_line(size_t w, size_t h, size_t row, std::span buf) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = get_test_pattern_pixel_16(w, h, x, row); -} - -static void get_test_pattern_line_yuv(size_t w, size_t h, size_t row, - std::span buf, - const TestPatternOptions& options) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = get_test_pattern_pixel_16(w, h, x, row) - .to_yuv(options.rec, options.range); -} -// SMPTE RP 219-1:2014 -// High-Definition, Standard-Definition Compatible Color Bar Signal -// Limited range YUV -static YUV16 get_smpte_pixel(size_t w, size_t h, size_t x, size_t y) +void draw_test_pattern(IFramebuffer& fb, const TestPatternOptions& options) { - // Pattern colors (12-bit values, limited range) - constexpr YUV16 gray40 = YUV16::from_12(1658, 2048, 2048); // 40% Gray - constexpr YUV16 white75 = YUV16::from_12(2884, 2048, 2048); // 75% White - constexpr YUV16 yellow75 = YUV16::from_12(2694, 704, 2171); // 75% Yellow - constexpr YUV16 cyan75 = YUV16::from_12(2325, 2356, 704); // 75% Cyan - constexpr YUV16 green75 = YUV16::from_12(2136, 1012, 827); // 75% Green - constexpr YUV16 magenta75 = YUV16::from_12(1004, 3084, 3269); // 75% Magenta - constexpr YUV16 red75 = YUV16::from_12(815, 1740, 3392); // 75% Red - constexpr YUV16 blue75 = YUV16::from_12(446, 3392, 1925); // 75% Blue - constexpr YUV16 cyan100 = YUV16::from_12(3015, 2459, 256); // 100% Cyan - constexpr YUV16 blue100 = YUV16::from_12(509, 3840, 1884); // 100% Blue - constexpr YUV16 yellow100 = YUV16::from_12(3507, 256, 2212); // 100% Yellow - constexpr YUV16 black = YUV16::from_12(256, 2048, 2048); // 0% Black - constexpr YUV16 white100 = YUV16::from_12(3760, 2048, 2048); // 100% White - constexpr YUV16 red100 = YUV16::from_12(1001, 1637, 3840); // 100% Red - constexpr YUV16 gray15 = YUV16::from_12(782, 2048, 2048); // 15% Gray - - // PLUGE steps - constexpr YUV16 black_m2 = YUV16::from_12(186, 2048, 2048); // -2% Black - constexpr YUV16 black_p2 = YUV16::from_12(326, 2048, 2048); // +2% Black - constexpr YUV16 black_p4 = YUV16::from_12(396, 2048, 2048); // +4% Black - - // High precision for x-axis calculations - constexpr size_t M = 1024; - x = x * M; - const size_t a = w * M; - const size_t c = (a * 3 / 4) / 7; - const size_t d = a / 8; - - // Pattern heights - const size_t pattern1_height = (h * 7) / 12; - const size_t pattern2_height = pattern1_height + (h / 12); - const size_t pattern3_height = pattern2_height + (h / 12); - - // Pattern 1 (75% color bars) - if (y < pattern1_height) { - if (x < d || x >= (a - d)) - return gray40; - - size_t bar_index = (x - d) / c; - switch (bar_index) { - case 0: - return white75; - case 1: - return yellow75; - case 2: - return cyan75; - case 3: - return green75; - case 4: - return magenta75; - case 5: - return red75; - default: - return blue75; - } - } - - // Pattern 2 (Color difference reference) - if (y >= pattern1_height && y < pattern2_height) { - if (x < d) - return cyan100; - - if (x >= (a - d)) - return blue100; - - return white75; - } - - // Pattern 3 (Ramp) - if (y >= pattern2_height && y < pattern3_height) { - if (x < d) - return yellow100; + const auto& info = get_pixel_format_info(fb.format()); - if (x >= (a - d)) - return red100; + pixpat_buffer buf{}; + buf.format = info.name.c_str(); + buf.width = fb.width(); + buf.height = fb.height(); + buf.num_planes = fb.num_planes(); - const size_t ramp_width = a - 2 * d; - const size_t ramp_x = x - d; + if (buf.num_planes > 4) + throw std::runtime_error("Too many planes"); - uint16_t y_val = 256 + (3760 - 256) * ramp_x / ramp_width; - return YUV16::from_12(y_val, 2048, 2048); + for (unsigned i = 0; i < buf.num_planes; ++i) { + buf.planes[i] = fb.map(i); + buf.strides[i] = fb.stride(i); } - // Pattern 4 (PLUGE) - if (y >= pattern3_height) { - const size_t c0 = d; - const size_t c1 = c0 + c * 3 / 2; - const size_t c2 = c1 + 2 * c; - const size_t c3 = c2 + c * 5 / 6; - - if (x < c0) - return gray15; - - if (x < c1) - return black; - - if (x < c2) - return white100; - - if (x < c3) - return black; - - if (x >= a - d) - return gray15; - - if (x >= a - d - c) - return black; - - const size_t step = (x - c3) / (c / 3); - - switch (step) { - case 0: - return black_m2; // -2% - case 1: - return black; // 0% - case 2: - return black_p2; // +2% - case 3: - return black; // 0% - default: - return black_p4; // +4% - } + pixpat_pattern_opts popts{}; + switch (options.rec) { + case RecStandard::BT601: popts.rec = PIXPAT_REC_BT601; break; + case RecStandard::BT709: popts.rec = PIXPAT_REC_BT709; break; + case RecStandard::BT2020: popts.rec = PIXPAT_REC_BT2020; break; } - - return black; -} - -static void get_smpte_line_rgb(size_t w, size_t h, size_t row, std::span buf, - const TestPatternOptions& options) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = get_smpte_pixel(w, h, x, row) - .to_rgb(RecStandard::BT709, ColorRange::Limited); -} - -static void get_smpte_line_yuv(size_t w, size_t h, size_t row, std::span buf, - const TestPatternOptions& options) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = get_smpte_pixel(w, h, x, row); -} - -static void get_plain_line_rgb(size_t w, const RGB16& color, std::span buf) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = color; -} - -static void get_plain_line_yuv(size_t w, const YUV16& color, std::span buf) -{ - for (size_t x = 0; x < w; ++x) - buf[x] = color; -} - -static void draw_test_pattern_part(IFramebuffer& fb, size_t start_y, size_t end_y, - const TestPatternOptions& options) -{ - std::optional solid; - - if (options.pattern == "red") - solid = RGB16(0xffff, 0, 0); - else if (options.pattern == "green") - solid = RGB16(0, 0xffff, 0); - else if (options.pattern == "blue") - solid = RGB16(0, 0, 0xffff); - else if (options.pattern == "white") - solid = RGB16(0xffff, 0xffff, 0xffff); - else if (options.pattern == "black") - solid = RGB16(0, 0, 0); - - std::function span)> generate_line_rgb; - std::function span)> generate_line_yuv; - - if (solid.has_value()) { - generate_line_rgb = [&fb, rgb = solid.value()](size_t y, - std::span span) { - get_plain_line_rgb(fb.width(), rgb, span); + popts.range = options.range == ColorRange::Full ? PIXPAT_RANGE_FULL + : PIXPAT_RANGE_LIMITED; + popts.num_threads = 0; + + const char* pattern = options.pattern.empty() ? nullptr : options.pattern.c_str(); + std::string params; + + if (pattern) { + struct { + const char* alias; + const char* color; + } static const solid_aliases[] = { + { "red", "ff0000" }, + { "green", "00ff00" }, + { "blue", "0000ff" }, + { "white", "ffffff" }, + { "black", "000000" }, }; - generate_line_yuv = [&fb, rgb = solid.value(), - &options](size_t y, std::span span) { - get_plain_line_yuv(fb.width(), - rgb.to_yuv(options.rec, options.range), span); - }; - } else if (options.pattern == "smpte") { - generate_line_rgb = [&fb, &options](size_t y, std::span span) { - get_smpte_line_rgb(fb.width(), fb.height(), y, span, options); - }; - - generate_line_yuv = [&fb, &options](size_t y, std::span span) { - get_smpte_line_yuv(fb.width(), fb.height(), y, span, options); - }; - } else { - generate_line_rgb = [&fb](size_t y, std::span span) { - get_test_pattern_line(fb.width(), fb.height(), y, span); - }; - - generate_line_yuv = [&fb, &options](size_t y, std::span span) { - get_test_pattern_line_yuv(fb.width(), fb.height(), y, span, - options); - }; - } - -#define CASE_ARGB(x) \ - case PixelFormat::x: \ - ARGB_Writer::write_pattern(fb, start_y, end_y, \ - generate_line_rgb); \ - break; - -#define CASE_YUV(x) \ - case PixelFormat::x: \ - YUV_Writer::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_YUV_PACKED(x) \ - case PixelFormat::x: \ - YUVPackedWriter::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_YUV_SEMI(x) \ - case PixelFormat::x: \ - YUVSemiPlanarWriter::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_YUV_PLANAR(x) \ - case PixelFormat::x: \ - YUVPlanarWriter::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_Y_ONLY(x) \ - case PixelFormat::x: \ - Y_Writer::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_YUV_PLANAR_PACKED(x) \ - case PixelFormat::x: \ - YUVPlanarPackedWriter::write_pattern(fb, start_y, end_y, \ - generate_line_yuv); \ - break; - -#define CASE_RAW(x) \ - case PixelFormat::x: \ - Bayer_Writer::write_pattern(fb, start_y, end_y, \ - generate_line_rgb); \ - break; - -#define CASE_RAW_PACKED(x) \ - case PixelFormat::x: \ - BayerPacked_Writer::write_pattern(fb, start_y, end_y, \ - generate_line_rgb); \ - break; - - switch (fb.format()) { - CASE_YUV_SEMI(P230); - CASE_YUV_SEMI(P030); - CASE_YUV_SEMI(NV12); - CASE_YUV_SEMI(NV21); - CASE_YUV_SEMI(NV16); - CASE_YUV_SEMI(NV61); - - CASE_ARGB(RGB565); - CASE_ARGB(BGR565); - - CASE_ARGB(XRGB1555); - CASE_ARGB(ARGB1555); - CASE_ARGB(XRGB4444); - CASE_ARGB(ARGB4444); - - CASE_ARGB(RGB888); - CASE_ARGB(BGR888); - - CASE_ARGB(XRGB8888); - CASE_ARGB(ARGB8888); - CASE_ARGB(XBGR8888); - CASE_ARGB(ABGR8888); - CASE_ARGB(RGBX8888); - CASE_ARGB(RGBA8888); - CASE_ARGB(BGRX8888); - CASE_ARGB(BGRA8888); - CASE_ARGB(XRGB2101010); - CASE_ARGB(ARGB2101010); - CASE_ARGB(XBGR2101010); - CASE_ARGB(ABGR2101010); - CASE_ARGB(RGBX1010102); - CASE_ARGB(RGBA1010102); - CASE_ARGB(BGRX1010102); - CASE_ARGB(BGRA1010102); - - CASE_YUV_PACKED(YUYV); - CASE_YUV_PACKED(YVYU); - CASE_YUV_PACKED(UYVY); - CASE_YUV_PACKED(VYUY); - - CASE_YUV(XVUY2101010); - - CASE_YUV_PLANAR(YUV444); - CASE_YUV_PLANAR(YVU444); - CASE_YUV_PLANAR(YUV422); - CASE_YUV_PLANAR(YVU422); - CASE_YUV_PLANAR(YUV420); - CASE_YUV_PLANAR(YVU420); - - CASE_Y_ONLY(Y8); - CASE_Y_ONLY(XYYY2101010); - - CASE_YUV_PLANAR_PACKED(T430); - - CASE_RAW(SRGGB8); - CASE_RAW(SGBRG8); - CASE_RAW(SGRBG8); - CASE_RAW(SBGGR8); - - CASE_RAW(SRGGB10); - CASE_RAW(SGBRG10); - CASE_RAW(SGRBG10); - CASE_RAW(SBGGR10); - - CASE_RAW(SRGGB12); - CASE_RAW(SGBRG12); - CASE_RAW(SGRBG12); - CASE_RAW(SBGGR12); - - CASE_RAW(SRGGB16); - CASE_RAW(SGBRG16); - CASE_RAW(SGRBG16); - CASE_RAW(SBGGR16); - - CASE_RAW_PACKED(SRGGB10P); - CASE_RAW_PACKED(SGBRG10P); - CASE_RAW_PACKED(SGRBG10P); - CASE_RAW_PACKED(SBGGR10P); - - CASE_RAW_PACKED(SRGGB12P); - CASE_RAW_PACKED(SGBRG12P); - CASE_RAW_PACKED(SGRBG12P); - CASE_RAW_PACKED(SBGGR12P); - - default: - break; - } -} - -void draw_test_pattern_multi(IFramebuffer& fb, const TestPatternOptions& options) -{ - const auto& info = get_pixel_format_info(fb.format()); - uint8_t v_sub = 0; - for (size_t p = 0; p < info.num_planes; ++p) - v_sub = max(v_sub, info.planes[p].vsub); - - if (!v_sub || fb.height() % v_sub) - throw invalid_argument("FB height must be divisible with vsub"); - - // Create the mmaps before starting the threads - for (size_t i = 0; i < fb.num_planes(); ++i) - fb.map(i); - - size_t num_threads = thread::hardware_concurrency(); - - size_t part_height = fb.height() / num_threads; - - // round up to v_sub - part_height = (part_height + v_sub - 1) / v_sub * v_sub; - - vector workers; - - std::vector errors(num_threads); - - for (size_t n = 0; n < num_threads; ++n) { - size_t start = n * part_height; - size_t end = start + part_height - 1; - - if (n == num_threads - 1) - end = fb.height() - 1; - - workers.push_back(thread([&fb, start, end, &options, &error = errors[n]]() { - try { - draw_test_pattern_part(fb, start, end, options); - } catch (...) { - error = std::current_exception(); + for (const auto& a : solid_aliases) { + if (options.pattern == a.alias) { + pattern = "plain"; + params = std::string("color=") + a.color; + popts.params = params.c_str(); + break; } - })); + } } - for (thread& t : workers) - t.join(); - - auto i = std::find_if(errors.begin(), errors.end(), - [](const auto& e) { return e != nullptr; }); - if (i != errors.end()) - std::rethrow_exception(*i); -} - -void draw_test_pattern_single(IFramebuffer& fb, const TestPatternOptions& options) -{ - draw_test_pattern_part(fb, 0, fb.height() - 1, options); -} - -void draw_test_pattern(IFramebuffer& fb, const TestPatternOptions& options) -{ -#ifdef HAS_PTHREAD - draw_test_pattern_multi(fb, options); -#else - draw_test_pattern_single(fb, options); -#endif + if (pixpat_draw_pattern(&buf, pattern, &popts) != 0) + throw std::runtime_error("pixpat_draw_pattern failed"); } } // namespace kms - -extern "C" { - -int c_draw_test_pattern(struct CDrawTestPatternParameters* params) -{ - using namespace kms; - - try { - auto fmt = find_pixel_format_by_name(params->format_name); - - ExtCPUFramebuffer fb(params->width, - params->height, - fmt, - params->buffers, - params->sizes, - params->pitches, - params->offsets); - - RecStandard rec; - if (params->rec_standard == 0) - rec = RecStandard::BT601; - else if (params->rec_standard == 1) - rec = RecStandard::BT709; - else if (params->rec_standard == 2) - rec = RecStandard::BT2020; - else - return -1; - - TestPatternOptions options; - options.pattern = params->pattern; - options.rec = rec; - options.range = params->full_range ? ColorRange::Full : ColorRange::Limited; - - draw_test_pattern(fb, options); - } catch (const exception&) { - return -1; - } - - return 0; -} - -} -- cgit v1.2.3