From 759fa0d0ab46d3bc254500a8ac751c305d7e5a2b Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 6 Nov 2025 07:50:59 -0800 Subject: vcr: vcr_draw_text() horiz wrapping support This is in preparation for use cases drawing text into graph layers where we unwrap the layer while presenting... the text needs to gracefully handle wrapping around the layer's left/right edges. --- src/vcr.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 38 deletions(-) (limited to 'src/vcr.c') diff --git a/src/vcr.c b/src/vcr.c index 93155be..0a0f0d8 100644 --- a/src/vcr.c +++ b/src/vcr.c @@ -927,6 +927,56 @@ int vcr_resize_visible(vcr_t *vcr, int width, int height) } + +static void vcr_mem_draw_text(vcr_t *vcr, vcr_layer_t layer, int x, int row, const vcr_str_t *strs, int n_strs) +{ + int y = row * VCR_ROW_HEIGHT + 3; + uint8_t mask = (0x1 << layer); + + for (int i = 0; i < n_strs && x < vcr->width; i++) { + unsigned char c; + + x += 4; /* match the delta used w/XDrawText */ + + for (int j = 0, n = 0; j < strs[i].len; j++) { + int w = ASCII_WIDTH; + + c = strs[i].str[j]; + + /* skip weird/non-printable chars */ + if (c < ' ' || c > '~') + continue; + + if (n > 0) + x += 1; + + if (x >= vcr->width) { + x = vcr->width; + break; + } + + if (x + w >= vcr->width) + w = vcr->width - x; + + for (int k = 0; k < ASCII_HEIGHT; k++) { + for (int l = 0; l < w; l++) { + int x_l = x + l; + uint8_t *p = &vcr->mem.bits[(y + k) * vcr->mem.pitch + (x_l >> 1)]; + + /* FIXME this can all be done more efficiently */ + if (x_l < 0) + continue; + + *p = (*p & ~(mask << ((x_l & 0x1) << 2))) | ((mask * ascii_chars[c][k * ASCII_WIDTH + l]) << ((x_l & 0x1) << 2)); + } + } + + x += ASCII_WIDTH; + n++; + } + } +} + /* this is inspired by XDrawText and its XTextItem *items + nr_items API, * primarily so it's easy to map the incoming call to an XDrawText call... * but for non-xlib backends, an XDrawText equivalent will be needed. @@ -975,9 +1025,47 @@ void vcr_draw_text(vcr_t *vcr, vcr_layer_t layer, vcr_text_flags_t flags, int x, } if (row >= 0) { - XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, - x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ - items, n_strs); + switch (flags) { + case VCR_TEXT_FLAGS_CLIPPED: + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + break; + + case VCR_TEXT_FLAGS_WRAPPED: { + int width = 0; + + for (int i = 0; i < n_strs; i++) + width += XTextWidth(vcr->backend->xlib.chart_font, items[i].chars, items[i].nchars) + items[i].delta; + + if (x < 0) { + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + vcr->width + x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + + if (x + width > 0) { + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + } + } else if (x + width > vcr->width) { + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + -(vcr->width - x), (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + } else { + XDrawText(vcr->backend->xlib.xserver->display, vcr->xlib.text_pixmap, vcr->backend->xlib.text_gc, + x, (row + 1) * VCR_ROW_HEIGHT - 3, /* dst x, y */ + items, n_strs); + } + break; + } + + default: + assert(0); + } } /* if the caller wants to know the width, compute it, it's dumb that XDrawText doesn't @@ -997,45 +1085,35 @@ void vcr_draw_text(vcr_t *vcr, vcr_layer_t layer, vcr_text_flags_t flags, int x, case VCR_BACKEND_TYPE_MEM: { if (row >= 0 && (row + 1) * VCR_ROW_HEIGHT < vcr->height) { - int y = row * VCR_ROW_HEIGHT + 3; - uint8_t mask = (0x1 << layer); - - for (int i = 0; i < n_strs && x < vcr->width; i++) { - unsigned char c; - - x += 4; /* match the delta used w/XDrawText */ - - for (int j = 0, n = 0; j < strs[i].len; j++) { - c = strs[i].str[j]; - - /* skip weird/non-printable chars */ - if (c < ' ' || c > '~') - continue; - - if (n > 0) - x += 1; - - if (x + ASCII_WIDTH >= vcr->width) { - x = vcr->width; - break; - } - - for (int k = 0; k < ASCII_HEIGHT; k++) { - for (int l = 0; l < ASCII_WIDTH; l++) { - int x_l = x + l; - uint8_t *p = &vcr->mem.bits[(y + k) * vcr->mem.pitch + (x_l >> 1)]; + switch (flags) { + case VCR_TEXT_FLAGS_CLIPPED: + vcr_mem_draw_text(vcr, layer, x, row, strs, n_strs); + break; + case VCR_TEXT_FLAGS_WRAPPED: { + int w = 0; + /* assume fixed 5x11 ascii glyphs */ + for (int i = 0; i < n_strs; i++) { + w += 4; /* match the delta used w/XDrawText */ - /* FIXME this can all be done more efficiently */ - if (x_l < 0) - continue; + w += strs[i].len * (ASCII_WIDTH + 1); + } - *p = (*p & ~(mask << ((x_l & 0x1) << 2))) | ((mask * ascii_chars[c][k * ASCII_WIDTH + l]) << ((x_l & 0x1) << 2)); - } - } + if (x < 0) { + vcr_mem_draw_text(vcr, layer, vcr->width + x, row, strs, n_strs); - x += ASCII_WIDTH; - n++; + if (x + w > 0) + vcr_mem_draw_text(vcr, layer, x, row, strs, n_strs); + } else if (x + w > vcr->width) { + vcr_mem_draw_text(vcr, layer, -(vcr->width - x), row, strs, n_strs); + vcr_mem_draw_text(vcr, layer, x, row, strs, n_strs); + } else { + vcr_mem_draw_text(vcr, layer, x, row, strs, n_strs); } + + break; + } + default: + assert(0); } } -- cgit v1.2.3