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

Reply via email to