Scale modes (STRETCH/FIT/FILL/1:1) with CENTER/TOP_LEFT anchor: - UV crop via u_uv_scale/u_uv_offset uniforms in vertex shader - glViewport sub-rect + glClear for FIT and 1:1 modes - xorg_viewer_set_scale() / xorg_viewer_set_anchor() setters - Stub implementations for both Resize fix: glfwSetWindowUserPointer + framebuffer_size_callback calls render() synchronously during resize so image tracks window edge immediately. Forward declaration added to fix implicit decl error. Q/Escape close the window via key_callback. xorg_cli: --scale and --anchor arguments added. architecture.md: - Scale mode table and anchor docs in Frame Viewer Sink section - Render loop design note: frame-driven not timer-driven, resize callback rationale, threading note (GL context ownership, frame queue) - Text overlay section: tier 1 bitmap atlas (Pillow build tool, skyline packing, quad rendering), tier 2 HarfBuzz+FreeType, migration path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
126 lines
4.5 KiB
C
126 lines
4.5 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.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");
|
|
xorg_viewer_set_scale(v, scale);
|
|
xorg_viewer_set_anchor(v, anchor);
|
|
if (!v) {
|
|
fprintf(stderr, "xorg_cli: failed to open viewer window\n");
|
|
test_image_free(f);
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
}
|