From b7b24205fc2e88278bfc6477c9f3f2eb8c59f369 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Tue, 3 Oct 2023 17:15:43 -0700 Subject: modules/asc: add justify= and [hv]offset= settings justify= now supports "aligned" and "offsetted", justify=aligned being the existing behavior where you would specify halign={left,right,center} and valign={top,bottom,center}. When justify=offsetted is specified however, {valign=,halign=} are unused and instead {hoffset=,voffset=} are expected, as either hoffset=auto/voffset=auto for automagic offsetting according to the x/y coords, or explicit offsetting using -1..+1 normalized fractional values serving as coordinates within the rendered text's rectangle where to anchor the x/y coordinate. By using halign=auto,valign=auto one can carelessly vary the x/y coordinates using the taps (i.e. via rkt) without having to deal with justification concerns (modulo large texts that can't possibly fit), as the offsets will automatically adapt according to the coordinates. --- src/modules/asc/asc.c | 202 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 45 deletions(-) diff --git a/src/modules/asc/asc.c b/src/modules/asc/asc.c index d47ac6d..98455a7 100644 --- a/src/modules/asc/asc.c +++ b/src/modules/asc/asc.c @@ -16,29 +16,31 @@ /* This is intended primarily for diagnostic purposes or as a stand-in you'd eventually * replace with something a more visually interesting font/style. - * - * TODO: - * - Maybe add a dynamic justification mode where the h/v alignment offsets are - * just the inverse of the normalized x/y coordinates. This requires extending - * libs/txt to support precise offsetting when rendering as an alternative to the - * enum'd txt_align_t variant. But it would allow a tapped x/y coordinate user to - * sweep the coordinates edge-to-edge with the text smoothly adjusting its offset - * throughout the sweep so it doesn't extend off-screen at the nearest edge. */ #define ASC_DEFAULT_STRING "Hello rototiller!" +#define ASC_DEFAULT_JUSTIFY ASC_JUSTIFY_ALIGNED #define ASC_DEFAULT_HALIGN TXT_HALIGN_CENTER #define ASC_DEFAULT_VALIGN TXT_VALIGN_CENTER +#define ASC_DEFAULT_HOFFSET "auto" +#define ASC_DEFAULT_VOFFSET "auto" #define ASC_DEFAULT_X 0 #define ASC_DEFAULT_Y 0 +typedef enum asc_justify_t { + ASC_JUSTIFY_ALIGNED, + ASC_JUSTIFY_OFFSETTED, + ASC_JUSITFY_CNT +} asc_justify_t; + typedef struct asc_setup_t { til_setup_t til_setup; const char *string; - txt_halign_t halign; - txt_valign_t valign; + asc_justify_t justify; + txt_halign_t halign, valign; + float hoffset, voffset; float x, y; } asc_setup_t; @@ -47,13 +49,16 @@ typedef struct asc_context_t { struct { til_tap_t x, y; + til_tap_t hoffset, voffset; } taps; struct { float x, y; + float hoffset, voffset; } vars; float *x, *y; + float *hoffset, *voffset; txt_t *txt; } asc_context_t; @@ -71,9 +76,22 @@ static void asc_update_taps(asc_context_t *ctxt, til_stream_t *stream) else ctxt->vars.y = *ctxt->y; - /* XXX: maybe clamp to -1.0..+1.0 ? It's not a crash risk since txt_render_fragment() + /* XXX: maybe clamp to -1.0..+1.0 ? It's not a crash risk since txt_render_fragment_aligned() * clips to the fragment by using ...put_pixel_checked() *shrug* */ + + if (((asc_setup_t *)ctxt->til_module_context.setup)->justify != ASC_JUSTIFY_OFFSETTED) + return; + + if (!til_stream_tap_context(stream, &ctxt->til_module_context, NULL, &ctxt->taps.hoffset)) + *ctxt->hoffset = ((asc_setup_t *)ctxt->til_module_context.setup)->hoffset; + else + ctxt->vars.hoffset = *ctxt->hoffset; + + if (!til_stream_tap_context(stream, &ctxt->til_module_context, NULL, &ctxt->taps.voffset)) + *ctxt->voffset = ((asc_setup_t *)ctxt->til_module_context.setup)->voffset; + else + ctxt->vars.voffset = *ctxt->voffset; } @@ -92,6 +110,11 @@ static til_module_context_t * asc_create_context(const til_module_t *module, til ctxt->taps.x = til_tap_init_float(ctxt, &ctxt->x, 1, &ctxt->vars.x, "x"); ctxt->taps.y = til_tap_init_float(ctxt, &ctxt->y, 1, &ctxt->vars.y, "y"); + if (((asc_setup_t *)setup)->justify == ASC_JUSTIFY_OFFSETTED) { + ctxt->taps.hoffset = til_tap_init_float(ctxt, &ctxt->hoffset, 1, &ctxt->vars.hoffset, "hoffset"); + ctxt->taps.voffset = til_tap_init_float(ctxt, &ctxt->voffset, 1, &ctxt->vars.voffset, "voffset"); + } + asc_update_taps(ctxt, stream); return &ctxt->til_module_context; @@ -108,13 +131,35 @@ static void asc_render_fragment(til_module_context_t *context, til_stream_t *str til_fb_fragment_clear(fragment); - txt_render_fragment_aligned(ctxt->txt, fragment, 0xffffffff, - ctxt->vars.x * ((float)fragment->frame_width) * .5f + .5f * ((float)fragment->frame_width), - ctxt->vars.y * ((float)fragment->frame_height) * .5f + .5f * ((float)fragment->frame_height), - (txt_align_t){ - .horiz = s->halign, - .vert = s->valign - }); + switch (s->justify) { + case ASC_JUSTIFY_ALIGNED: + return txt_render_fragment_aligned(ctxt->txt, fragment, 0xffffffff, + ctxt->vars.x * ((float)fragment->frame_width) * .5f + .5f * ((float)fragment->frame_width), + ctxt->vars.y * ((float)fragment->frame_height) * .5f + .5f * ((float)fragment->frame_height), + (txt_align_t){ + .horiz = s->halign, + .vert = s->valign + }); + + case ASC_JUSTIFY_OFFSETTED: { + float hoffset = ctxt->vars.hoffset, + voffset = ctxt->vars.voffset; + + if (isnan(hoffset)) + hoffset = ctxt->vars.x; + + if (isnan(voffset)) + voffset = ctxt->vars.y; + + return txt_render_fragment_offsetted(ctxt->txt, fragment, 0xffffffff, + ctxt->vars.x * ((float)fragment->frame_width) * .5f + .5f * ((float)fragment->frame_width), + ctxt->vars.y * ((float)fragment->frame_height) * .5f + .5f * ((float)fragment->frame_height), + hoffset, voffset); + } + + default: + assert(0); + } } @@ -146,9 +191,15 @@ til_module_t asc_module = { static int asc_setup(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 *string; - til_setting_t *valign; - til_setting_t *halign; + til_setting_t *justify; + til_setting_t *valign, *voffset; + til_setting_t *halign, *hoffset; til_setting_t *x, *y; + const char *justify_values[] = { + "aligned", + "offsetted", + NULL + }; const char *valign_values[] = { "center", "top", @@ -178,33 +229,78 @@ static int asc_setup(const til_settings_t *settings, til_setting_t **res_setting r = til_settings_get_and_describe_setting(settings, &(til_setting_spec_t){ - .name = "Vertical alignment", - .key = "valign", + .name = "Justification", + .key = "justify", /* .regex = "" TODO */ - .preferred = valign_values[ASC_DEFAULT_VALIGN], - .values = valign_values, + .preferred = justify_values[ASC_DEFAULT_JUSTIFY], + .values = justify_values, .annotations = NULL }, - &valign, + &justify, res_setting, res_desc); if (r) return r; - r = til_settings_get_and_describe_setting(settings, - &(til_setting_spec_t){ - .name = "Horizontal alignment", - .key = "halign", - /* .regex = "" TODO */ - .preferred = halign_values[ASC_DEFAULT_HALIGN], - .values = halign_values, - .annotations = NULL - }, - &halign, - res_setting, - res_desc); - if (r) - return r; + if (!strcasecmp(justify->value, justify_values[ASC_JUSTIFY_ALIGNED])) { + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Vertical alignment", + .key = "valign", + /* .regex = "" TODO */ + .preferred = valign_values[ASC_DEFAULT_VALIGN], + .values = valign_values, + .annotations = NULL + }, + &valign, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Horizontal alignment", + .key = "halign", + /* .regex = "" TODO */ + .preferred = halign_values[ASC_DEFAULT_HALIGN], + .values = halign_values, + .annotations = NULL + }, + &halign, + res_setting, + res_desc); + if (r) + return r; + } else { + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Vertical offset [-1.0...1.0] or 'auto'", + .key = "voffset", + /* .regex = "" TODO */ + .preferred = ASC_DEFAULT_VOFFSET, + .annotations = NULL + }, + &voffset, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Horizontal offset [-1.0...1.0] or 'auto'", + .key = "hoffset", + /* .regex = "" TODO */ + .preferred = ASC_DEFAULT_HOFFSET, + .annotations = NULL + }, + &hoffset, + res_setting, + res_desc); + if (r) + return r; + } r = til_settings_get_and_describe_setting(settings, &(til_setting_spec_t){ @@ -245,13 +341,29 @@ static int asc_setup(const til_settings_t *settings, til_setting_t **res_setting if (!setup->string) return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, string, res_setting, -ENOMEM); - r = til_value_to_pos(halign_values, halign->value, (unsigned *)&setup->halign); - if (r < 0) - return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, halign, res_setting, -EINVAL); - - r = til_value_to_pos(valign_values, valign->value, (unsigned *)&setup->valign); + r = til_value_to_pos(justify_values, justify->value, (unsigned *)&setup->justify); if (r < 0) - return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, valign, res_setting, -EINVAL); + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, justify, res_setting, -EINVAL); + + if (setup->justify == ASC_JUSTIFY_ALIGNED) { + r = til_value_to_pos(halign_values, halign->value, (unsigned *)&setup->halign); + if (r < 0) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, halign, res_setting, -EINVAL); + + r = til_value_to_pos(valign_values, valign->value, (unsigned *)&setup->valign); + if (r < 0) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, valign, res_setting, -EINVAL); + } else { + if (!strcasecmp(hoffset->value, "auto")) + setup->hoffset = NAN; + else if (sscanf(hoffset->value, "%f", &setup->hoffset) != 1) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, hoffset, res_setting, -EINVAL); + + if (!strcasecmp(voffset->value, "auto")) + setup->voffset = NAN; + else if (sscanf(voffset->value, "%f", &setup->voffset) != 1) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, voffset, res_setting, -EINVAL); + } if (sscanf(x->value, "%f", &setup->x) != 1) return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, x, res_setting, -EINVAL); -- cgit v1.2.3