From ce748d0b1e9f78b29cc2029f9446cd242af5fa3c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 8 Sep 2025 16:23:21 +0300 Subject: kms++util/testpat: Add RAW Bayer testpat generation Signed-off-by: Tomi Valkeinen --- kms++util/src/conv-raw-packed.h | 230 ++++++++++++++++++++++++++++++++++++++++ kms++util/src/conv-raw.h | 202 +++++++++++++++++++++++++++++++++++ kms++util/src/conv.h | 2 + kms++util/src/testpat.cpp | 42 ++++++++ 4 files changed, 476 insertions(+) create mode 100644 kms++util/src/conv-raw-packed.h create mode 100644 kms++util/src/conv-raw.h diff --git a/kms++util/src/conv-raw-packed.h b/kms++util/src/conv-raw-packed.h new file mode 100644 index 0000000..7465c6b --- /dev/null +++ b/kms++util/src/conv-raw-packed.h @@ -0,0 +1,230 @@ +#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 new file mode 100644 index 0000000..a0752d3 --- /dev/null +++ b/kms++util/src/conv-raw.h @@ -0,0 +1,202 @@ +#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.h b/kms++util/src/conv.h index 720480a..82608dd 100644 --- a/kms++util/src/conv.h +++ b/kms++util/src/conv.h @@ -7,3 +7,5 @@ #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 bdbe647..7984de8 100644 --- a/kms++util/src/testpat.cpp +++ b/kms++util/src/testpat.cpp @@ -367,6 +367,18 @@ static void draw_test_pattern_part(IFramebuffer& fb, size_t start_y, size_t end_ 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(XV20); CASE_YUV_SEMI(XV15); @@ -422,6 +434,36 @@ static void draw_test_pattern_part(IFramebuffer& fb, size_t start_y, size_t end_ CASE_YUV_PLANAR_PACKED(X403); + 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; } -- cgit v1.2.3