diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2019-11-16 19:37:22 -0800 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2019-11-16 19:37:22 -0800 |
commit | a6f43bc59fc1292060081d5a4837f5679e1b186f (patch) | |
tree | 20e87bf064a67473a81bedc64a1ef6bd4667ee21 /src/libs | |
parent | 7bbb0fa20ff65a05031886c35646248dc94a38a8 (diff) |
libs/txt: add minimal ascii text renderer
This is as basic as it gets, the only fanciness is it recognizes
newlines and supports horizontal and vertical justification.
As this is intended to be run from potentially threaded fragmenter
renderers, it receives a fragment and *frame* coordinates for the
text to be rendered. If the text doesn't land in the given fragment,
nothing gets drawn.
Currently this is not optimized at all. There's a stubbed out rect
overlap test function which could be used to avoid entering the
text rendering loop for fragments with zero overlap, that's an obvious
low-hanging fruit optimization. After that, skipping characters
that don't overlap would be another obvious thing.
As-is the text render loop is always entered and the bounds-checked
put pixel helper is used. So every fragment will incur the cost of
rendering the full string, even when it's not visible.
For the rtv captions this isn't a particularly huge deal, but stuff
to improve upon in the future.
Diffstat (limited to 'src/libs')
-rw-r--r-- | src/libs/Makefile.am | 2 | ||||
-rw-r--r-- | src/libs/txt/Makefile.am | 3 | ||||
-rw-r--r-- | src/libs/txt/txt.c | 200 | ||||
-rw-r--r-- | src/libs/txt/txt.h | 33 |
4 files changed, 237 insertions, 1 deletions
diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am index 48fb649..e0895ce 100644 --- a/src/libs/Makefile.am +++ b/src/libs/Makefile.am @@ -1 +1 @@ -SUBDIRS = ascii grid ray +SUBDIRS = ascii grid ray txt diff --git a/src/libs/txt/Makefile.am b/src/libs/txt/Makefile.am new file mode 100644 index 0000000..88aa79b --- /dev/null +++ b/src/libs/txt/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libtxt.a +libtxt_a_SOURCES = txt.c txt.h +libtxt_a_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs diff --git a/src/libs/txt/txt.c b/src/libs/txt/txt.c new file mode 100644 index 0000000..e42d8c2 --- /dev/null +++ b/src/libs/txt/txt.c @@ -0,0 +1,200 @@ +#include <assert.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "fb.h" + +#include "ascii/ascii.h" +#include "txt.h" + + +struct txt_t { + int len, width, height; + char str[]; +}; + + +/* compute the rectangle dimensions of the string in rendered pixels */ +static void measure_str(const char *str, int *res_width, int *res_height) +{ + int rows = 1, cols = 0, col = 0; + char c; + + assert(str); + assert(res_width); + assert(res_height); + + while (c = *str) { + switch (c) { + case ' '...'~': + col++; + break; + + case '\n': + if (col > cols) + cols = col; + col = 0; + rows++; + break; + + default: + break; + } + str++; + } + + *res_height = 1 + rows * (ASCII_HEIGHT + 1); + *res_width = 1 + cols * (ASCII_WIDTH + 1); +} + + +txt_t * txt_new(const char *str) +{ + txt_t *txt; + int len; + + assert(str); + + len = strlen(str); + + txt = calloc(1, sizeof(txt_t) + len + 1); + if (!txt) + return NULL; + + txt->len = len; + memcpy(txt->str, str, len); + + measure_str(txt->str, &txt->width, &txt->height); + + return txt; +} + + +txt_t * txt_newf(const char *fmt, ...) +{ + char buf[1024] = {}; /* XXX: it's not expected there will be long strings */ + va_list ap; + + assert(fmt); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + return txt_new(buf); +} + + +txt_t * txt_free(txt_t *txt) +{ + free(txt); + + return NULL; +} + + +/* Adjusts x and y according to alignment, width, and height. Returning the adjusted x and y + * in res_x, res_y. + * + * res_x,res_y will be the coordinate of the upper left corner of the rect + * described by width,height when aligned relative to x,y according to the + * specified alignment. + * + * e.g. if an alignment of TXT_HALIGN_LEFT,TXT_VALIGN_TOP is supplied, x,y is returned verbatim + * in res_x,res_y. + * an alignment of TXT_HALIGN_CENTER,TXT_VALIGN_CENTER returns x-width/2 and y-width/2. + */ +static void justify(txt_align_t alignment, int x, int y, unsigned width, unsigned height, int *res_x, int *res_y) +{ + assert(res_x); + assert(res_y); + + switch (alignment.horiz) { + case TXT_HALIGN_CENTER: + x -= width >> 1; + break; + + case TXT_HALIGN_LEFT: + break; + + case TXT_HALIGN_RIGHT: + x -= width; + break; + + default: + assert(0); + } + + switch (alignment.vert) { + case TXT_VALIGN_CENTER: + y -= height >> 1; + break; + + case TXT_VALIGN_TOP: + break; + + case TXT_VALIGN_BOTTOM: + y -= height; + break; + + default: + assert(0); + } + + *res_x = x; + *res_y = y; +} + + +static int overlaps(int x1, int y1, unsigned w1, unsigned h1, int x2, int y2, unsigned w2, unsigned h2) +{ + /* TODO */ + return 1; +} + + +static inline void draw_char(fb_fragment_t *fragment, uint32_t color, int x, int y, char c) +{ + /* TODO: this could be optimized to skip characters with no overlap */ + for (int i = 0; i < ASCII_HEIGHT; i++) { + for (int j = 0; j < ASCII_WIDTH; j++) { + if (ascii_chars[c][i * ASCII_WIDTH + j]) + fb_fragment_put_pixel_checked(fragment, x + j, y + i, color); + } + } +} + + +void txt_render_fragment(txt_t *txt, fb_fragment_t *fragment, uint32_t color, int x, int y, txt_align_t alignment) +{ + int jx, jy, col, row; + char c, *str; + + assert(txt); + assert(fragment); + + justify(alignment, x, y, txt->width, txt->height, &jx, &jy); + + if (!overlaps(jx, jy, txt->width, txt->height, + fragment->x, fragment->y, + fragment->width, fragment->height)) + return; + + + for (col = 0, row = 0, str = txt->str; *str; str++) { + switch (*str) { + case ' '...'~': + draw_char(fragment, color, jx + 1 + col * (ASCII_WIDTH + 1), jy + 1 + row * (ASCII_HEIGHT + 1), *str); + col++; + break; + + case '\n': + col = 0; + row++; + break; + + default: + break; + } + } +} diff --git a/src/libs/txt/txt.h b/src/libs/txt/txt.h new file mode 100644 index 0000000..8c68e30 --- /dev/null +++ b/src/libs/txt/txt.h @@ -0,0 +1,33 @@ +#ifndef _TXT_H +#define _TXT_H + +#include <stdint.h> + +typedef struct fb_fragment_t fb_fragment_t; +typedef struct txt_t txt_t; + +typedef enum txt_halign_t { + TXT_HALIGN_CENTER, + TXT_HALIGN_LEFT, + TXT_HALIGN_RIGHT, + TXT_HALIGN_CNT, +} txt_halign_t; + +typedef enum txt_valign_t { + TXT_VALIGN_CENTER, + TXT_VALIGN_TOP, + TXT_VALIGN_BOTTOM, + TXT_VALIGN_CNT, +} txt_valign_t; + +typedef struct txt_align_t { + txt_halign_t horiz; + txt_valign_t vert; +} txt_align_t; + +txt_t * txt_new(const char *str); +txt_t * txt_newf(const char *fmt, ...); +txt_t * txt_free(txt_t *txt); +void txt_render_fragment(txt_t *txt, fb_fragment_t *fragment, uint32_t color, int x, int y, txt_align_t alignment); + +#endif |