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:
@@ -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,
|
ret = vidconsole_measure(scn->expo->cons, txt->font_name,
|
||||||
txt->font_size, str, &bbox, NULL);
|
txt->font_size, str, -1, &bbox, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("mea", ret);
|
return log_msg_ret("mea", ret);
|
||||||
if (widthp)
|
if (widthp)
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
* Copyright (c) 2016 Google, Inc
|
* Copyright (c) 2016 Google, Inc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define LOG_CATEGORY UCLASS_VIDEO
|
||||||
|
|
||||||
#include <abuf.h>
|
#include <abuf.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <log.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,
|
static int truetype_measure(struct udevice *dev, const char *name, uint size,
|
||||||
const char *text, struct vidconsole_bbox *bbox,
|
const char *text, int pixel_limit,
|
||||||
struct alist *lines)
|
struct vidconsole_bbox *bbox, struct alist *lines)
|
||||||
{
|
{
|
||||||
struct console_tt_metrics *met;
|
struct console_tt_metrics *met;
|
||||||
struct vidconsole_mline mline;
|
struct vidconsole_mline mline;
|
||||||
const char *s;
|
const char *s, *last_space;
|
||||||
|
int width, last_width;
|
||||||
stbtt_fontinfo *font;
|
stbtt_fontinfo *font;
|
||||||
int lsb, advance;
|
int lsb, advance;
|
||||||
int width;
|
|
||||||
int start;
|
int start;
|
||||||
|
int limit;
|
||||||
int lastch;
|
int lastch;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -754,14 +757,30 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
|
|||||||
if (!*text)
|
if (!*text)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
limit = -1;
|
||||||
|
if (pixel_limit != -1)
|
||||||
|
limit = tt_ceil((double)pixel_limit / met->scale);
|
||||||
|
|
||||||
font = &met->font;
|
font = &met->font;
|
||||||
width = 0;
|
width = 0;
|
||||||
bbox->y1 = 0;
|
bbox->y1 = 0;
|
||||||
|
bbox->x1 = 0;
|
||||||
start = 0;
|
start = 0;
|
||||||
|
last_space = NULL;
|
||||||
|
last_width = 0;
|
||||||
for (lastch = 0, s = text; *s; s++) {
|
for (lastch = 0, s = text; *s; s++) {
|
||||||
int neww;
|
int neww;
|
||||||
int ch = *s;
|
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 */
|
/* First get some basic metrics about this character */
|
||||||
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
|
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
|
||||||
neww = width + advance;
|
neww = width + advance;
|
||||||
@@ -772,10 +791,16 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
|
|||||||
lastch = ch;
|
lastch = ch;
|
||||||
|
|
||||||
/* see if we need to start a new line */
|
/* 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.x0 = 0;
|
||||||
mline.bbox.y0 = bbox->y1;
|
mline.bbox.y0 = bbox->y1;
|
||||||
mline.bbox.x1 = tt_ceil((double)width * met->scale);
|
mline.bbox.x1 = tt_ceil((double)width * met->scale);
|
||||||
|
bbox->x1 = max(bbox->x1, mline.bbox.x1);
|
||||||
bbox->y1 += met->font_size;
|
bbox->y1 += met->font_size;
|
||||||
mline.bbox.y1 = bbox->y1;
|
mline.bbox.y1 = bbox->y1;
|
||||||
mline.bbox.valid = true;
|
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);
|
mline.start, mline.len, mline.len, text + mline.start);
|
||||||
|
|
||||||
start = s - text;
|
start = s - text;
|
||||||
if (ch == '\n')
|
start++;
|
||||||
start++;
|
|
||||||
lastch = 0;
|
lastch = 0;
|
||||||
neww = 0;
|
neww = 0;
|
||||||
}
|
}
|
||||||
@@ -811,7 +835,7 @@ static int truetype_measure(struct udevice *dev, const char *name, uint size,
|
|||||||
bbox->valid = true;
|
bbox->valid = true;
|
||||||
bbox->x0 = 0;
|
bbox->x0 = 0;
|
||||||
bbox->y0 = 0;
|
bbox->y0 = 0;
|
||||||
bbox->x1 = tt_ceil((double)width * met->scale);
|
bbox->x1 = max(bbox->x1, mline.bbox.x1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
int vidconsole_measure(struct udevice *dev, const char *name, uint size,
|
||||||
const char *text, struct vidconsole_bbox *bbox,
|
const char *text, int limit,
|
||||||
struct alist *lines)
|
struct vidconsole_bbox *bbox, struct alist *lines)
|
||||||
{
|
{
|
||||||
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
|
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
|
||||||
struct vidconsole_ops *ops = vidconsole_get_ops(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 (ops->measure) {
|
||||||
if (lines)
|
if (lines)
|
||||||
alist_empty(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)
|
if (ret != -ENOSYS)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -250,6 +250,7 @@ struct vidconsole_ops {
|
|||||||
* @name: Font name to use (NULL to use default)
|
* @name: Font name to use (NULL to use default)
|
||||||
* @size: Font size to use (0 to use default)
|
* @size: Font size to use (0 to use default)
|
||||||
* @text: Text to measure
|
* @text: Text to measure
|
||||||
|
* @limit: Width limit for each line, or -1 if none
|
||||||
* @bbox: Returns bounding box of text, assuming it is positioned
|
* @bbox: Returns bounding box of text, assuming it is positioned
|
||||||
* at 0,0
|
* at 0,0
|
||||||
* @lines: If non-NULL, this must be an alist of
|
* @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
|
* Returns: 0 on success, -ENOENT if no such font
|
||||||
*/
|
*/
|
||||||
int (*measure)(struct udevice *dev, const char *name, uint size,
|
int (*measure)(struct udevice *dev, const char *name, uint size,
|
||||||
const char *text, struct vidconsole_bbox *bbox,
|
const char *text, int limit,
|
||||||
struct alist *lines);
|
struct vidconsole_bbox *bbox, struct alist *lines);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nominal() - Measure the expected width of a line of text
|
* 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)
|
* @name: Font name to use (NULL to use default)
|
||||||
* @size: Font size to use (0 to use default)
|
* @size: Font size to use (0 to use default)
|
||||||
* @text: Text to measure
|
* @text: Text to measure
|
||||||
|
* @limit: Width limit for each line, or -1 if none
|
||||||
* @bbox: Returns bounding box of text, assuming it is positioned
|
* @bbox: Returns bounding box of text, assuming it is positioned
|
||||||
* at 0,0
|
* at 0,0
|
||||||
* @lines: If non-NULL, this must be an alist of
|
* @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
|
* Returns: 0 on success, -ENOENT if no such font
|
||||||
*/
|
*/
|
||||||
int vidconsole_measure(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,
|
const char *text, int limit,
|
||||||
struct alist *lines);
|
struct vidconsole_bbox *bbox, struct alist *lines);
|
||||||
/**
|
/**
|
||||||
* vidconsole_nominal() - Measure the expected width of a line of text
|
* vidconsole_nominal() - Measure the expected width of a line of text
|
||||||
*
|
*
|
||||||
|
@@ -789,6 +789,7 @@ static int dm_test_font_measure(struct unit_test_state *uts)
|
|||||||
struct vidconsole_bbox bbox;
|
struct vidconsole_bbox bbox;
|
||||||
struct video_priv *priv;
|
struct video_priv *priv;
|
||||||
struct udevice *dev, *con;
|
struct udevice *dev, *con;
|
||||||
|
const int limit = 0x320;
|
||||||
struct alist lines;
|
struct alist lines;
|
||||||
int nl;
|
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));
|
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
|
||||||
vidconsole_position_cursor(con, 0, 0);
|
vidconsole_position_cursor(con, 0, 0);
|
||||||
alist_init_struct(&lines, struct vidconsole_mline);
|
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));
|
&lines));
|
||||||
ut_asserteq(0, bbox.x0);
|
ut_asserteq(0, bbox.x0);
|
||||||
ut_asserteq(0, bbox.y0);
|
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(163, line->len);
|
||||||
ut_asserteq(strlen(test_string + nl + 1), 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;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_font_measure, UTF_SCAN_FDT);
|
DM_TEST(dm_test_font_measure, UTF_SCAN_FDT);
|
||||||
|
Reference in New Issue
Block a user