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/src/testpat.cpp | 598 ++++------------------------------------------ 1 file changed, 45 insertions(+), 553 deletions(-) (limited to 'kms++util/src/testpat.cpp') 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