- tools/gen_font_atlas: Python/Pillow build tool — skyline packs DejaVu Sans glyphs 32-255 into a grayscale atlas, emits build/gen/font_atlas.h with pixel data and Font_Glyph[256] metrics table - xorg: bitmap font atlas text overlay rendering (GL_R8 atlas texture, alpha-blended glyph quads, dark background rect per overlay) - xorg: add xorg_viewer_set_overlay_text / clear_overlays API - xorg: add xorg_viewer_handle_events for streaming use (events only, no redundant render) - xorg_cli: show today's date as white text overlay - v4l2_view_cli: new tool — V4L2 capture with format auto-selection (highest FPS then largest resolution), MJPEG/YUYV, measured FPS overlay - docs: update README, planning, architecture to reflect current status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
137 lines
4.8 KiB
C
137 lines
4.8 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "test_image.h"
|
|
#include "xorg.h"
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"usage: xorg_cli [--pattern bars|ramp|grid]\n"
|
|
" [--width N] [--height N]\n"
|
|
" [--format yuv420|bgra]\n"
|
|
" [--scale stretch|fit|fill|1:1]\n"
|
|
" [--anchor center|topleft]\n"
|
|
" [--x N] [--y N]\n"
|
|
"\n"
|
|
"Opens a window and renders a test image using the xorg viewer sink.\n"
|
|
"Q or Escape closes the window.\n"
|
|
"\n"
|
|
"defaults: bars 1280x720 yuv420 stretch center at 0,0\n");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
Test_Pattern pattern = TEST_PATTERN_BARS;
|
|
Test_Fmt fmt = TEST_FMT_YUV420;
|
|
Xorg_Scale scale = XORG_SCALE_STRETCH;
|
|
Xorg_Anchor anchor = XORG_ANCHOR_CENTER;
|
|
int width = 1280;
|
|
int height = 720;
|
|
int win_x = 0;
|
|
int win_y = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "--pattern") == 0 && i + 1 < argc) {
|
|
i++;
|
|
if (strcmp(argv[i], "bars") == 0) { pattern = TEST_PATTERN_BARS; }
|
|
else if (strcmp(argv[i], "ramp") == 0) { pattern = TEST_PATTERN_RAMP; }
|
|
else if (strcmp(argv[i], "grid") == 0) { pattern = TEST_PATTERN_GRID; }
|
|
else { fprintf(stderr, "unknown pattern: %s\n", argv[i]); usage(); return 1; }
|
|
} else if (strcmp(argv[i], "--width") == 0 && i + 1 < argc) {
|
|
width = atoi(argv[++i]);
|
|
} else if (strcmp(argv[i], "--height") == 0 && i + 1 < argc) {
|
|
height = atoi(argv[++i]);
|
|
} else if (strcmp(argv[i], "--x") == 0 && i + 1 < argc) {
|
|
win_x = atoi(argv[++i]);
|
|
} else if (strcmp(argv[i], "--y") == 0 && i + 1 < argc) {
|
|
win_y = atoi(argv[++i]);
|
|
} else if (strcmp(argv[i], "--scale") == 0 && i + 1 < argc) {
|
|
i++;
|
|
if (strcmp(argv[i], "stretch") == 0) { scale = XORG_SCALE_STRETCH; }
|
|
else if (strcmp(argv[i], "fit") == 0) { scale = XORG_SCALE_FIT; }
|
|
else if (strcmp(argv[i], "fill") == 0) { scale = XORG_SCALE_FILL; }
|
|
else if (strcmp(argv[i], "1:1") == 0) { scale = XORG_SCALE_1_1; }
|
|
else { fprintf(stderr, "unknown scale: %s\n", argv[i]); usage(); return 1; }
|
|
} else if (strcmp(argv[i], "--anchor") == 0 && i + 1 < argc) {
|
|
i++;
|
|
if (strcmp(argv[i], "center") == 0) { anchor = XORG_ANCHOR_CENTER; }
|
|
else if (strcmp(argv[i], "topleft") == 0) { anchor = XORG_ANCHOR_TOP_LEFT; }
|
|
else { fprintf(stderr, "unknown anchor: %s\n", argv[i]); usage(); return 1; }
|
|
} else if (strcmp(argv[i], "--format") == 0 && i + 1 < argc) {
|
|
i++;
|
|
if (strcmp(argv[i], "yuv420") == 0) { fmt = TEST_FMT_YUV420; }
|
|
else if (strcmp(argv[i], "bgra") == 0) { fmt = TEST_FMT_BGRA; }
|
|
else { fprintf(stderr, "unknown format: %s\n", argv[i]); usage(); return 1; }
|
|
} else {
|
|
usage(); return 1;
|
|
}
|
|
}
|
|
|
|
if (!xorg_available()) {
|
|
fprintf(stderr, "xorg_cli: built without HAVE_GLFW — viewer not available\n");
|
|
return 1;
|
|
}
|
|
|
|
if (width < 2 || height < 2) {
|
|
fprintf(stderr, "xorg_cli: width and height must be >= 2\n");
|
|
return 1;
|
|
}
|
|
|
|
Test_Frame *f = test_image_alloc(width, height, fmt);
|
|
if (!f) {
|
|
fprintf(stderr, "xorg_cli: allocation failed\n");
|
|
return 1;
|
|
}
|
|
test_image_generate(f, pattern);
|
|
|
|
const char *pat_name = pattern == TEST_PATTERN_BARS ? "bars"
|
|
: pattern == TEST_PATTERN_RAMP ? "ramp"
|
|
: "grid";
|
|
const char *fmt_name = fmt == TEST_FMT_YUV420 ? "yuv420" : "bgra";
|
|
const char *scale_name = scale == XORG_SCALE_STRETCH ? "stretch"
|
|
: scale == XORG_SCALE_FIT ? "fit"
|
|
: scale == XORG_SCALE_FILL ? "fill"
|
|
: "1:1";
|
|
const char *anchor_name = anchor == XORG_ANCHOR_CENTER ? "center" : "topleft";
|
|
|
|
printf("opening %dx%d %s %s scale=%s anchor=%s at (%d,%d)\n",
|
|
width, height, fmt_name, pat_name, scale_name, anchor_name, win_x, win_y);
|
|
|
|
Xorg_Viewer *v = xorg_viewer_open(win_x, win_y, width, height, "xorg_cli");
|
|
if (!v) {
|
|
fprintf(stderr, "xorg_cli: failed to open viewer window\n");
|
|
test_image_free(f);
|
|
return 1;
|
|
}
|
|
|
|
xorg_viewer_set_scale(v, scale);
|
|
xorg_viewer_set_anchor(v, anchor);
|
|
|
|
/* Overlay: today's date, white text, top-left corner. */
|
|
{
|
|
char date_buf[32];
|
|
time_t now = time(NULL);
|
|
struct tm *tm = localtime(&now);
|
|
strftime(date_buf, sizeof(date_buf), "%Y-%m-%d", tm);
|
|
xorg_viewer_set_overlay_text(v, 0, 10, 10, date_buf, 1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
if (fmt == TEST_FMT_YUV420) {
|
|
xorg_viewer_push_yuv420(v,
|
|
f->plane[0], f->plane[1], f->plane[2],
|
|
f->width, f->height);
|
|
} else {
|
|
xorg_viewer_push_bgra(v, f->plane[0], f->width, f->height);
|
|
}
|
|
|
|
test_image_free(f);
|
|
|
|
while (xorg_viewer_poll(v)) { /* wait for window close */ }
|
|
|
|
xorg_viewer_close(v);
|
|
return 0;
|
|
}
|