From 878514e85a987f267250838398d4e27e44016ec5 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 30 Nov 2023 11:50:26 -0800 Subject: til,main: introduce ratio= --video setting First stab at supporting explicit aspect ratios. This performs the adjustment when needed in til_fb so it's automatically applied to all fb backends. The syntax is ratio=W:H with ratio=full being special cased for when no aspect ratio adjustment is desired (just use whatever the fb page dimensions are, usually specified via size= in the fb backend's settings, or display native res when fullscreen=on) For now when an aspect ratio is specified it always fits the content within the alotted space... there is no full-but-ratio-preserved-by-clipping-if-needed variant like widescreen TVs often have. --- src/main.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++----- src/til_fb.c | 59 +++++++++++++++++++++++++++++++++++++++++-- src/til_video_setup.h | 2 ++ 3 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index df2d219..28f897f 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "til.h" #include "til_args.h" @@ -24,7 +25,7 @@ /* Copyright (C) 2016 Vito Caputo */ -#define NUM_FB_PAGES 3 +#define NUM_FB_PAGES 3 /* ^ By triple-buffering, we can have a page tied up being displayed, another * tied up submitted and waiting for vsync, and still not block on getting * another page so we can begin rendering another frame before vsync. With @@ -33,30 +34,33 @@ #ifndef DEFAULT_VIDEO #ifdef HAVE_SDL -#define DEFAULT_VIDEO "sdl" +#define DEFAULT_VIDEO "sdl" #endif #endif #ifndef DEFAULT_VIDEO #ifdef HAVE_DRM -#define DEFAULT_VIDEO "drm" +#define DEFAULT_VIDEO "drm" #endif #endif #ifndef DEFAULT_VIDEO -#define DEFAULT_VIDEO "mem" +#define DEFAULT_VIDEO "mem" #endif +#define DEFAULT_VIDEO_RATIO "full" + #ifndef DEFAULT_AUDIO #ifdef HAVE_SDL -#define DEFAULT_AUDIO "sdl" +#define DEFAULT_AUDIO "sdl" #endif #endif #ifndef DEFAULT_AUDIO -#define DEFAULT_AUDIO "mem" +#define DEFAULT_AUDIO "mem" #endif + extern til_fb_ops_t drm_fb_ops; extern til_fb_ops_t mem_fb_ops; extern til_fb_ops_t sdl_fb_ops; @@ -153,6 +157,33 @@ static int setup_audio(const til_settings_t *settings, til_setting_t **res_setti static int setup_video(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) { til_setting_t *setting; + til_setting_t *ratio; + const char *ratio_values[] = { + "full", + "1:1", + "4:3", + "3:2", + "16:10", + "5:3", + "16:9", + "2:1", + "21:9", + "32:9", + NULL + }; + const char *ratio_annotations[] = { + "Fill fb with content, inheriting its ratio as-is (may stretch)", + "Square", + "CRT Monitor/TV (VGA/XGA)", + "35mm film, iphone", + "Widescreen monitor (WXGA)", + "Super 16mm film", + "Widescreen TV / newer laptops", + "Dominoes", + "Ultra-widescreen", + "Super ultra-widescreen", + NULL + }; const char *video; int r; @@ -188,6 +219,21 @@ static int setup_video(const til_settings_t *settings, til_setting_t **res_setti return 1; } + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Content aspect ratio (W:H)", + .key = "ratio", + .preferred = DEFAULT_VIDEO_RATIO, + .values = ratio_values, + .annotations = ratio_annotations, + }, + &ratio, + res_setting, + res_desc); + if (r) + return r; + + /* XXX: this is kind of hacky for now */ #ifdef HAVE_DRM if (!strcasecmp(video, "drm")) @@ -215,6 +261,17 @@ static int setup_video(const til_settings_t *settings, til_setting_t **res_setti if (r) return r; + if (!strcasecmp(ratio->value, "full")) + setup->ratio = NAN; + else { + float w, h; + + if (sscanf(ratio->value, "%f:%f", &w, &h) != 2) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, ratio, res_setting, -EINVAL); + + setup->ratio = w / h; + } + *res_setup = &setup->til_setup; } diff --git a/src/til_fb.c b/src/til_fb.c index 3c19ae5..cc07909 100644 --- a/src/til_fb.c +++ b/src/til_fb.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -126,6 +127,7 @@ typedef struct til_fb_t { const til_fb_ops_t *ops; void *ops_context; int n_pages; + float ratio; pthread_mutex_t rebuild_mutex; int rebuild_pages; /* counter of pages needing a rebuild */ @@ -157,6 +159,48 @@ typedef struct til_fb_t { static _til_fb_page_t * _til_fb_page_alloc(til_fb_t *fb); +/* apply an aspect ratio to a fragment's frame, + * if ratio=NAN then the fragment is used as-is (e.g. for "ratio=full"). + */ +static _til_fb_page_t * _til_fb_page_apply_ratio(_til_fb_page_t *page) +{ + til_fb_fragment_t *fragment; + float fragment_ratio, d; + float ratio; + + assert(page); + assert(page->fb); + + ratio = page->fb->ratio; + fragment = &page->fragment.public; + + assert(fragment->frame_width > 0 && fragment->frame_height > 0); + assert(ratio > 0.f); + + if (isnan(ratio)) + return page; + + fragment_ratio = (float)fragment->frame_width / (float)fragment->frame_height; + d = fragment_ratio - ratio; + if (d < 0) { /* letterboxed, scale height */ + unsigned h = fragment->frame_width / ratio; + + fragment->buf += ((fragment->frame_height - h) / 2) * fragment->pitch; + fragment->frame_height = h; + fragment->height = h; + } else if (d > 0) { /* pillarboxed, scale width */ + unsigned w = fragment->frame_height * ratio; + + fragment->buf += ((fragment->frame_width - w) / 2); + fragment->stride += fragment->frame_width - w; + fragment->frame_width = w; + fragment->width = w; + } + + return page; +} + + /* Consumes ready pages queued via til_fb_page_put(), submits them to drm to flip * on vsync. Produces inactive pages from those replaced, making them * available to til_fb_page_get(). */ @@ -205,6 +249,8 @@ int til_fb_flip(til_fb_t *fb) fb->ops->page_free(fb, fb->ops_context, p->fb_ops_page); p->fb_ops_page = fb->ops->page_alloc(fb, fb->ops_context, &p->fragment.public); p->fragment.public.ops = &p->fragment.ops; + _til_fb_page_apply_ratio(p); + fb->rebuild_pages--; } pthread_mutex_unlock(&fb->rebuild_mutex); @@ -366,7 +412,7 @@ static _til_fb_page_t * _til_fb_page_alloc(til_fb_t *fb) else fb->all_pages_tail = page; - return page; + return _til_fb_page_apply_ratio(page); } @@ -561,7 +607,7 @@ int til_fb_new(const til_fb_ops_t *ops, const char *title, const til_video_setup assert(ops->page_alloc); assert(ops->page_free); assert(ops->page_flip); - assert(!setup && setup->til_setup.creator == ops); + assert(setup && setup->til_setup.creator == ops); assert(n_pages > 1); assert(res_fb); @@ -580,6 +626,15 @@ int til_fb_new(const til_fb_ops_t *ops, const char *title, const til_video_setup goto fail; } + /* TODO: fb should probably just take a reference on the setup, + * the whole distinction of the ops-specific setup and intermediate + * fb-common setup needs clarification, it's rather muddy right now esp. + * with the advent of til_video_setup_t to facilitate a common ratio= + * setting... with the backend allocating the whole setup, when it only + * performs setup of its own little subset. This all needs some tidying up + * now that things have evolved so much on the video/fb side. + */ + fb->ratio = setup->ratio; pthread_mutex_init(&fb->ready_mutex, NULL); pthread_cond_init(&fb->ready_cond, NULL); pthread_mutex_init(&fb->inactive_mutex, NULL); diff --git a/src/til_video_setup.h b/src/til_video_setup.h index 21c5844..f774c6f 100644 --- a/src/til_video_setup.h +++ b/src/til_video_setup.h @@ -5,6 +5,8 @@ typedef struct til_video_setup_t { til_setup_t til_setup; + + float ratio; } til_video_setup_t; #endif -- cgit v1.2.1