video: truetype: Support a limit on the width of a line

Expo needs to be able to word-wrap lines so that they are displayed as
the user expects. Add a limit on the width of each line and support this
in the measurement algorithm.

Add a log category to truetype while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2025-04-02 06:29:39 +13:00
committed by Tom Rini
parent 030e53aaaa
commit cdd095e48a
5 changed files with 100 additions and 17 deletions

View File

@@ -298,7 +298,7 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
}
ret = vidconsole_measure(scn->expo->cons, txt->font_name,
txt->font_size, str, &bbox, NULL);
txt->font_size, str, -1, &bbox, NULL);
if (ret)
return log_msg_ret("mea", ret);
if (widthp)

View File

@@ -3,6 +3,8 @@
* Copyright (c) 2016 Google, Inc
*/
#define LOG_CATEGORY UCLASS_VIDEO
#include <abuf.h>
#include <dm.h>
#include <log.h>
@@ -733,16 +735,17 @@ static int truetype_select_font(struct udevice *dev, const char *name,
}
static int truetype_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox,
struct alist *lines)
const char *text, int pixel_limit,
struct vidconsole_bbox *bbox, struct alist *lines)
{
struct console_tt_metrics *met;
struct vidconsole_mline mline;
const char *s;
const char *s, *last_space;
int width, last_width;
stbtt_fontinfo *font;
int lsb, advance;
int width;
int start;
int limit;
int lastch;
int ret;
@@ -754,14 +757,30 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
if (!*text)
return 0;
limit = -1;
if (pixel_limit != -1)
limit = tt_ceil((double)pixel_limit / met->scale);
font = &met->font;
width = 0;
bbox->y1 = 0;
bbox->x1 = 0;
start = 0;
last_space = NULL;
last_width = 0;
for (lastch = 0, s = text; *s; s++) {
int neww;
int ch = *s;
if (ch == ' ') {
/*
* store the position and width so we can use it again
* if we need to word-wrap
*/
last_space = s;
last_width = width;
}
/* First get some basic metrics about this character */
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
neww = width + advance;
@@ -772,10 +791,16 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
lastch = ch;
/* see if we need to start a new line */
if (ch == '\n') {
if (ch == '\n' || (limit != -1 && neww >= limit)) {
if (ch != '\n' && last_space) {
s = last_space;
width = last_width;
}
last_space = NULL;
mline.bbox.x0 = 0;
mline.bbox.y0 = bbox->y1;
mline.bbox.x1 = tt_ceil((double)width * met->scale);
bbox->x1 = max(bbox->x1, mline.bbox.x1);
bbox->y1 += met->font_size;
mline.bbox.y1 = bbox->y1;
mline.bbox.valid = true;
@@ -788,8 +813,7 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
mline.start, mline.len, mline.len, text + mline.start);
start = s - text;
if (ch == '\n')
start++;
start++;
lastch = 0;
neww = 0;
}
@@ -811,7 +835,7 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
bbox->valid = true;
bbox->x0 = 0;
bbox->y0 = 0;
bbox->x1 = tt_ceil((double)width * met->scale);
bbox->x1 = max(bbox->x1, mline.bbox.x1);
return 0;
}

View File

@@ -608,8 +608,8 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size)
}
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox,
struct alist *lines)
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines)
{
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
struct vidconsole_ops *ops = vidconsole_get_ops(dev);
@@ -618,7 +618,7 @@ int vidconsole_measure(struct udevice *dev, const char *name, uint size,
if (ops->measure) {
if (lines)
alist_empty(lines);
ret = ops->measure(dev, name, size, text, bbox, lines);
ret = ops->measure(dev, name, size, text, limit, bbox, lines);
if (ret != -ENOSYS)
return ret;
}

View File

@@ -250,6 +250,7 @@ struct vidconsole_ops {
* @name: Font name to use (NULL to use default)
* @size: Font size to use (0 to use default)
* @text: Text to measure
* @limit: Width limit for each line, or -1 if none
* @bbox: Returns bounding box of text, assuming it is positioned
* at 0,0
* @lines: If non-NULL, this must be an alist of
@@ -259,8 +260,8 @@ struct vidconsole_ops {
* Returns: 0 on success, -ENOENT if no such font
*/
int (*measure)(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox,
struct alist *lines);
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines);
/**
* nominal() - Measure the expected width of a line of text
@@ -350,6 +351,7 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size);
* @name: Font name to use (NULL to use default)
* @size: Font size to use (0 to use default)
* @text: Text to measure
* @limit: Width limit for each line, or -1 if none
* @bbox: Returns bounding box of text, assuming it is positioned
* at 0,0
* @lines: If non-NULL, this must be an alist of
@@ -359,8 +361,8 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size);
* Returns: 0 on success, -ENOENT if no such font
*/
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox,
struct alist *lines);
const char *text, int limit,
struct vidconsole_bbox *bbox, struct alist *lines);
/**
* vidconsole_nominal() - Measure the expected width of a line of text
*

View File

@@ -789,6 +789,7 @@ static int dm_test_font_measure(struct unit_test_state *uts)
struct vidconsole_bbox bbox;
struct video_priv *priv;
struct udevice *dev, *con;
const int limit = 0x320;
struct alist lines;
int nl;
@@ -801,7 +802,7 @@ static int dm_test_font_measure(struct unit_test_state *uts)
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_position_cursor(con, 0, 0);
alist_init_struct(&lines, struct vidconsole_mline);
ut_assertok(vidconsole_measure(con, NULL, 0, test_string, &bbox,
ut_assertok(vidconsole_measure(con, NULL, 0, test_string, -1, &bbox,
&lines));
ut_asserteq(0, bbox.x0);
ut_asserteq(0, bbox.y0);
@@ -831,6 +832,62 @@ static int dm_test_font_measure(struct unit_test_state *uts)
ut_asserteq(163, line->len);
ut_asserteq(strlen(test_string + nl + 1), line->len);
/* now use a limit on the width */
ut_assertok(vidconsole_measure(con, NULL, 0, test_string, limit, &bbox,
&lines));
ut_asserteq(0, bbox.x0);
ut_asserteq(0, bbox.y0);
ut_asserteq(0x31e, bbox.x1);
ut_asserteq(0x36, bbox.y1);
ut_asserteq(3, lines.count);
nl = strchr(test_string, '\n') - test_string;
line = alist_get(&lines, 0, struct vidconsole_mline);
ut_assertnonnull(line);
ut_asserteq(0, line->bbox.x0);
ut_asserteq(0, line->bbox.y0);
ut_asserteq(0x8c, line->bbox.x1);
ut_asserteq(0x12, line->bbox.y1);
ut_asserteq(0, line->start);
ut_asserteq(20, line->len);
ut_asserteq(nl, line->len);
printf("line0 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("There is always much",
test_string + line->start);
line++;
ut_asserteq(0x0, line->bbox.x0);
ut_asserteq(0x12, line->bbox.y0);
ut_asserteq(0x31e, line->bbox.x1);
ut_asserteq(0x24, line->bbox.y1);
ut_asserteq(21, line->start);
ut_asserteq(nl + 1, line->start);
ut_asserteq(129, line->len);
printf("line1 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("to be said for not attempting more than you can do "
"and for making a certainty of what you try. But this "
"principle, like others in",
test_string + line->start);
line++;
ut_asserteq(0x0, line->bbox.x0);
ut_asserteq(0x24, line->bbox.y0);
ut_asserteq(0xc8, line->bbox.x1);
ut_asserteq(0x36, line->bbox.y1);
ut_asserteq(21 + 130, line->start);
ut_asserteq(33, line->len);
printf("line2 '%.*s'\n", line->len, test_string + line->start);
ut_asserteq_strn("life and war, has its exceptions.",
test_string + line->start);
/*
* all characters should be accounted for, except the newline and the
* space which is consumed in the wordwrap
*/
ut_asserteq(strlen(test_string) - 2,
line[-2].len + line[-1].len + line->len);
return 0;
}
DM_TEST(dm_test_font_measure, UTF_SCAN_FDT);