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 <s...@chromium.org> --- boot/scene.c | 2 +- drivers/video/console_truetype.c | 40 ++++++++++++++++----- drivers/video/vidconsole-uclass.c | 6 ++-- include/video_console.h | 10 +++--- test/dm/video.c | 59 ++++++++++++++++++++++++++++++- 5 files changed, 100 insertions(+), 17 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index d3ae5816bef..fb82ffe768c 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -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) diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 6eca5d7f543..2e3e6f07112 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -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; } diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 4ca41dc331e..3259bd2ef7d 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -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; } diff --git a/include/video_console.h b/include/video_console.h index a0ee9ab62e9..1bb265dc9da 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -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 * diff --git a/test/dm/video.c b/test/dm/video.c index a3f3b046882..c1b2a502b47 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -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); -- 2.43.0