summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-07-14 20:26:42 +0300
committerTom Rini <trini@konsulko.com>2023-07-14 20:26:42 +0300
commitb3bbad816e97538c8c3b8acad7c7e134261cf3a3 (patch)
tree0cfdcc657a9e0b3a5cf91e5fbb3ef2415aa2b12f
parentcef36755094f0c5463ff34ac89de8d88ef68982b (diff)
parent04f3dcd503a537fab50329686874559dae8a1a22 (diff)
downloadu-boot-b3bbad816e97538c8c3b8acad7c7e134261cf3a3.tar.xz
Merge branch '2023-07-14-expo-initial-config-editor'
To quote the author: This series provides a means to edit board configuration in U-Boot in a graphical manner. It supports multiple menu items and allows moving between them and selecting items. The configuration is defined in a data format so that code is not needed in most cases. This allows the board configuration to be provided in the devicetree. This is still at an early stage, since it only supports menus. Numeric values are not supported. Most importantly it does not yet support loading or saving the configuration selected by the user. To try it out you can use something like: ./tools/expo.py -e test/boot/files/expo_layout.dts \ -l test/boot/files/expo_layout.dts -o cedit.dtb ./u-boot -Tl -c "cedit load hostfs - cedit.dtb; cedit run" Use the arrow keys to move between menus, enter to open a menu, escape to exit. Various minor fixes and improvements are provided in this series: - Update STB TrueType library to latest - Support clearing part of the video display - Support multiple livetrees loaded at runtime - Support loading and allocating a file - Support proper measuring of text in expo - Support simple themes for expo
-rw-r--r--arch/sandbox/cpu/sdl.c3
-rw-r--r--arch/sandbox/dts/cedit.dtsi64
-rw-r--r--arch/sandbox/dts/sandbox.dtsi6
-rw-r--r--arch/sandbox/dts/test.dts13
-rw-r--r--boot/Kconfig14
-rw-r--r--boot/Makefile3
-rw-r--r--boot/bootflow_menu.c6
-rw-r--r--boot/bootmeth-uclass.c30
-rw-r--r--boot/cedit.c163
-rw-r--r--boot/expo.c102
-rw-r--r--boot/expo_build.c401
-rw-r--r--boot/scene.c361
-rw-r--r--boot/scene_internal.h84
-rw-r--r--boot/scene_menu.c290
-rw-r--r--cmd/Kconfig9
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/cat.c47
-rw-r--r--cmd/cedit.c93
-rw-r--r--common/log.c1
-rw-r--r--configs/sandbox_defconfig1
-rw-r--r--doc/develop/expo.rst305
-rw-r--r--doc/usage/cmd/cedit.rst31
-rw-r--r--doc/usage/index.rst1
-rw-r--r--drivers/core/ofnode.c27
-rw-r--r--drivers/gpio/gpio-uclass.c2
-rw-r--r--drivers/video/console_truetype.c259
-rw-r--r--drivers/video/stb_truetype.h2257
-rw-r--r--drivers/video/vidconsole-uclass.c42
-rw-r--r--drivers/video/video-uclass.c54
-rw-r--r--fs/fs.c58
-rw-r--r--include/dm/of.h2
-rw-r--r--include/dm/ofnode.h10
-rw-r--r--include/expo.h209
-rw-r--r--include/fs.h38
-rw-r--r--include/log.h2
-rw-r--r--include/of_live.h10
-rw-r--r--include/test/cedit-test.h29
-rw-r--r--include/test/ut.h36
-rw-r--r--include/video.h22
-rw-r--r--include/video_console.h78
-rw-r--r--lib/of_live.c11
-rw-r--r--test/boot/expo.c241
-rw-r--r--test/boot/files/expo_layout.dts84
-rw-r--r--test/cmd/bdinfo.c79
-rw-r--r--test/dm/ofnode.c45
-rw-r--r--test/dm/video.c6
-rw-r--r--test/py/tests/test_ut.py10
-rw-r--r--tools/binman/control.py2
-rwxr-xr-xtools/expo.py130
49 files changed, 5159 insertions, 613 deletions
diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c
index 2c570ed8d1..590e406517 100644
--- a/arch/sandbox/cpu/sdl.c
+++ b/arch/sandbox/cpu/sdl.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <unistd.h>
#include <stdbool.h>
+#include <sysreset.h>
#include <linux/input.h>
#include <SDL2/SDL.h>
#include <asm/state.h>
@@ -81,7 +82,7 @@ static void sandbox_sdl_poll_events(void)
switch (event.type) {
case SDL_QUIT:
puts("LCD window closed - quitting\n");
- reset_cpu();
+ sysreset_walk(SYSRESET_POWER_OFF);
break;
}
}
diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi
new file mode 100644
index 0000000000..a9eb4c2d59
--- /dev/null
+++ b/arch/sandbox/dts/cedit.dtsi
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Expo definition for the configuration editor
+ *
+ * This used for testing building an expo from a data file. This devicetree
+ * provides a description of the objects to be created.
+ */
+
+#include <test/cedit-test.h>
+
+&cedit {
+ dynamic-start = <ID_DYNAMIC_START>;
+
+ scenes {
+ main {
+ id = <ID_SCENE1>;
+
+ /* value refers to the matching id in /strings */
+ title-id = <ID_SCENE1_TITLE>;
+
+ /* simple string is used as it is */
+ prompt = "UP and DOWN to choose, ENTER to select";
+
+ /* defines a menu within the scene */
+ cpu-speed {
+ type = "menu";
+ id = <ID_CPU_SPEED>;
+
+ /*
+ * has both string and ID. The string is ignored
+ * if the ID is present and points to a string
+ */
+ title = "CPU speed";
+ title-id = <ID_CPU_SPEED_TITLE>;
+
+ /* menu items as simple strings */
+ item-label = "2 GHz", "2.5 GHz", "3 GHz";
+
+ /* IDs for the menu items */
+ item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
+ ID_CPU_SPEED_3>;
+ };
+
+ power-loss {
+ type = "menu";
+ id = <ID_POWER_LOSS>;
+
+ title = "AC Power";
+ item-label = "Always Off", "Always On",
+ "Memory";
+
+ item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ };
+ };
+ };
+
+ strings {
+ title {
+ id = <ID_SCENE1_TITLE>;
+ value = "Test Configuration";
+ value-es = "configuraciĆ³n de prueba";
+ };
+ };
+};
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi
index 30a305c4d2..f0ee0b3481 100644
--- a/arch/sandbox/dts/sandbox.dtsi
+++ b/arch/sandbox/dts/sandbox.dtsi
@@ -16,6 +16,12 @@
stdout-path = "/serial";
};
+ cedit-theme {
+ font-size = <30>;
+ menu-inset = <3>;
+ menuitem-gap-y = <1>;
+ };
+
alarm_wdt: alarm-wdt {
compatible = "sandbox,alarm-wdt";
timeout-sec = <5>;
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index ff9f9222e6..b5509eee8c 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -96,6 +96,8 @@
theme {
font-size = <30>;
+ menu-inset = <3>;
+ menuitem-gap-y = <1>;
};
/*
@@ -139,6 +141,15 @@
};
};
+ cedit: cedit {
+ };
+
+ cedit-theme {
+ font-size = <30>;
+ menu-inset = <3>;
+ menuitem-gap-y = <1>;
+ };
+
fuzzing-engine {
compatible = "sandbox,fuzzing-engine";
};
@@ -1828,3 +1839,5 @@
#ifdef CONFIG_SANDBOX_VPL
#include "sandbox_vpl.dtsi"
#endif
+
+#include "cedit.dtsi"
diff --git a/boot/Kconfig b/boot/Kconfig
index a643a3d128..c8b8f36d83 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1630,4 +1630,18 @@ config SAVE_PREV_BL_INITRAMFS_START_ADDR
If no initramfs was provided by previous bootloader, no env variables
will be created.
+menu "Configuration editor"
+
+config CEDIT
+ bool "Configuration editor"
+ depends on BOOTSTD
+ help
+ Provides a way to deal with board configuration and present it to
+ the user for adjustment.
+
+ This is intended to provide both graphical and text-based user
+ interfaces, but only graphical is support at present.
+
+endmenu # Configuration editor
+
endmenu # Booting
diff --git a/boot/Makefile b/boot/Makefile
index f94c31d922..f828f870a3 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -33,6 +33,7 @@ ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o
obj-$(CONFIG_$(SPL_TPL_)EXPO) += bootflow_menu.o
obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootflow_menu.o
+obj-$(CONFIG_$(SPL_TPL_)CEDIT) += cedit.o
endif
obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
@@ -50,7 +51,7 @@ ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o
endif
-obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o
+obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o expo_build.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o
diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c
index 7f06dac0af..7c1abe5772 100644
--- a/boot/bootflow_menu.c
+++ b/boot/bootflow_menu.c
@@ -124,6 +124,10 @@ int bootflow_menu_new(struct expo **expp)
priv->num_bootflows++;
}
+ ret = scene_arrange(scn);
+ if (ret)
+ return log_msg_ret("arr", ret);
+
*expp = exp;
return 0;
@@ -205,7 +209,7 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
return log_msg_ret("scn", ret);
if (text_mode)
- exp_set_text_mode(exp, text_mode);
+ expo_set_text_mode(exp, text_mode);
done = false;
do {
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index 3b3e0614da..701ee8ad1c 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -301,32 +301,6 @@ int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc,
return 0;
}
-static int alloc_file(const char *fname, uint size, void **bufp)
-{
- loff_t bytes_read;
- ulong addr;
- char *buf;
- int ret;
-
- buf = malloc(size + 1);
- if (!buf)
- return log_msg_ret("buf", -ENOMEM);
- addr = map_to_sysmem(buf);
-
- ret = fs_read(fname, addr, 0, size, &bytes_read);
- if (ret) {
- free(buf);
- return log_msg_ret("read", ret);
- }
- if (size != bytes_read)
- return log_msg_ret("bread", -EIO);
- buf[size] = '\0';
-
- *bufp = buf;
-
- return 0;
-}
-
int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align)
{
void *buf;
@@ -338,7 +312,7 @@ int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align)
if (size > size_limit)
return log_msg_ret("chk", -E2BIG);
- ret = alloc_file(bflow->fname, bflow->size, &buf);
+ ret = fs_read_alloc(bflow->fname, bflow->size, align, &buf);
if (ret)
return log_msg_ret("all", ret);
@@ -374,7 +348,7 @@ int bootmeth_alloc_other(struct bootflow *bflow, const char *fname,
if (ret)
return log_msg_ret("fs", ret);
- ret = alloc_file(path, size, &buf);
+ ret = fs_read_alloc(path, size, 0, &buf);
if (ret)
return log_msg_ret("all", ret);
diff --git a/boot/cedit.c b/boot/cedit.c
new file mode 100644
index 0000000000..ee24658917
--- /dev/null
+++ b/boot/cedit.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Implementation of configuration editor
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <cli.h>
+#include <dm.h>
+#include <expo.h>
+#include <menu.h>
+#include <video.h>
+#include <linux/delay.h>
+#include "scene_internal.h"
+
+int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
+{
+ struct scene_obj_txt *txt;
+ struct scene_obj *obj;
+ struct scene *scn;
+ int y;
+
+ scn = expo_lookup_scene_id(exp, scene_id);
+ if (!scn)
+ return log_msg_ret("scn", -ENOENT);
+
+ txt = scene_obj_find_by_name(scn, "prompt");
+ if (txt)
+ scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50);
+
+ txt = scene_obj_find_by_name(scn, "title");
+ if (txt)
+ scene_obj_set_pos(scn, txt->obj.id, 200, 10);
+
+ y = 100;
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ if (obj->type == SCENEOBJT_MENU) {
+ scene_obj_set_pos(scn, obj->id, 50, y);
+ scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
+ y += 50;
+ }
+ }
+
+ return 0;
+}
+
+int cedit_run(struct expo *exp)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct video_priv *vid_priv;
+ uint scene_id;
+ struct udevice *dev;
+ struct scene *scn;
+ bool done;
+ int ret;
+
+ cli_ch_init(cch);
+
+ /* For now we only support a video console */
+ ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
+ if (ret)
+ return log_msg_ret("vid", ret);
+ ret = expo_set_display(exp, dev);
+ if (ret)
+ return log_msg_ret("dis", ret);
+
+ ret = expo_first_scene_id(exp);
+ if (ret < 0)
+ return log_msg_ret("scn", ret);
+ scene_id = ret;
+
+ ret = expo_set_scene_id(exp, scene_id);
+ if (ret)
+ return log_msg_ret("sid", ret);
+
+ exp->popup = true;
+
+ /* This is not supported for now */
+ if (0)
+ expo_set_text_mode(exp, true);
+
+ vid_priv = dev_get_uclass_priv(dev);
+
+ scn = expo_lookup_scene_id(exp, scene_id);
+ scene_highlight_first(scn);
+
+ cedit_arange(exp, vid_priv, scene_id);
+
+ ret = expo_calc_dims(exp);
+ if (ret)
+ return log_msg_ret("dim", ret);
+
+ done = false;
+ do {
+ struct expo_action act;
+ int ichar, key;
+
+ ret = expo_render(exp);
+ if (ret)
+ break;
+
+ ichar = cli_ch_process(cch, 0);
+ if (!ichar) {
+ while (!ichar && !tstc()) {
+ schedule();
+ mdelay(2);
+ ichar = cli_ch_process(cch, -ETIMEDOUT);
+ }
+ if (!ichar) {
+ ichar = getchar();
+ ichar = cli_ch_process(cch, ichar);
+ }
+ }
+
+ key = 0;
+ if (ichar) {
+ key = bootmenu_conv_key(ichar);
+ if (key == BKEY_NONE)
+ key = ichar;
+ }
+ if (!key)
+ continue;
+
+ ret = expo_send_key(exp, key);
+ if (ret)
+ break;
+
+ ret = expo_action_get(exp, &act);
+ if (!ret) {
+ switch (act.type) {
+ case EXPOACT_POINT_OBJ:
+ scene_set_highlight_id(scn, act.select.id);
+ cedit_arange(exp, vid_priv, scene_id);
+ break;
+ case EXPOACT_OPEN:
+ scene_set_open(scn, act.select.id, true);
+ cedit_arange(exp, vid_priv, scene_id);
+ break;
+ case EXPOACT_CLOSE:
+ scene_set_open(scn, act.select.id, false);
+ cedit_arange(exp, vid_priv, scene_id);
+ break;
+ case EXPOACT_SELECT:
+ scene_set_open(scn, scn->highlight_id, false);
+ cedit_arange(exp, vid_priv, scene_id);
+ break;
+ case EXPOACT_QUIT:
+ log_debug("quitting\n");
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+ } while (!done);
+
+ if (ret)
+ return log_msg_ret("end", ret);
+
+ return 0;
+}
diff --git a/boot/expo.c b/boot/expo.c
index 05950a1760..db837f7b49 100644
--- a/boot/expo.c
+++ b/boot/expo.c
@@ -6,6 +6,8 @@
* Written by Simon Glass <sjg@chromium.org>
*/
+#define LOG_CATEGORY LOGC_EXPO
+
#include <common.h>
#include <dm.h>
#include <expo.h>
@@ -54,6 +56,22 @@ void expo_destroy(struct expo *exp)
free(exp);
}
+uint resolve_id(struct expo *exp, uint id)
+{
+ log_debug("resolve id %d\n", id);
+ if (!id)
+ id = exp->next_id++;
+ else if (id >= exp->next_id)
+ exp->next_id = id + 1;
+
+ return id;
+}
+
+void expo_set_dynamic_start(struct expo *exp, uint dyn_start)
+{
+ exp->next_id = dyn_start;
+}
+
int expo_str(struct expo *exp, const char *name, uint id, const char *str)
{
struct expo_string *estr;
@@ -83,12 +101,45 @@ const char *expo_get_str(struct expo *exp, uint id)
int expo_set_display(struct expo *exp, struct udevice *dev)
{
+ struct udevice *cons;
+ int ret;
+
+ ret = device_find_first_child_by_uclass(dev, UCLASS_VIDEO_CONSOLE,
+ &cons);
+ if (ret)
+ return log_msg_ret("con", ret);
+
exp->display = dev;
+ exp->cons = cons;
+
+ return 0;
+}
+
+int expo_calc_dims(struct expo *exp)
+{
+ struct scene *scn;
+ int ret;
+
+ if (!exp->cons)
+ return log_msg_ret("dim", -ENOTSUPP);
+
+ list_for_each_entry(scn, &exp->scene_head, sibling) {
+ /*
+ * Do the menus last so that all the menus' text objects
+ * are dimensioned
+ */
+ ret = scene_calc_dims(scn, false);
+ if (ret)
+ return log_msg_ret("scn", ret);
+ ret = scene_calc_dims(scn, true);
+ if (ret)
+ return log_msg_ret("scn", ret);
+ }
return 0;
}
-void exp_set_text_mode(struct expo *exp, bool text_mode)
+void expo_set_text_mode(struct expo *exp, bool text_mode)
{
exp->text_mode = text_mode;
}
@@ -107,13 +158,33 @@ struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id)
int expo_set_scene_id(struct expo *exp, uint scene_id)
{
- if (!expo_lookup_scene_id(exp, scene_id))
+ struct scene *scn;
+ int ret;
+
+ scn = expo_lookup_scene_id(exp, scene_id);
+ if (!scn)
return log_msg_ret("id", -ENOENT);
+ ret = scene_arrange(scn);
+ if (ret)
+ return log_msg_ret("arr", ret);
+
exp->scene_id = scene_id;
return 0;
}
+int expo_first_scene_id(struct expo *exp)
+{
+ struct scene *scn;
+
+ if (list_empty(&exp->scene_head))
+ return -ENOENT;
+
+ scn = list_first_entry(&exp->scene_head, struct scene, sibling);
+
+ return scn->id;
+}
+
int expo_render(struct expo *exp)
{
struct udevice *dev = exp->display;
@@ -156,6 +227,11 @@ int expo_send_key(struct expo *exp, int key)
ret = scene_send_key(scn, key, &exp->action);
if (ret)
return log_msg_ret("key", ret);
+
+ /* arrange it to get any changes */
+ ret = scene_arrange(scn);
+ if (ret)
+ return log_msg_ret("arr", ret);
}
return scn ? 0 : -ECHILD;
@@ -168,3 +244,25 @@ int expo_action_get(struct expo *exp, struct expo_action *act)
return act->type == EXPOACT_NONE ? -EAGAIN : 0;
}
+
+int expo_apply_theme(struct expo *exp, ofnode node)
+{
+ struct scene *scn;
+ struct expo_theme *theme = &exp->theme;
+ int ret;
+
+ log_debug("Applying theme %s\n", ofnode_get_name(node));
+
+ memset(theme, '\0', sizeof(struct expo_theme));
+ ofnode_read_u32(node, "font-size", &theme->font_size);
+ ofnode_read_u32(node, "menu-inset", &theme->menu_inset);
+ ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y);
+
+ list_for_each_entry(scn, &exp->scene_head, sibling) {
+ ret = scene_apply_theme(scn, theme);
+ if (ret)
+ return log_msg_ret("app", ret);
+ }
+
+ return 0;
+}
diff --git a/boot/expo_build.c b/boot/expo_build.c
new file mode 100644
index 0000000000..22f62eb54b
--- /dev/null
+++ b/boot/expo_build.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Building an expo from an FDT description
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY LOGC_EXPO
+
+#include <common.h>
+#include <expo.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/ofnode.h>
+#include <linux/libfdt.h>
+
+/**
+ * struct build_info - Information to use when building
+ *
+ * @str_for_id: String for each ID in use, NULL if empty. The string is NULL
+ * if there is nothing for this ID. Since ID 0 is never used, the first
+ * element of this array is always NULL
+ * @str_count: Number of entries in @str_for_id
+ */
+struct build_info {
+ const char **str_for_id;
+ int str_count;
+};
+
+/**
+ * add_txt_str - Add a string or lookup its ID, then add to expo
+ *
+ * @info: Build information
+ * @node: Node describing scene
+ * @scn: Scene to add to
+ * @find_name: Name to look for (e.g. "title"). This will find a property called
+ * "title" if it exists, else will look up the string for "title-id"
+ * Return: ID of added string, or -ve on error
+ */
+int add_txt_str(struct build_info *info, ofnode node, struct scene *scn,
+ const char *find_name, uint obj_id)
+{
+ const char *text;
+ uint str_id;
+ int ret;
+
+ text = ofnode_read_string(node, find_name);
+ if (!text) {
+ char name[40];
+ u32 id;
+
+ snprintf(name, sizeof(name), "%s-id", find_name);
+ ret = ofnode_read_u32(node, name, &id);
+ if (ret)
+ return log_msg_ret("id", -EINVAL);
+
+ if (id >= info->str_count)
+ return log_msg_ret("id", -E2BIG);
+ text = info->str_for_id[id];
+ if (!text)
+ return log_msg_ret("id", -EINVAL);
+ }
+
+ ret = expo_str(scn->expo, find_name, 0, text);
+ if (ret < 0)
+ return log_msg_ret("add", ret);
+ str_id = ret;
+
+ ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
+ if (ret < 0)
+ return log_msg_ret("add", ret);
+
+ return ret;
+}
+
+/**
+ * add_txt_str_list - Add a list string or lookup its ID, then add to expo
+ *
+ * @info: Build information
+ * @node: Node describing scene
+ * @scn: Scene to add to
+ * @find_name: Name to look for (e.g. "title"). This will find a string-list
+ * property called "title" if it exists, else will look up the string in the
+ * "title-id" string list.
+ * Return: ID of added string, or -ve on error
+ */
+int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn,
+ const char *find_name, int index, uint obj_id)
+{
+ const char *text;
+ uint str_id;
+ int ret;
+
+ ret = ofnode_read_string_index(node, find_name, index, &text);
+ if (ret) {
+ char name[40];
+ u32 id;
+
+ snprintf(name, sizeof(name), "%s-id", find_name);
+ ret = ofnode_read_u32_index(node, name, index, &id);
+ if (ret)
+ return log_msg_ret("id", -ENOENT);
+
+ if (id >= info->str_count)
+ return log_msg_ret("id", -E2BIG);
+ text = info->str_for_id[id];
+ if (!text)
+ return log_msg_ret("id", -EINVAL);
+ }
+
+ ret = expo_str(scn->expo, find_name, 0, text);
+ if (ret < 0)
+ return log_msg_ret("add", ret);
+ str_id = ret;
+
+ ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
+ if (ret < 0)
+ return log_msg_ret("add", ret);
+
+ return ret;
+}
+
+/*
+ * build_element() - Handle creating a text object from a label
+ *
+ * Look up a property called @label or @label-id and create a string for it
+ */
+int build_element(void *ldtb, int node, const char *label)
+{
+ return 0;
+}
+
+/**
+ * read_strings() - Read in the list of strings
+ *
+ * Read the strings into an ID-indexed list, so they can be used for building
+ * an expo. The strings are in a /strings node and each has its own subnode
+ * containing the ID and the string itself:
+ *
+ * example {
+ * id = <123>;
+ * value = "This is a test";
+ * };
+ *
+ * Future work may add support for unicode and multiple languages
+ *
+ * @info: Build information
+ * @root: Root node to read from
+ * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
+ * error
+ */
+static int read_strings(struct build_info *info, ofnode root)
+{
+ ofnode strings, node;
+
+ strings = ofnode_find_subnode(root, "strings");
+ if (!ofnode_valid(strings))
+ return log_msg_ret("str", -EINVAL);
+
+ ofnode_for_each_subnode(node, strings) {
+ const char *val;
+ int ret;
+ u32 id;
+
+ ret = ofnode_read_u32(node, "id", &id);
+ if (ret)
+ return log_msg_ret("id", -EINVAL);
+ val = ofnode_read_string(node, "value");
+ if (!val)
+ return log_msg_ret("val", -EINVAL);
+
+ if (id >= info->str_count) {
+ int new_count = info->str_count + 20;
+ void *new_arr;
+
+ new_arr = realloc(info->str_for_id,
+ new_count * sizeof(char *));
+ if (!new_arr)
+ return log_msg_ret("id", -ENOMEM);
+ memset(new_arr + info->str_count, '\0',
+ (new_count - info->str_count) * sizeof(char *));
+ info->str_for_id = new_arr;
+ info->str_count = new_count;
+ }
+
+ info->str_for_id[id] = val;
+ }
+
+ return 0;
+}
+
+/**
+ * list_strings() - List the available strings with their IDs
+ *
+ * @info: Build information
+ */
+static void list_strings(struct build_info *info)
+{
+ int i;
+
+ for (i = 0; i < info->str_count; i++) {
+ if (info->str_for_id[i])
+ printf("%3d %s\n", i, info->str_for_id[i]);
+ }
+}
+
+/**
+ * menu_build() - Build a menu and add it to a scene
+ *
+ * See doc/developer/expo.rst for a description of the format
+ *
+ * @info: Build information
+ * @node: Node containing the menu description
+ * @scn: Scene to add the menu to
+ * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
+ * error, -ENOENT if there is a references to a non-existent string
+ */
+static int menu_build(struct build_info *info, ofnode node, struct scene *scn)
+{
+ struct scene_obj_menu *menu;
+ uint title_id, menu_id;
+ const u32 *item_ids;
+ int ret, size, i;
+ const char *name;
+ u32 id;
+
+ name = ofnode_get_name(node);
+ ret = ofnode_read_u32(node, "id", &id);
+ if (ret)
+ return log_msg_ret("id", -EINVAL);
+
+ ret = scene_menu(scn, name, id, &menu);
+ if (ret < 0)
+ return log_msg_ret("men", ret);
+ menu_id = ret;
+
+ /* Set the title */
+ ret = add_txt_str(info, node, scn, "title", 0);
+ if (ret < 0)
+ return log_msg_ret("tit", ret);
+ title_id = ret;
+ ret = scene_menu_set_title(scn, menu_id, title_id);
+
+ item_ids = ofnode_read_prop(node, "item-id", &size);
+ if (!item_ids)
+ return log_msg_ret("itm", -EINVAL);
+ if (!size || size % sizeof(u32))
+ return log_msg_ret("isz", -EINVAL);
+ size /= sizeof(u32);
+
+ for (i = 0; i < size; i++) {
+ struct scene_menitem *item;
+ uint label, key, desc;
+
+ ret = add_txt_str_list(info, node, scn, "item-label", i, 0);
+ if (ret < 0 && ret != -ENOENT)
+ return log_msg_ret("lab", ret);
+ label = max(0, ret);
+
+ ret = add_txt_str_list(info, node, scn, "key-label", i, 0);
+ if (ret < 0 && ret != -ENOENT)
+ return log_msg_ret("key", ret);
+ key = max(0, ret);
+
+ ret = add_txt_str_list(info, node, scn, "desc-label", i, 0);
+ if (ret < 0 && ret != -ENOENT)
+ return log_msg_ret("lab", ret);
+ desc = max(0, ret);
+
+ ret = scene_menuitem(scn, menu_id, simple_xtoa(i),
+ fdt32_to_cpu(item_ids[i]), key, label,
+ desc, 0, 0, &item);
+ if (ret < 0)
+ return log_msg_ret("mi", ret);
+ }
+
+ return 0;
+}
+
+/**
+ * menu_build() - Build an expo object and add it to a scene
+ *
+ * See doc/developer/expo.rst for a description of the format
+ *
+ * @info: Build information
+ * @node: Node containing the object description
+ * @scn: Scene to add the object to
+ * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
+ * error, -ENOENT if there is a references to a non-existent string
+ */
+static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
+{
+ const char *type;
+ u32 id;
+ int ret;
+
+ log_debug("- object %s\n", ofnode_get_name(node));
+ ret = ofnode_read_u32(node, "id", &id);
+ if (ret)
+ return log_msg_ret("id", -EINVAL);
+
+ type = ofnode_read_string(node, "type");
+ if (!type)
+ return log_msg_ret("typ", -EINVAL);
+
+ if (!strcmp("menu", type))
+ ret = menu_build(info, node, scn);
+ else
+ ret = -EINVAL;
+ if (ret)
+ return log_msg_ret("bld", ret);
+
+ return 0;
+}
+
+/**
+ * scene_build() - Build a scene and all its objects
+ *
+ * See doc/developer/expo.rst for a description of the format
+ *
+ * @info: Build information
+ * @node: Node containing the scene description
+ * @scn: Scene to add the object to
+ * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
+ * error, -ENOENT if there is a references to a non-existent string
+ */
+static int scene_build(struct build_info *info, ofnode scn_node,
+ struct expo *exp)
+{
+ const char *name;
+ struct scene *scn;
+ uint id, title_id;
+ ofnode node;
+ int ret;
+
+ name = ofnode_get_name(scn_node);
+ log_debug("Building scene %s\n", name);
+ ret = ofnode_read_u32(scn_node, "id", &id);
+ if (ret)
+ return log_msg_ret("id", -EINVAL);
+
+ ret = scene_new(exp, name, id, &scn);
+ if (ret < 0)
+ return log_msg_ret("scn", ret);
+
+ ret = add_txt_str(info, scn_node, scn, "title", 0);
+ if (ret < 0)
+ return log_msg_ret("tit", ret);
+ title_id = ret;
+ scene_title_set(scn, title_id);
+
+ ret = add_txt_str(info, scn_node, scn, "prompt", 0);
+ if (ret < 0)
+ return log_msg_ret("pr", ret);
+
+ ofnode_for_each_subnode(node, scn_node) {
+ ret = obj_build(info, node, scn);
+ if (ret < 0)
+ return log_msg_ret("mit", ret);
+ }
+
+ return 0;
+}
+
+int expo_build(ofnode root, struct expo **expp)
+{
+ struct build_info info;
+ ofnode scenes, node;
+ struct expo *exp;
+ u32 dyn_start;
+ int ret;
+
+ memset(&info, '\0', sizeof(info));
+ ret = read_strings(&info, root);
+ if (ret)
+ return log_msg_ret("str", ret);
+ if (_DEBUG)
+ list_strings(&info);
+
+ ret = expo_new("name", NULL, &exp);
+ if (ret)
+ return log_msg_ret("exp", ret);
+
+ if (!ofnode_read_u32(root, "dynamic-start", &dyn_start))
+ expo_set_dynamic_start(exp, dyn_start);
+
+ scenes = ofnode_find_subnode(root, "scenes");
+ if (!ofnode_valid(scenes))
+ return log_msg_ret("sno", -EINVAL);
+
+ ofnode_for_each_subnode(node, scenes) {
+ ret = scene_build(&info, node, exp);
+ if (ret < 0)
+ return log_msg_ret("scn", ret);
+ }
+ *expp = exp;
+
+ return 0;
+}
diff --git a/boot/scene.c b/boot/scene.c
index 030f6aa2a0..e52333371f 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -6,26 +6,19 @@
* Written by Simon Glass <sjg@chromium.org>
*/
+#define LOG_CATEGORY LOGC_EXPO
+
#include <common.h>
#include <dm.h>
#include <expo.h>
#include <malloc.h>
#include <mapmem.h>
+#include <menu.h>
#include <video.h>
#include <video_console.h>
#include <linux/input.h>
#include "scene_internal.h"
-uint resolve_id(struct expo *exp, uint id)
-{
- if (!id)
- id = exp->next_id++;
- else if (id >= exp->next_id)
- exp->next_id = id + 1;
-
- return id;
-}
-
int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
{
struct scene *scn;
@@ -65,16 +58,12 @@ void scene_destroy(struct scene *scn)
scene_obj_destroy(obj);
free(scn->name);
- free(scn->title);
free(scn);
}
-int scene_title_set(struct scene *scn, const char *title)
+int scene_title_set(struct scene *scn, uint id)
{
- free(scn->title);
- scn->title = strdup(title);
- if (!scn->title)
- return log_msg_ret("tit", -ENOMEM);
+ scn->title_id = id;
return 0;
}
@@ -103,6 +92,18 @@ void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
return NULL;
}
+void *scene_obj_find_by_name(struct scene *scn, const char *name)
+{
+ struct scene_obj *obj;
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ if (!strcmp(name, obj->name))
+ return obj;
+ }
+
+ return NULL;
+}
+
int scene_obj_add(struct scene *scn, const char *name, uint id,
enum scene_obj_t type, uint size, struct scene_obj **objp)
{
@@ -213,22 +214,46 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
if (!obj)
return log_msg_ret("find", -ENOENT);
- obj->x = x;
- obj->y = y;
- if (obj->type == SCENEOBJT_MENU)
- scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
+ obj->dim.x = x;
+ obj->dim.y = y;
+
+ return 0;
+}
+
+int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
+{
+ struct scene_obj *obj;
+
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("find", -ENOENT);
+ obj->dim.w = w;
+ obj->dim.h = h;
return 0;
}
int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
{
+ int ret;
+
+ ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
+ hide ? SCENEOF_HIDE : 0);
+ if (ret)
+ return log_msg_ret("flg", ret);
+
+ return 0;
+}
+
+int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
+{
struct scene_obj *obj;
obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
if (!obj)
return log_msg_ret("find", -ENOENT);
- obj->hide = hide;
+ obj->flags &= ~clr;
+ obj->flags |= set;
return 0;
}
@@ -258,16 +283,30 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
case SCENEOBJT_TEXT: {
struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
struct expo *exp = scn->expo;
+ struct vidconsole_bbox bbox;
+ const char *str;
+ int len, ret;
+ str = expo_get_str(exp, txt->str_id);
+ if (!str)
+ return log_msg_ret("str", -ENOENT);
+ len = strlen(str);
+
+ /* if there is no console, make it up */
+ if (!exp->cons) {
+ if (widthp)
+ *widthp = 8 * len;
+ return 16;
+ }
+
+ ret = vidconsole_measure(scn->expo->cons, txt->font_name,
+ txt->font_size, str, &bbox);
+ if (ret)
+ return log_msg_ret("mea", ret);
if (widthp)
- *widthp = 16; /* fake value for now */
- if (txt->font_size)
- return txt->font_size;
- if (exp->display)
- return video_default_font_height(exp->display);
-
- /* use a sensible default */
- return 16;
+ *widthp = bbox.x1;
+
+ return bbox.y1;
}
}
@@ -282,18 +321,13 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
{
struct scene *scn = obj->scene;
struct expo *exp = scn->expo;
- struct udevice *cons, *dev = exp->display;
+ const struct expo_theme *theme = &exp->theme;
+ struct udevice *dev = exp->display;
+ struct udevice *cons = text_mode ? NULL : exp->cons;
int x, y, ret;
- cons = NULL;
- if (!text_mode) {
- ret = device_find_first_child_by_uclass(dev,
- UCLASS_VIDEO_CONSOLE,
- &cons);
- }
-
- x = obj->x;
- y = obj->y;
+ x = obj->dim.x;
+ y = obj->dim.y;
switch (obj->type) {
case SCENEOBJT_NONE:
@@ -325,14 +359,45 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
}
if (ret && ret != -ENOSYS)
return log_msg_ret("font", ret);
- vidconsole_set_cursor_pos(cons, x, y);
str = expo_get_str(exp, txt->str_id);
- if (str)
+ if (str) {
+ struct video_priv *vid_priv;
+ struct vidconsole_colour old;
+ enum colour_idx fore, back;
+
+ if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+ fore = VID_BLACK;
+ back = VID_WHITE;
+ } else {
+ fore = VID_LIGHT_GRAY;
+ back = VID_BLACK;
+ }
+
+ vid_priv = dev_get_uclass_priv(dev);
+ if (obj->flags & SCENEOF_POINT) {
+ vidconsole_push_colour(cons, fore, back, &old);
+ video_fill_part(dev, x - theme->menu_inset, y,
+ x + obj->dim.w,
+ y + obj->dim.h,
+ vid_priv->colour_bg);
+ }
+ vidconsole_set_cursor_pos(cons, x, y);
vidconsole_put_string(cons, str);
+ if (obj->flags & SCENEOF_POINT)
+ vidconsole_pop_colour(cons, &old);
+ }
break;
}
case SCENEOBJT_MENU: {
struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
+
+ if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
+ if (!cons)
+ return -ENOTSUPP;
+
+ /* draw a background behind the menu items */
+ scene_menu_render(menu);
+ }
/*
* With a vidconsole, the text and item pointer are rendered as
* normal objects so we don't need to do anything here. The menu
@@ -371,6 +436,30 @@ int scene_arrange(struct scene *scn)
return 0;
}
+int scene_render_deps(struct scene *scn, uint id)
+{
+ struct scene_obj *obj;
+ int ret;
+
+ if (!id)
+ return 0;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("obj", -ENOENT);
+
+ if (!(obj->flags & SCENEOF_HIDE)) {
+ ret = scene_obj_render(obj, false);
+ if (ret && ret != -ENOTSUPP)
+ return log_msg_ret("ren", ret);
+
+ if (obj->type == SCENEOBJT_MENU)
+ scene_menu_render_deps(scn,
+ (struct scene_obj_menu *)obj);
+ }
+
+ return 0;
+}
+
int scene_render(struct scene *scn)
{
struct expo *exp = scn->expo;
@@ -378,21 +467,107 @@ int scene_render(struct scene *scn)
int ret;
list_for_each_entry(obj, &scn->obj_head, sibling) {
- if (!obj->hide) {
+ if (!(obj->flags & SCENEOF_HIDE)) {
ret = scene_obj_render(obj, exp->text_mode);
if (ret && ret != -ENOTSUPP)
return log_msg_ret("ren", ret);
}
}
+ /* render any highlighted object on top of the others */
+ if (scn->highlight_id && !exp->text_mode) {
+ ret = scene_render_deps(scn, scn->highlight_id);
+ if (ret && ret != -ENOTSUPP)
+ return log_msg_ret("dep", ret);
+ }
+
return 0;
}
+/**
+ * send_key_obj() - Handle a keypress for moving between objects
+ *
+ * @scn: Scene to receive the key
+ * @key: Key to send (KEYCODE_UP)
+ * @event: Returns resulting event from this keypress
+ * Returns: 0 if OK, -ve on error
+ */
+static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
+ struct expo_action *event)
+{
+ switch (key) {
+ case BKEY_UP:
+ while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
+ sibling)) {
+ obj = list_entry(obj->sibling.prev,
+ struct scene_obj, sibling);
+ if (obj->type == SCENEOBJT_MENU) {
+ event->type = EXPOACT_POINT_OBJ;
+ event->select.id = obj->id;
+ log_debug("up to obj %d\n", event->select.id);
+ break;
+ }
+ }
+ break;
+ case BKEY_DOWN:
+ while (!list_is_last(&obj->sibling, &scn->obj_head)) {
+ obj = list_entry(obj->sibling.next, struct scene_obj,
+ sibling);
+ if (obj->type == SCENEOBJT_MENU) {
+ event->type = EXPOACT_POINT_OBJ;
+ event->select.id = obj->id;
+ log_debug("down to obj %d\n", event->select.id);
+ break;
+ }
+ }
+ break;
+ case BKEY_SELECT:
+ if (obj->type == SCENEOBJT_MENU) {
+ event->type = EXPOACT_OPEN;
+ event->select.id = obj->id;
+ log_debug("open obj %d\n", event->select.id);
+ }
+ break;
+ case BKEY_QUIT:
+ event->type = EXPOACT_QUIT;
+ log_debug("obj quit\n");
+ break;
+ }
+}
+
int scene_send_key(struct scene *scn, int key, struct expo_action *event)
{
+ struct scene_obj_menu *menu;
struct scene_obj *obj;
int ret;
+ event->type = EXPOACT_NONE;
+
+ /*
+ * In 'popup' mode, arrow keys move betwen objects, unless a menu is
+ * opened
+ */
+ if (scn->expo->popup) {
+ obj = NULL;
+ if (scn->highlight_id) {
+ obj = scene_obj_find(scn, scn->highlight_id,
+ SCENEOBJT_NONE);
+ }
+ if (!obj)
+ return 0;
+
+ if (!(obj->flags & SCENEOF_OPEN)) {
+ send_key_obj(scn, obj, key, event);
+ return 0;
+ }
+
+ menu = (struct scene_obj_menu *)obj,
+ ret = scene_menu_send_key(scn, menu, key, event);
+ if (ret)
+ return log_msg_ret("key", ret);
+ return 0;
+ }
+
list_for_each_entry(obj, &scn->obj_head, sibling) {
if (obj->type == SCENEOBJT_MENU) {
struct scene_obj_menu *menu;
@@ -401,14 +576,108 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event)
ret = scene_menu_send_key(scn, menu, key, event);
if (ret)
return log_msg_ret("key", ret);
+ break;
+ }
+ }
- /* only allow one menu */
- ret = scene_menu_arrange(scn, menu);
- if (ret)
- return log_msg_ret("arr", ret);
+ return 0;
+}
+
+int scene_calc_dims(struct scene *scn, bool do_menus)
+{
+ struct scene_obj *obj;
+ int ret;
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_TEXT:
+ case SCENEOBJT_IMAGE: {
+ int width;
+
+ if (!do_menus) {
+ ret = scene_obj_get_hw(scn, obj->id, &width);
+ if (ret < 0)
+ return log_msg_ret("get", ret);
+ obj->dim.w = width;
+ obj->dim.h = ret;
+ }
+ break;
+ }
+ case SCENEOBJT_MENU: {
+ struct scene_obj_menu *menu;
+
+ if (do_menus) {
+ menu = (struct scene_obj_menu *)obj;
+
+ ret = scene_menu_calc_dims(menu);
+ if (ret)
+ return log_msg_ret("men", ret);
+ }
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
+{
+ struct scene_obj *obj;
+ int ret;
+
+ /* Avoid error-checking optional items */
+ scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_MENU:
+ break;
+ case SCENEOBJT_TEXT:
+ scene_txt_set_font(scn, obj->id, NULL,
+ theme->font_size);
+ break;
+ }
+ }
+
+ ret = scene_arrange(scn);
+ if (ret)
+ return log_msg_ret("arr", ret);
+
+ return 0;
+}
+
+void scene_set_highlight_id(struct scene *scn, uint id)
+{
+ scn->highlight_id = id;
+}
+
+void scene_highlight_first(struct scene *scn)
+{
+ struct scene_obj *obj;
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ switch (obj->type) {
+ case SCENEOBJT_MENU:
+ scene_set_highlight_id(scn, obj->id);
+ return;
+ default:
break;
}
}
+}
+
+int scene_set_open(struct scene *scn, uint id, bool open)
+{
+ int ret;
+
+ ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
+ open ? SCENEOF_OPEN : 0);
+ if (ret)
+ return log_msg_ret("flg", ret);
return 0;
}
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index e8fd765811..fb1ea5533b 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -23,7 +23,7 @@ struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id);
*
* @exp: Expo to use
* @id: ID to use, or 0 to auto-allocate one
- * @return: Either @id, or the auto-allocated ID
+ * Returns: Either @id, or the auto-allocated ID
*/
uint resolve_id(struct expo *exp, uint id);
@@ -36,10 +36,19 @@ uint resolve_id(struct expo *exp, uint id);
* @scn: Scene to search
* @id: ID of object to find
* @type: Type of the object, or SCENEOBJT_NONE to match any type
+ * Returns: Object found, or NULL if not found
*/
void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type);
/**
+ * scene_obj_find_by_name() - Find an object in a scene by name
+ *
+ * @scn: Scene to search
+ * @name: Name to search for
+ */
+void *scene_obj_find_by_name(struct scene *scn, const char *name);
+
+/**
* scene_obj_add() - Add a new object to a scene
*
* @scn: Scene to update
@@ -54,6 +63,28 @@ int scene_obj_add(struct scene *scn, const char *name, uint id,
enum scene_obj_t type, uint size, struct scene_obj **objp);
/**
+ * scene_obj_flag_clrset() - Adjust object flags
+ *
+ * @scn: Scene to update
+ * @id: ID of object to update
+ * @clr: Bits to clear in the object's flags
+ * @set: Bits to set in the object's flags
+ * Returns 0 if OK, -ENOENT if the object was not found
+ */
+int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set);
+
+/**
+ * scene_calc_dims() - Calculate the dimensions of the scene objects
+ *
+ * Updates the width and height of all objects based on their contents
+ *
+ * @scn: Scene to update
+ * @do_menus: true to calculate only menus, false to calculate everything else
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int scene_calc_dims(struct scene *scn, bool do_menus);
+
+/**
* scene_menu_arrange() - Set the position of things in the menu
*
* This updates any items associated with a menu to make sure they are
@@ -62,17 +93,27 @@ int scene_obj_add(struct scene *scn, const char *name, uint id,
*
* @scn: Scene to update
* @menu: Menu to process
+ * Returns: 0 if OK, -ve on error
*/
int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu);
/**
+ * scene_apply_theme() - Apply a theme to a scene
+ *
+ * @scn: Scene to update
+ * @theme: Theme to apply
+ * Returns: 0 if OK, -ve on error
+ */
+int scene_apply_theme(struct scene *scn, struct expo_theme *theme);
+
+/**
* scene_menu_send_key() - Send a key to a menu for processing
*
* @scn: Scene to use
* @menu: Menu to use
* @key: Key code to send (KEY_...)
* @event: Place to put any event which is generated by the key
- * @return 0 if OK, -ENOTTY if there is no current menu item, other -ve on other
+ * Returns: 0 if OK, -ENOTTY if there is no current menu item, other -ve on other
* error
*/
int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
@@ -89,7 +130,7 @@ void scene_menu_destroy(struct scene_obj_menu *menu);
* scene_menu_display() - Display a menu as text
*
* @menu: Menu to display
- * @return 0 if OK, -ENOENT if @id is invalid
+ * Returns: 0 if OK, -ENOENT if @id is invalid
*/
int scene_menu_display(struct scene_obj_menu *menu);
@@ -120,4 +161,41 @@ int scene_render(struct scene *scn);
*/
int scene_send_key(struct scene *scn, int key, struct expo_action *event);
+/**
+ * scene_menu_render() - Render the background behind a menu
+ *
+ * @menu: Menu to render
+ */
+void scene_menu_render(struct scene_obj_menu *menu);
+
+/**
+ * scene_render_deps() - Render an object and its dependencies
+ *
+ * @scn: Scene to render
+ * @id: Object ID to render (or 0 for none)
+ * Returns: 0 if OK, -ve on error
+ */
+int scene_render_deps(struct scene *scn, uint id);
+
+/**
+ * scene_menu_render_deps() - Render a menu and its dependencies
+ *
+ * Renders the menu and all of its attached objects
+ *
+ * @scn: Scene to render
+ * @menu: Menu render
+ * Returns: 0 if OK, -ve on error
+ */
+int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu);
+
+/**
+ * scene_menu_calc_dims() - Calculate the dimensions of a menu
+ *
+ * Updates the width and height of the menu based on its contents
+ *
+ * @menu: Menu to update
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int scene_menu_calc_dims(struct scene_obj_menu *menu);
+
#endif /* __SCENE_INTERNAL_H */
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index 18998e862a..8a355f838c 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -6,7 +6,7 @@
* Written by Simon Glass <sjg@chromium.org>
*/
-#define LOG_CATEGORY LOGC_BOOT
+#define LOG_CATEGORY LOGC_EXPO
#include <common.h>
#include <dm.h>
@@ -33,6 +33,58 @@ void scene_menu_destroy(struct scene_obj_menu *menu)
scene_menuitem_destroy(item);
}
+static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu,
+ int id)
+{
+ struct scene_menitem *item;
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ if (item->id == id)
+ return item;
+ }
+
+ return NULL;
+}
+
+/**
+ * update_pointers() - Update the pointer object and handle highlights
+ *
+ * @menu: Menu to update
+ * @id: ID of menu item to select/deselect
+ * @point: true if @id is being selected, false if it is being deselected
+ */
+static int update_pointers(struct scene_obj_menu *menu, uint id, bool point)
+{
+ struct scene *scn = menu->obj.scene;
+ const bool stack = scn->expo->popup;
+ const struct scene_menitem *item;
+ int ret;
+
+ item = scene_menuitem_find(menu, id);
+ if (!item)
+ return log_msg_ret("itm", -ENOENT);
+
+ /* adjust the pointer object to point to the selected item */
+ if (menu->pointer_id && item && point) {
+ struct scene_obj *label;
+
+ label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
+
+ ret = scene_obj_set_pos(scn, menu->pointer_id,
+ menu->obj.dim.x + 200, label->dim.y);
+ if (ret < 0)
+ return log_msg_ret("ptr", ret);
+ }
+
+ if (stack) {
+ point &= scn->highlight_id == menu->obj.id;
+ scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT,
+ point ? SCENEOF_POINT : 0);
+ }
+
+ return 0;
+}
+
/**
* menu_point_to_item() - Point to a particular menu item
*
@@ -40,18 +92,115 @@ void scene_menu_destroy(struct scene_obj_menu *menu)
*/
static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
{
+ if (menu->cur_item_id)
+ update_pointers(menu, menu->cur_item_id, false);
menu->cur_item_id = item_id;
+ update_pointers(menu, item_id, true);
+}
+
+static int scene_bbox_union(struct scene *scn, uint id, int inset,
+ struct vidconsole_bbox *bbox)
+{
+ struct scene_obj *obj;
+
+ if (!id)
+ return 0;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("obj", -ENOENT);
+ if (bbox->valid) {
+ bbox->x0 = min(bbox->x0, obj->dim.x - inset);
+ bbox->y0 = min(bbox->y0, obj->dim.y);
+ bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
+ bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
+ } else {
+ bbox->x0 = obj->dim.x - inset;
+ bbox->y0 = obj->dim.y;
+ bbox->x1 = obj->dim.x + obj->dim.w + inset;
+ bbox->y1 = obj->dim.y + obj->dim.h;
+ bbox->valid = true;
+ }
+
+ return 0;
+}
+
+/**
+ * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
+ *
+ * @menu: Menu to process
+ * @bbox: Returns bounding box of menu including prompts
+ * @label_bbox: Returns bounding box of labels
+ */
+static void scene_menu_calc_bbox(struct scene_obj_menu *menu,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox)
+{
+ const struct expo_theme *theme = &menu->obj.scene->expo->theme;
+ const struct scene_menitem *item;
+
+ bbox->valid = false;
+ scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox);
+
+ label_bbox->valid = false;
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_bbox_union(menu->obj.scene, item->label_id,
+ theme->menu_inset, bbox);
+ scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox);
+ scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox);
+ scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox);
+
+ /* Get the bounding box of all labels */
+ scene_bbox_union(menu->obj.scene, item->label_id,
+ theme->menu_inset, label_bbox);
+ }
+
+ /*
+ * subtract the final menuitem's gap to keep the insert the same top
+ * and bottom
+ */
+ label_bbox->y1 -= theme->menuitem_gap_y;
+}
+
+int scene_menu_calc_dims(struct scene_obj_menu *menu)
+{
+ struct vidconsole_bbox bbox, label_bbox;
+ const struct scene_menitem *item;
+
+ scene_menu_calc_bbox(menu, &bbox, &label_bbox);
+
+ /* Make all labels the same size */
+ if (label_bbox.valid) {
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_obj_set_size(menu->obj.scene, item->label_id,
+ label_bbox.x1 - label_bbox.x0,
+ label_bbox.y1 - label_bbox.y0);
+ }
+ }
+
+ if (bbox.valid) {
+ menu->obj.dim.w = bbox.x1 - bbox.x0;
+ menu->obj.dim.h = bbox.y1 - bbox.y0;
+ }
+
+ return 0;
}
int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
{
+ const bool open = menu->obj.flags & SCENEOF_OPEN;
+ struct expo *exp = scn->expo;
+ const bool stack = exp->popup;
+ const struct expo_theme *theme = &exp->theme;
struct scene_menitem *item;
- int y, cur_y;
+ uint sel_id;
+ int x, y;
int ret;
- y = menu->obj.y;
+ x = menu->obj.dim.x;
+ y = menu->obj.dim.y;
if (menu->title_id) {
- ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.x, y);
+ ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y);
if (ret < 0)
return log_msg_ret("tit", ret);
@@ -59,7 +208,10 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
if (ret < 0)
return log_msg_ret("hei", ret);
- y += ret * 2;
+ if (stack)
+ x += 200;
+ else
+ y += ret * 2;
}
/*
@@ -68,11 +220,12 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
* small. This can be updated once text measuring is supported in
* vidconsole
*/
- cur_y = -1;
+ sel_id = menu->cur_item_id;
list_for_each_entry(item, &menu->item_head, sibling) {
+ bool selected;
int height;
- ret = scene_obj_get_hw(scn, item->desc_id, NULL);
+ ret = scene_obj_get_hw(scn, item->label_id, NULL);
if (ret < 0)
return log_msg_ret("get", ret);
height = ret;
@@ -81,32 +234,33 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
y += height;
/* select an item if not done already */
- if (!menu->cur_item_id)
- menu_point_to_item(menu, item->id);
+ if (!sel_id)
+ sel_id = item->id;
+
+ selected = sel_id == item->id;
/*
* Put the label on the left, then leave a space for the
* pointer, then the key and the description
*/
- if (item->label_id) {
- ret = scene_obj_set_pos(scn, item->label_id, menu->obj.x,
- y);
- if (ret < 0)
- return log_msg_ret("nam", ret);
- }
-
- ret = scene_obj_set_pos(scn, item->key_id, menu->obj.x + 230,
- y);
+ ret = scene_obj_set_pos(scn, item->label_id,
+ x + theme->menu_inset, y);
if (ret < 0)
- return log_msg_ret("key", ret);
+ return log_msg_ret("nam", ret);
+ scene_obj_set_hide(scn, item->label_id,
+ stack && !open && !selected);
- ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.x + 280,
- y);
- if (ret < 0)
- return log_msg_ret("des", ret);
+ if (item->key_id) {
+ ret = scene_obj_set_pos(scn, item->key_id, x + 230, y);
+ if (ret < 0)
+ return log_msg_ret("key", ret);
+ }
- if (menu->cur_item_id == item->id)
- cur_y = y;
+ if (item->desc_id) {
+ ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y);
+ if (ret < 0)
+ return log_msg_ret("des", ret);
+ }
if (item->preview_id) {
bool hide;
@@ -125,19 +279,12 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
return log_msg_ret("hid", ret);
}
- y += height;
+ if (!stack || open)
+ y += height + theme->menuitem_gap_y;
}
- if (menu->pointer_id && cur_y != -1) {
- /*
- * put the pointer to the right of and level with the item it
- * points to
- */
- ret = scene_obj_set_pos(scn, menu->pointer_id,
- menu->obj.x + 200, cur_y);
- if (ret < 0)
- return log_msg_ret("ptr", ret);
- }
+ if (sel_id)
+ menu_point_to_item(menu, sel_id);
return 0;
}
@@ -158,10 +305,6 @@ int scene_menu(struct scene *scn, const char *name, uint id,
*menup = menu;
INIT_LIST_HEAD(&menu->item_head);
- ret = scene_menu_arrange(scn, menu);
- if (ret)
- return log_msg_ret("pos", ret);
-
return menu->obj.id;
}
@@ -191,6 +334,7 @@ static struct scene_menitem *scene_menu_find_key(struct scene *scn,
int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
struct expo_action *event)
{
+ const bool open = menu->obj.flags & SCENEOF_OPEN;
struct scene_menitem *item, *cur, *key_item;
cur = NULL;
@@ -215,7 +359,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
struct scene_menitem, sibling)) {
item = list_entry(item->sibling.prev,
struct scene_menitem, sibling);
- event->type = EXPOACT_POINT;
+ event->type = EXPOACT_POINT_ITEM;
event->select.id = item->id;
log_debug("up to item %d\n", event->select.id);
}
@@ -224,7 +368,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
if (!list_is_last(&item->sibling, &menu->item_head)) {
item = list_entry(item->sibling.next,
struct scene_menitem, sibling);
- event->type = EXPOACT_POINT;
+ event->type = EXPOACT_POINT_ITEM;
event->select.id = item->id;
log_debug("down to item %d\n", event->select.id);
}
@@ -235,8 +379,13 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
log_debug("select item %d\n", event->select.id);
break;
case BKEY_QUIT:
- event->type = EXPOACT_QUIT;
- log_debug("quit\n");
+ if (scn->expo->popup && open) {
+ event->type = EXPOACT_CLOSE;
+ event->select.id = menu->obj.id;
+ } else {
+ event->type = EXPOACT_QUIT;
+ log_debug("menu quit\n");
+ }
break;
case '0'...'9':
key_item = scene_menu_find_key(scn, menu, key);
@@ -258,14 +407,13 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
{
struct scene_obj_menu *menu;
struct scene_menitem *item;
- int ret;
menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
if (!menu)
return log_msg_ret("find", -ENOENT);
/* Check that the text ID is valid */
- if (!scene_obj_find(scn, desc_id, SCENEOBJT_TEXT))
+ if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT))
return log_msg_ret("txt", -EINVAL);
item = calloc(1, sizeof(struct scene_obj_menu));
@@ -285,10 +433,6 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
item->flags = flags;
list_add_tail(&item->sibling, &menu->item_head);
- ret = scene_menu_arrange(scn, menu);
- if (ret)
- return log_msg_ret("pos", ret);
-
if (itemp)
*itemp = item;
@@ -388,3 +532,49 @@ int scene_menu_display(struct scene_obj_menu *menu)
return -ENOTSUPP;
}
+
+void scene_menu_render(struct scene_obj_menu *menu)
+{
+ struct expo *exp = menu->obj.scene->expo;
+ const struct expo_theme *theme = &exp->theme;
+ struct vidconsole_bbox bbox, label_bbox;
+ struct udevice *dev = exp->display;
+ struct video_priv *vid_priv;
+ struct udevice *cons = exp->cons;
+ struct vidconsole_colour old;
+ enum colour_idx fore, back;
+
+ if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+ fore = VID_BLACK;
+ back = VID_WHITE;
+ } else {
+ fore = VID_LIGHT_GRAY;
+ back = VID_BLACK;
+ }
+
+ scene_menu_calc_bbox(menu, &bbox, &label_bbox);
+ vidconsole_push_colour(cons, fore, back, &old);
+ vid_priv = dev_get_uclass_priv(dev);
+ video_fill_part(dev, label_bbox.x0 - theme->menu_inset,
+ label_bbox.y0 - theme->menu_inset,
+ label_bbox.x1, label_bbox.y1 + theme->menu_inset,
+ vid_priv->colour_fg);
+ vidconsole_pop_colour(cons, &old);
+}
+
+int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
+{
+ struct scene_menitem *item;
+
+ scene_render_deps(scn, menu->title_id);
+ scene_render_deps(scn, menu->cur_item_id);
+ scene_render_deps(scn, menu->pointer_id);
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_render_deps(scn, item->key_id);
+ scene_render_deps(scn, item->label_id);
+ scene_render_deps(scn, item->desc_id);
+ }
+
+ return 0;
+}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index c1941849f9..f6b10e01f8 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -428,6 +428,15 @@ config CMD_ABOOTIMG
See doc/android/boot-image.rst for details.
+config CMD_CEDIT
+ bool "cedit - Configuration editor"
+ depends on CEDIT
+ default y
+ help
+ Provides a command to allow editing of board configuration and
+ providing a UI for the user to adjust settings. Subcommands allow
+ loading and saving of configuration as well as showing an editor.
+
config CMD_ELF
bool "bootelf, bootvx"
default y
diff --git a/cmd/Makefile b/cmd/Makefile
index 6c37521b4e..9f8c0b058b 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_CMD_BUTTON) += button.o
obj-$(CONFIG_CMD_CAT) += cat.o
obj-$(CONFIG_CMD_CACHE) += cache.o
obj-$(CONFIG_CMD_CBFS) += cbfs.o
+obj-$(CONFIG_CMD_CEDIT) += cedit.o
obj-$(CONFIG_CMD_CLK) += clk.o
obj-$(CONFIG_CMD_CLS) += cls.o
obj-$(CONFIG_CMD_CONFIG) += config.o
diff --git a/cmd/cat.c b/cmd/cat.c
index 1273a26b14..b059080193 100644
--- a/cmd/cat.c
+++ b/cmd/cat.c
@@ -17,8 +17,8 @@ static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
char *dev;
char *file;
char *buffer;
- phys_addr_t addr;
- loff_t file_size;
+ ulong file_size;
+ int ret;
if (argc < 4)
return CMD_RET_USAGE;
@@ -27,40 +27,27 @@ static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
dev = argv[2];
file = argv[3];
+ ret = fs_load_alloc(ifname, dev, file, 0, 0, (void **)&buffer,
+ &file_size);
+
// check file exists
- if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
+ switch (ret) {
+ case 0:
+ break;
+ case -ENOMEDIUM:
return CMD_RET_FAILURE;
-
- if (!fs_exists(file)) {
+ case -ENOENT:
log_err("File does not exist: ifname=%s dev=%s file=%s\n", ifname, dev, file);
return CMD_RET_FAILURE;
- }
-
- // get file size
- if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
+ case -E2BIG:
+ log_err("File is too large: ifname=%s dev=%s file=%s\n", ifname, dev, file);
return CMD_RET_FAILURE;
-
- if (fs_size(file, &file_size)) {
- log_err("Cannot read file size: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ case -ENOMEM:
+ log_err("Not enough memory: ifname=%s dev=%s file=%s\n", ifname, dev, file);
return CMD_RET_FAILURE;
- }
-
- // allocate memory for file content
- buffer = calloc(sizeof(char), file_size + 1);
- if (!buffer) {
- log_err("Out of memory\n");
- return CMD_RET_FAILURE;
- }
-
- // map pointer to system memory
- addr = map_to_sysmem(buffer);
-
- // read file to memory
- if (fs_set_blk_dev(ifname, dev, FS_TYPE_ANY))
- return CMD_RET_FAILURE;
-
- if (fs_read(file, addr, 0, 0, &file_size)) {
- log_err("Cannot read file: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+ default:
+ case -EIO:
+ log_err("File-read failed: ifname=%s dev=%s file=%s\n", ifname, dev, file);
return CMD_RET_FAILURE;
}
diff --git a/cmd/cedit.c b/cmd/cedit.c
new file mode 100644
index 0000000000..0cae304c4a
--- /dev/null
+++ b/cmd/cedit.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'cedit' command
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <expo.h>
+#include <fs.h>
+#include <dm/ofnode.h>
+#include <linux/sizes.h>
+
+struct expo *cur_exp;
+
+static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ struct expo *exp;
+ oftree tree;
+ ulong size;
+ void *buf;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size);
+ if (ret) {
+ printf("File not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ tree = oftree_from_fdt(buf);
+ if (!oftree_valid(tree)) {
+ printf("Cannot create oftree\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = expo_build(oftree_root(tree), &exp);
+ oftree_dispose(tree);
+ if (ret) {
+ printf("Failed to build expo: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ cur_exp = exp;
+
+ return 0;
+}
+
+static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ ofnode node;
+ int ret;
+
+ if (!cur_exp) {
+ printf("No expo loaded\n");
+ return CMD_RET_FAILURE;
+ }
+
+ node = ofnode_path("/cedit-theme");
+ if (ofnode_valid(node)) {
+ ret = expo_apply_theme(cur_exp, node);
+ if (ret)
+ return CMD_RET_FAILURE;
+ } else {
+ log_warning("No theme found\n");
+ }
+ ret = cedit_run(cur_exp);
+ if (ret) {
+ log_err("Failed (err=%dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char cedit_help_text[] =
+ "load <interface> <dev[:part]> <filename> - load config editor\n"
+ "cedit run - run config editor";
+#endif /* CONFIG_SYS_LONGHELP */
+
+U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text,
+ U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load),
+ U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run),
+);
diff --git a/common/log.c b/common/log.c
index 7cfc49bc28..6f02a25c59 100644
--- a/common/log.c
+++ b/common/log.c
@@ -31,6 +31,7 @@ static const char *const log_cat_name[] = {
"boot",
"event",
"fs",
+ "expo",
};
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 1ec44d5b33..4cef6c5153 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -30,6 +30,7 @@ CONFIG_AUTOBOOT_STOP_STR_ENABLE=y
CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJpTr9tjZLZQyzqPASBY20xuK5Rent9"
CONFIG_IMAGE_PRE_LOAD=y
CONFIG_IMAGE_PRE_LOAD_SIG=y
+CONFIG_CEDIT=y
CONFIG_CONSOLE_RECORD=y
CONFIG_CONSOLE_RECORD_OUT_SIZE=0x6000
CONFIG_PRE_CONSOLE_BUFFER=y
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index 32dd7f0903..2ac4af232d 100644
--- a/doc/develop/expo.rst
+++ b/doc/develop/expo.rst
@@ -85,6 +85,9 @@ or even the IDs of objects. Programmatic creation of many items in a loop can be
handled by allocating space in the enum for a maximum number of items, then
adding the loop count to the enum values to obtain unique IDs.
+Where dynamic IDs are need, use expo_set_dynamic_start() to set the start value,
+so that they are allocated above the starting (enum) IDs.
+
All text strings are stored in a structure attached to the expo, referenced by
a text ID. This makes it easier at some point to implement multiple languages or
to support Unicode strings.
@@ -97,10 +100,13 @@ objects first, then create the menu item, passing in the relevant IDs.
Creating an expo
----------------
-To create an expo, use `expo_new()` followed by `scene_new()` to create a scene.
-Then add objects to the scene, using functions like `scene_txt_str()` and
-`scene_menu()`. For every menu item, add text and image objects, then create
-the menu item with `scene_menuitem()`, referring to those objects.
+To create an expo programmatically, use `expo_new()` followed by `scene_new()`
+to create a scene. Then add objects to the scene, using functions like
+`scene_txt_str()` and `scene_menu()`. For every menu item, add text and image
+objects, then create the menu item with `scene_menuitem()`, referring to those
+objects.
+
+To create an expo using a description file, see :ref:`expo_format` below.
Layout
------
@@ -152,8 +158,287 @@ such as scanning devices for more bootflows.
Themes
------
-Expo does not itself support themes. The bootflow_menu implement supposed a
-basic theme, applying font sizes to the various text objects in the expo.
+Expo supports simple themes, for setting the font size, for example. Use the
+expo_apply_theme() function to load a theme, passing a node with the required
+properties:
+
+font-size
+ Font size to use for all text (type: u32)
+
+menu-inset
+ Number of pixels to inset the menu on the sides and top (type: u32)
+
+menuitem-gap-y
+ Number of pixels between menu items
+
+Pop-up mode
+-----------
+
+Expos support two modes. The simple mode is used for selecting from a single
+menu, e.g. when choosing with OS to boot. In this mode the menu items are shown
+in a list (label, > pointer, key and description) and can be chosen using arrow
+keys and enter::
+
+ U-Boot Boot Menu
+
+ UP and DOWN to choose, ENTER to select
+
+ mmc1 > 0 Fedora-Workstation-armhfp-31-1.9
+ mmc3 1 Armbian
+
+The popup mode allows multiple menus to be present in a scene. Each is shown
+just as its title and label, as with the `CPU Speed` and `AC Power` menus here::
+
+ Test Configuration
+
+
+ CPU Speed <2 GHz> (highlighted)
+
+ AC Power Always Off
+
+
+ UP and DOWN to choose, ENTER to select
+
+
+.. _expo_format:
+
+Expo Format
+-----------
+
+It can be tedious to create a complex expo using code. Expo supports a
+data-driven approach, where the expo description is in a devicetree file. This
+makes it easier and faster to create and edit the description. An expo builder
+is provided to convert this format into an expo structure.
+
+Layout of the expo scenes is handled automatically, based on a set of simple
+rules. The :doc:`../usage/cmd/cedit` can be used to load a configuration
+and create an expo from it.
+
+Top-level node
+~~~~~~~~~~~~~~
+
+The top-level node has the following properties:
+
+dynamic-start
+ type: u32, optional
+
+ Specifies the start of the dynamically allocated objects. This results in
+ a call to expo_set_dynamic_start().
+
+The top-level node has the following subnodes:
+
+scenes
+ Specifies the scenes in the expo, each one being a subnode
+
+strings
+ Specifies the strings in the expo, each one being a subnode
+
+`scenes` node
+~~~~~~~~~~~~~
+
+Contains a list of scene subnodes. The name of each subnode is passed as the
+name to `scene_new()`.
+
+`strings` node
+~~~~~~~~~~~~~~
+
+Contains a list of string subnodes. The name of each subnode is ignored.
+
+`strings` subnodes
+~~~~~~~~~~~~~~~~~~
+
+Each subnode defines a string which can be used by scenes and objects. Each
+string has an ID number which is used to refer to it.
+
+The `strings` subnodes have the following properties:
+
+id
+ type: u32, required
+
+ Specifies the ID number for the string.
+
+value:
+ type: string, required
+
+ Specifies the string text. For now only a single value is supported. Future
+ work may add support for multiple languages by using a value for each
+ language.
+
+Scene nodes (`scenes` subnodes)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each subnode of the `scenes` node contains a scene description.
+
+Most properties can use either a string or a string ID. For example, a `title`
+property can be used to provide the title for a menu; alternatively a `title-id`
+property can provide the string ID of the title. If both are present, the
+ID takes preference, except that if a string with that ID does not exist, it
+falls back to using the string from the property (`title` in this example). The
+description below shows these are alternative properties with the same
+description.
+
+The scene nodes have the following properties:
+
+id
+ type: u32, required
+
+ Specifies the ID number for the string.
+
+title / title-id
+ type: string / u32, required
+
+ Specifies the title of the scene. This is shown at the top of the scene.
+
+prompt / prompt-id
+ type: string / u32, required
+
+ Specifies a prompt for the scene. This is shown at the bottom of the scene.
+
+The scene nodes have a subnode for each object in the scene.
+
+Object nodes
+~~~~~~~~~~~~
+
+The object-node name is used as the name of the object, e.g. when calling
+`scene_menu()` to create a menu.
+
+Object nodes have the following common properties:
+
+type
+ type: string, required
+
+ Specifies the type of the object. Valid types are:
+
+ "menu"
+ Menu containing items which can be selected by the user
+
+id
+ type: u32, required
+
+ Specifies the ID of the object. This is used when referring to the object.
+
+
+Menu nodes have the following additional properties:
+
+title / title-id
+ type: string / u32, required
+
+ Specifies the title of the menu. This is shown to the left of the area for
+ this menu.
+
+item-id
+ type: u32 list, required
+
+ Specifies the ID for each menu item. These are used for checking which item
+ has been selected.
+
+item-label / item-label-id
+ type: string list / u32 list, required
+
+ Specifies the label for each item in the menu. These are shown to the user.
+ In 'popup' mode these form the items in the menu.
+
+key-label / key-label-id
+ type: string list / u32 list, optional
+
+ Specifies the key for each item in the menu. These are currently only
+ intended for use in simple mode.
+
+desc-label / desc-label-id
+ type: string list / u32 list, optional
+
+ Specifies the description for each item in the menu. These are currently
+ only intended for use in simple mode.
+
+
+Expo layout
+~~~~~~~~~~~
+
+The `expo_arrange()` function can be called to arrange the expo objects in a
+suitable manner. For each scene it puts the title at the top, the prompt at the
+bottom and the objects in order from top to bottom.
+
+Expo format example
+~~~~~~~~~~~~~~~~~~~
+
+This example shows an expo with a single scene consisting of two menus. The
+scene title is specified using a string from the strings table, but all other
+strings are provided inline in the nodes where they are used.
+
+::
+
+ #define ID_PROMPT 1
+ #define ID_SCENE1 2
+ #define ID_SCENE1_TITLE 3
+
+ #define ID_CPU_SPEED 4
+ #define ID_CPU_SPEED_TITLE 5
+ #define ID_CPU_SPEED_1 6
+ #define ID_CPU_SPEED_2 7
+ #define ID_CPU_SPEED_3 8
+
+ #define ID_POWER_LOSS 9
+ #define ID_AC_OFF 10
+ #define ID_AC_ON 11
+ #define ID_AC_MEMORY 12
+
+ #define ID_DYNAMIC_START 13
+
+ &cedit {
+ dynamic-start = <ID_DYNAMIC_START>;
+
+ scenes {
+ main {
+ id = <ID_SCENE1>;
+
+ /* value refers to the matching id in /strings */
+ title-id = <ID_SCENE1_TITLE>;
+
+ /* simple string is used as it is */
+ prompt = "UP and DOWN to choose, ENTER to select";
+
+ /* defines a menu within the scene */
+ cpu-speed {
+ type = "menu";
+ id = <ID_CPU_SPEED>;
+
+ /*
+ * has both string and ID. The string is ignored
+ * if the ID is present and points to a string
+ */
+ title = "CPU speed";
+ title-id = <ID_CPU_SPEED_TITLE>;
+
+ /* menu items as simple strings */
+ item-label = "2 GHz", "2.5 GHz", "3 GHz";
+
+ /* IDs for the menu items */
+ item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
+ ID_CPU_SPEED_3>;
+ };
+
+ power-loss {
+ type = "menu";
+ id = <ID_POWER_LOSS>;
+
+ title = "AC Power";
+ item-label = "Always Off", "Always On",
+ "Memory";
+
+ item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ };
+ };
+ };
+
+ strings {
+ title {
+ id = <ID_SCENE1_TITLE>;
+ value = "Test Configuration";
+ value-es = "configuraciĆ³n de prueba";
+ };
+ };
+ };
+
API documentation
-----------------
@@ -166,12 +451,10 @@ Future ideas
Some ideas for future work:
- Default menu item and a timeout
-- Higher-level / automatic / more flexible layout of objects
- Image formats other than BMP
- Use of ANSI sequences to control a serial terminal
- Colour selection
-- Better support for handling lots of settings, e.g. with multiple menus and
- radio/option widgets
+- Support for more widgets, e.g. text, numeric, radio/option
- Mouse support
- Integrate Nuklear, NxWidgets or some other library for a richer UI
- Optimise rendering by only updating the display with changes since last render
@@ -179,10 +462,10 @@ Some ideas for future work:
- Add a Kconfig option to drop the names to save code / data space
- Add a Kconfig option to disable vidconsole support to save code / data space
- Support both graphical and text menus at the same time on different devices
-- Implement proper measurement of object bounding boxes, to permit more exact
- layout. This would tidy up the layout when Truetype is not used
- Support unicode
- Support curses for proper serial-terminal menus
+- Add support for large menus which need to scroll
+- Add support for reading and writing configuration settings with cedit
.. Simon Glass <sjg@chromium.org>
.. 7-Oct-22
diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst
new file mode 100644
index 0000000000..8e1110c7c7
--- /dev/null
+++ b/doc/usage/cmd/cedit.rst
@@ -0,0 +1,31 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+cedit command
+=============
+
+Synopis
+-------
+
+::
+
+ cedit load <interface> <dev[:part]> <filename>
+ cedit run
+
+Description
+-----------
+
+The *cedit* command is used to load a configuration-editor description and allow
+the user to interact with it.
+
+It makes use of the expo subsystem.
+
+The description is in the form of a devicetree file, as documented at
+:ref:`expo_format`.
+
+Example
+-------
+
+::
+
+ => cedit load hostfs - fred.dtb
+ => cedit run
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index 388e59f173..f2ffd2787a 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -39,6 +39,7 @@ Shell commands
cmd/bootz
cmd/cat
cmd/cbsysinfo
+ cmd/cedit
cmd/cls
cmd/cmp
cmd/coninfo
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index ec574c4460..8df16e56af 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -12,6 +12,7 @@
#include <fdt_support.h>
#include <log.h>
#include <malloc.h>
+#include <of_live.h>
#include <linux/libfdt.h>
#include <dm/of_access.h>
#include <dm/of_addr.h>
@@ -51,6 +52,20 @@ static oftree oftree_ensure(void *fdt)
oftree tree;
int i;
+ if (of_live_active()) {
+ struct device_node *root;
+ int ret;
+
+ ret = unflatten_device_tree(fdt, &root);
+ if (ret) {
+ log_err("Failed to create live tree: err=%d\n", ret);
+ return oftree_null();
+ }
+ tree = oftree_from_np(root);
+
+ return tree;
+ }
+
if (gd->flags & GD_FLG_RELOC) {
i = oftree_find(fdt);
if (i == -1) {
@@ -67,7 +82,7 @@ static oftree oftree_ensure(void *fdt)
}
} else {
if (fdt != gd->fdt_blob) {
- log_debug("Cannot only access control FDT before relocation\n");
+ log_debug("Only the control FDT can be accessed before relocation\n");
return oftree_null();
}
}
@@ -77,6 +92,12 @@ static oftree oftree_ensure(void *fdt)
return tree;
}
+void oftree_dispose(oftree tree)
+{
+ if (of_live_active())
+ of_live_free(tree.np);
+}
+
void *ofnode_lookup_fdt(ofnode node)
{
if (gd->flags & GD_FLG_RELOC) {
@@ -133,6 +154,10 @@ oftree oftree_from_fdt(void *fdt)
if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE))
return oftree_ensure(fdt);
+#ifdef OF_CHECKS
+ if (of_live_active())
+ return oftree_null();
+#endif
tree.fdt = fdt;
return tree;
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 712119c341..31027f3d99 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -1474,7 +1474,7 @@ static int gpio_post_bind(struct udevice *dev)
}
#endif
- if (CONFIG_IS_ENABLED(GPIO_HOG)) {
+ if (CONFIG_IS_ENABLED(GPIO_HOG) && dev_has_ofnode(dev)) {
struct udevice *child;
ofnode node;
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c
index 6b5390136a..0f9bb49e44 100644
--- a/drivers/video/console_truetype.c
+++ b/drivers/video/console_truetype.c
@@ -62,10 +62,43 @@ static double tt_sqrt(double value)
return lo;
}
+static double tt_fmod(double x, double y)
+{
+ double rem;
+
+ if (y == 0.0)
+ return 0.0;
+ rem = x - (x / y) * y;
+
+ return rem;
+}
+
+/* dummy implementation */
+static double tt_pow(double x, double y)
+{
+ return 0;
+}
+
+/* dummy implementation */
+static double tt_cos(double val)
+{
+ return 0;
+}
+
+/* dummy implementation */
+static double tt_acos(double val)
+{
+ return 0;
+}
+
#define STBTT_ifloor tt_floor
#define STBTT_iceil tt_ceil
#define STBTT_fabs tt_fabs
#define STBTT_sqrt tt_sqrt
+#define STBTT_pow tt_pow
+#define STBTT_fmod tt_fmod
+#define STBTT_cos tt_cos
+#define STBTT_acos tt_acos
#define STBTT_malloc(size, u) ((void)(u), malloc(size))
#define STBTT_free(size, u) ((void)(u), free(size))
#define STBTT_assert(x)
@@ -154,33 +187,33 @@ static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
end = line + met->font_size * vid_priv->line_length;
switch (vid_priv->bpix) {
-#ifdef CONFIG_VIDEO_BPP8
case VIDEO_BPP8: {
u8 *dst;
- for (dst = line; dst < (u8 *)end; ++dst)
- *dst = clr;
+ if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
+ for (dst = line; dst < (u8 *)end; ++dst)
+ *dst = clr;
+ }
break;
}
-#endif
-#ifdef CONFIG_VIDEO_BPP16
case VIDEO_BPP16: {
u16 *dst = line;
- for (dst = line; dst < (u16 *)end; ++dst)
- *dst = clr;
+ if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
+ for (dst = line; dst < (u16 *)end; ++dst)
+ *dst = clr;
+ }
break;
}
-#endif
-#ifdef CONFIG_VIDEO_BPP32
case VIDEO_BPP32: {
u32 *dst = line;
- for (dst = line; dst < (u32 *)end; ++dst)
- *dst = clr;
+ if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
+ for (dst = line; dst < (u32 *)end; ++dst)
+ *dst = clr;
+ }
break;
}
-#endif
default:
return -ENOSYS;
}
@@ -256,7 +289,7 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
*/
x_shift = xpos - (double)tt_floor(xpos);
xpos += advance * met->scale;
- width_frac = (int)VID_TO_POS(xpos);
+ width_frac = (int)VID_TO_POS(advance * met->scale);
if (x + width_frac >= vc_priv->xsize_frac)
return -EAGAIN;
@@ -317,52 +350,52 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
end = dst;
}
break;
-#ifdef CONFIG_VIDEO_BPP16
case VIDEO_BPP16: {
uint16_t *dst = (uint16_t *)line + xoff;
int i;
- for (i = 0; i < width; i++) {
- int val = *bits;
- int out;
-
- if (vid_priv->colour_bg)
- val = 255 - val;
- out = val >> 3 |
- (val >> 2) << 5 |
- (val >> 3) << 11;
- if (vid_priv->colour_fg)
- *dst++ |= out;
- else
- *dst++ &= out;
- bits++;
+ if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
+ for (i = 0; i < width; i++) {
+ int val = *bits;
+ int out;
+
+ if (vid_priv->colour_bg)
+ val = 255 - val;
+ out = val >> 3 |
+ (val >> 2) << 5 |
+ (val >> 3) << 11;
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ bits++;
+ }
+ end = dst;
}
- end = dst;
break;
}
-#endif
-#ifdef CONFIG_VIDEO_BPP32
case VIDEO_BPP32: {
u32 *dst = (u32 *)line + xoff;
int i;
- for (i = 0; i < width; i++) {
- int val = *bits;
- int out;
-
- if (vid_priv->colour_bg)
- val = 255 - val;
- out = val | val << 8 | val << 16;
- if (vid_priv->colour_fg)
- *dst++ |= out;
- else
- *dst++ &= out;
- bits++;
+ if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
+ for (i = 0; i < width; i++) {
+ int val = *bits;
+ int out;
+
+ if (vid_priv->colour_bg)
+ val = 255 - val;
+ out = val | val << 8 | val << 16;
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ bits++;
+ }
+ end = dst;
}
- end = dst;
break;
}
-#endif
default:
free(data);
return -ENOSYS;
@@ -379,72 +412,6 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
}
/**
- * console_truetype_erase() - Erase a character
- *
- * This is used for backspace. We erase a square of the display within the
- * given bounds.
- *
- * @dev: Device to update
- * @xstart: X start position in pixels from the left
- * @ystart: Y start position in pixels from the top
- * @xend: X end position in pixels from the left
- * @yend: Y end position in pixels from the top
- * @clr: Value to write
- * Return: 0 if OK, -ENOSYS if the display depth is not supported
- */
-static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
- int xend, int yend, int clr)
-{
- struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
- void *start, *line;
- int pixels = xend - xstart;
- int row, i, ret;
-
- start = vid_priv->fb + ystart * vid_priv->line_length;
- start += xstart * VNBYTES(vid_priv->bpix);
- line = start;
- for (row = ystart; row < yend; row++) {
- switch (vid_priv->bpix) {
-#ifdef CONFIG_VIDEO_BPP8
- case VIDEO_BPP8: {
- uint8_t *dst = line;
-
- for (i = 0; i < pixels; i++)
- *dst++ = clr;
- break;
- }
-#endif
-#ifdef CONFIG_VIDEO_BPP16
- case VIDEO_BPP16: {
- uint16_t *dst = line;
-
- for (i = 0; i < pixels; i++)
- *dst++ = clr;
- break;
- }
-#endif
-#ifdef CONFIG_VIDEO_BPP32
- case VIDEO_BPP32: {
- uint32_t *dst = line;
-
- for (i = 0; i < pixels; i++)
- *dst++ = clr;
- break;
- }
-#endif
- default:
- return -ENOSYS;
- }
- line += vid_priv->line_length;
- }
- ret = vidconsole_sync_copy(dev, start, line);
- if (ret)
- return ret;
-
- return 0;
-}
-
-/**
* console_truetype_backspace() - Handle a backspace operation
*
* This clears the previous character so that the console looks as if it had
@@ -482,9 +449,9 @@ static int console_truetype_backspace(struct udevice *dev)
else
xend = vid_priv->xsize;
- console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
- xend, pos->ypos + vc_priv->y_charsize,
- vid_priv->colour_bg);
+ video_fill_part(vid_dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
+ xend, pos->ypos + vc_priv->y_charsize,
+ vid_priv->colour_bg);
/* Move the cursor back to where it was when we pushed this record */
vc_priv->xcur_frac = pos->xpos_frac;
@@ -680,8 +647,8 @@ static void select_metrics(struct udevice *dev, struct console_tt_metrics *met)
vc_priv->tab_width_frac = VID_TO_POS(met->font_size) * 8 / 2;
}
-static int truetype_select_font(struct udevice *dev, const char *name,
- uint size)
+static int get_metrics(struct udevice *dev, const char *name, uint size,
+ struct console_tt_metrics **metp)
{
struct console_tt_priv *priv = dev_get_priv(dev);
struct console_tt_metrics *met;
@@ -719,11 +686,70 @@ static int truetype_select_font(struct udevice *dev, const char *name,
met = priv->metrics;
}
+ *metp = met;
+
+ return 0;
+}
+
+static int truetype_select_font(struct udevice *dev, const char *name,
+ uint size)
+{
+ struct console_tt_metrics *met;
+ int ret;
+
+ ret = get_metrics(dev, name, size, &met);
+ if (ret)
+ return log_msg_ret("sel", ret);
+
select_metrics(dev, met);
return 0;
}
+int truetype_measure(struct udevice *dev, const char *name, uint size,
+ const char *text, struct vidconsole_bbox *bbox)
+{
+ struct console_tt_metrics *met;
+ stbtt_fontinfo *font;
+ int lsb, advance;
+ const char *s;
+ int width;
+ int last;
+ int ret;
+
+ ret = get_metrics(dev, name, size, &met);
+ if (ret)
+ return log_msg_ret("sel", ret);
+
+ bbox->valid = false;
+ if (!*text)
+ return 0;
+
+ font = &met->font;
+ width = 0;
+ for (last = 0, s = text; *s; s++) {
+ int ch = *s;
+
+ /* Used kerning to fine-tune the position of this character */
+ if (last)
+ width += stbtt_GetCodepointKernAdvance(font, last, ch);
+
+ /* First get some basic metrics about this character */
+ stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
+
+ width += advance;
+ last = ch;
+ }
+
+ bbox->valid = true;
+ bbox->x0 = 0;
+ bbox->y0 = 0;
+ bbox->x1 = tt_ceil((double)width * met->scale);
+ bbox->y1 = met->font_size;
+
+ return 0;
+}
+
const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep)
{
struct console_tt_priv *priv = dev_get_priv(dev);
@@ -775,6 +801,7 @@ struct vidconsole_ops console_truetype_ops = {
.get_font = console_truetype_get_font,
.get_font_size = console_truetype_get_font_size,
.select_font = truetype_select_font,
+ .measure = truetype_measure,
};
U_BOOT_DRIVER(vidconsole_truetype) = {
diff --git a/drivers/video/stb_truetype.h b/drivers/video/stb_truetype.h
index 438bfce468..c6973bb353 100644
--- a/drivers/video/stb_truetype.h
+++ b/drivers/video/stb_truetype.h
@@ -1,11 +1,21 @@
-// stb_truetype.h - v1.08 - public domain
-// authored from 2009-2015 by Sean Barrett / RAD Game Tools
+// stb_truetype.h - v1.26 - public domain
+// authored from 2009-2021 by Sean Barrett / RAD Game Tools
+//
+// =======================================================================
+//
+// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
+//
+// This library does no range checking of the offsets found in the file,
+// meaning an attacker can use it to read arbitrary memory.
+//
+// =======================================================================
//
// This library processes TrueType files:
// parse files
// extract glyph metrics
// extract glyph shapes
// render glyphs to one-channel bitmaps with antialiasing (box filter)
+// render glyphs to one-channel SDF bitmaps (signed-distance field/function)
//
// Todo:
// non-MS cmaps
@@ -20,58 +30,68 @@
//
// Mikko Mononen: compound shape support, more cmap formats
// Tor Andersson: kerning, subpixel rendering
-//
-// Bug/warning reports/fixes:
-// "Zer" on mollyrocket (with fix)
-// Cass Everitt
-// stoiko (Haemimont Games)
-// Brian Hook
-// Walter van Niftrik
-// David Gow
-// David Given
-// Ivan-Assen Ivanov
-// Anthony Pesch
-// Johan Duparc
-// Hou Qiming
-// Fabian "ryg" Giesen
-// Martins Mozeiko
-// Cap Petschulat
-// Omar Cornut
-// github:aloucks
-// Peter LaValle
-// Sergey Popov
-// Giumo X. Clanjor
-// Higor Euripedes
+// Dougall Johnson: OpenType / Type 2 font handling
+// Daniel Ribeiro Maciel: basic GPOS-based kerning
//
// Misc other:
// Ryan Gordon
+// Simon Glass
+// github:IntellectualKitty
+// Imanol Celaya
+// Daniel Ribeiro Maciel
+//
+// Bug/warning reports/fixes:
+// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
+// Cass Everitt Martins Mozeiko github:aloucks
+// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
+// Brian Hook Omar Cornut github:vassvik
+// Walter van Niftrik Ryan Griege
+// David Gow Peter LaValle
+// David Given Sergey Popov
+// Ivan-Assen Ivanov Giumo X. Clanjor
+// Anthony Pesch Higor Euripedes
+// Johan Duparc Thomas Fields
+// Hou Qiming Derek Vinyard
+// Rob Loach Cort Stratton
+// Kenney Phillis Jr. Brian Costabile
+// Ken Voskuil (kaesve)
//
// VERSION HISTORY
//
+// 1.26 (2021-08-28) fix broken rasterizer
+// 1.25 (2021-07-11) many fixes
+// 1.24 (2020-02-05) fix warning
+// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
+// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
+// 1.21 (2019-02-25) fix warning
+// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
+// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
+// 1.18 (2018-01-29) add missing function
+// 1.17 (2017-07-23) make more arguments const; doc fix
+// 1.16 (2017-07-12) SDF support
+// 1.15 (2017-03-03) make more arguments const
+// 1.14 (2017-01-16) num-fonts-in-TTC function
+// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
+// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
// variant PackFontRanges to pack and render in separate phases;
// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
// fixed an assert() bug in the new rasterizer
// replace assert() with STBTT_assert() in new rasterizer
-// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
-// also more precise AA rasterizer, except if shapes overlap
-// remove need for STBTT_sort
-// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
-// 1.04 (2015-04-15) typo in example
-// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
//
// Full history can be found at the end of this file.
//
// LICENSE
//
-// This software is in the public domain. Where that dedication is not
-// recognized, you are granted a perpetual, irrevocable license to copy,
-// distribute, and modify this file as you see fit.
+// See end of file for license information.
//
// USAGE
//
-// Include this file in whatever places neeed to refer to it. In ONE C/C++
+// Include this file in whatever places need to refer to it. In ONE C/C++
// file, write:
// #define STB_TRUETYPE_IMPLEMENTATION
// before the #include of this file. This expands out the actual
@@ -87,14 +107,15 @@
// Improved 3D API (more shippable):
// #include "stb_rect_pack.h" -- optional, but you really want it
// stbtt_PackBegin()
-// stbtt_PackSetOversample() -- for improved quality on small fonts
+// stbtt_PackSetOversampling() -- for improved quality on small fonts
// stbtt_PackFontRanges() -- pack and renders
// stbtt_PackEnd()
// stbtt_GetPackedQuad()
//
// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
// stbtt_InitFont()
-// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
+// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
+// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections
//
// Render a unicode codepoint to a bitmap
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
@@ -104,6 +125,7 @@
// Character advance/positioning
// stbtt_GetCodepointHMetrics()
// stbtt_GetFontVMetrics()
+// stbtt_GetFontVMetricsOS2()
// stbtt_GetCodepointKernAdvance()
//
// Starting with version 1.06, the rasterizer was replaced with a new,
@@ -159,7 +181,7 @@
// measurement for describing font size, defined as 72 points per inch.
// stb_truetype provides a point API for compatibility. However, true
// "per inch" conventions don't make much sense on computer displays
-// since they different monitors have different number of pixels per
+// since different monitors have different number of pixels per
// inch. For example, Windows traditionally uses a convention that
// there are 96 pixels per inch, thus making 'inch' measurements have
// nothing to do with inches, and thus effectively defining a point to
@@ -169,6 +191,39 @@
// for non-commercial fonts, thus making fonts scaled in points
// according to the TrueType spec incoherently sized in practice.
//
+// DETAILED USAGE:
+//
+// Scale:
+// Select how high you want the font to be, in points or pixels.
+// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
+// a scale factor SF that will be used by all other functions.
+//
+// Baseline:
+// You need to select a y-coordinate that is the baseline of where
+// your text will appear. Call GetFontBoundingBox to get the baseline-relative
+// bounding box for all characters. SF*-y0 will be the distance in pixels
+// that the worst-case character could extend above the baseline, so if
+// you want the top edge of characters to appear at the top of the
+// screen where y=0, then you would set the baseline to SF*-y0.
+//
+// Current point:
+// Set the current point where the first character will appear. The
+// first character could extend left of the current point; this is font
+// dependent. You can either choose a current point that is the leftmost
+// point and hope, or add some padding, or check the bounding box or
+// left-side-bearing of the first character to be displayed and set
+// the current point based on that.
+//
+// Displaying a character:
+// Compute the bounding box of the character. It will contain signed values
+// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
+// then the character should be displayed in the rectangle from
+// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
+//
+// Advancing for the next character:
+// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
+//
+//
// ADVANCED USAGE
//
// Quality:
@@ -203,19 +258,6 @@
// recommend it.
//
//
-// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
-//
-// Documentation & header file 520 LOC \___ 660 LOC documentation
-// Sample code 140 LOC /
-// Truetype parsing 620 LOC ---- 620 LOC TrueType
-// Software rasterization 240 LOC \ .
-// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
-// Bitmap management 100 LOC /
-// Baked bitmap interface 70 LOC /
-// Font name matching & access 150 LOC ---- 150
-// C runtime library abstraction 60 LOC ---- 60
-//
-//
// PERFORMANCE MEASUREMENTS FOR 1.06:
//
// 32-bit 64-bit
@@ -230,8 +272,8 @@
//// SAMPLE PROGRAMS
////
//
-// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
-//
+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
+// See "tests/truetype_demo_win32.c" for a complete version.
#if 0
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h"
@@ -257,6 +299,8 @@ void my_stbtt_initfont(void)
void my_stbtt_print(float x, float y, char *text)
{
// assume orthographic projection with units = screen pixels, origin at top left
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS);
@@ -264,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
}
++text;
}
@@ -305,7 +349,7 @@ int main(int argc, char **argv)
}
return 0;
}
-#endif
+#endif
//
// Output:
//
@@ -319,9 +363,9 @@ int main(int argc, char **argv)
// :@@. M@M
// @@@o@@@@
// :M@@V:@@.
-//
+//
//////////////////////////////////////////////////////////////////////////////
-//
+//
// Complete program: print "Hello World!" banner, with bugs
//
#if 0
@@ -375,7 +419,8 @@ int main(int arg, char **argv)
//// INTEGRATION WITH YOUR CODEBASE
////
//// The following sections allow you to supply alternate definitions
-//// of C library functions used by stb_truetype.
+//// of C library functions used by stb_truetype, e.g. if you don't
+//// link with the C runtime library.
#ifdef STB_TRUETYPE_IMPLEMENTATION
// #define your own (u)stbtt_int8/16/32 before including to override this
@@ -391,7 +436,7 @@ int main(int arg, char **argv)
typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
+ // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
#ifndef STBTT_ifloor
#include <math.h>
#define STBTT_ifloor(x) ((int) floor(x))
@@ -401,6 +446,18 @@ int main(int arg, char **argv)
#ifndef STBTT_sqrt
#include <math.h>
#define STBTT_sqrt(x) sqrt(x)
+ #define STBTT_pow(x,y) pow(x,y)
+ #endif
+
+ #ifndef STBTT_fmod
+ #include <math.h>
+ #define STBTT_fmod(x,y) fmod(x,y)
+ #endif
+
+ #ifndef STBTT_cos
+ #include <math.h>
+ #define STBTT_cos(x) cos(x)
+ #define STBTT_acos(x) acos(x)
#endif
#ifndef STBTT_fabs
@@ -452,6 +509,14 @@ int main(int arg, char **argv)
extern "C" {
#endif
+// private structure
+typedef struct
+{
+ unsigned char *data;
+ int cursor;
+ int size;
+} stbtt__buf;
+
//////////////////////////////////////////////////////////////////////////////
//
// TEXTURE BAKING API
@@ -481,7 +546,7 @@ typedef struct
float x1,y1,s1,t1; // bottom-right
} stbtt_aligned_quad;
-STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
+STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above
int char_index, // character to display
float *xpos, float *ypos, // pointers to current position in screen pixel space
stbtt_aligned_quad *q, // output: quad to draw
@@ -496,6 +561,9 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, //
//
// It's inefficient; you might want to c&p it and optimize it.
+STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
+// Query the font vertical metrics without having to create a font first.
+
//////////////////////////////////////////////////////////////////////////////
//
@@ -520,7 +588,7 @@ typedef struct stbrp_rect stbrp_rect;
STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
// Initializes a packing context stored in the passed-in stbtt_pack_context.
// Future calls using this context will pack characters into the bitmap passed
-// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
+// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
// the distance from one row to the next (or 0 to mean they are packed tightly
// together). "padding" is the amount of padding to leave between each
// character (normally you want '1' for bitmaps you'll use as textures with
@@ -533,7 +601,7 @@ STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
#define STBTT_POINT_SIZE(x) (-(x))
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
// Creates character bitmaps from the font_index'th font found in fontdata (use
// font_index=0 if you don't know what that is). It creates num_chars_in_range
@@ -558,7 +626,7 @@ typedef struct
unsigned char h_oversample, v_oversample; // don't set these, they're used internally
} stbtt_pack_range;
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
// Creates character bitmaps from multiple ranges of characters stored in
// ranges. This will usually create a better-packed bitmap than multiple
// calls to stbtt_PackFontRange. Note that you can call this multiple
@@ -580,19 +648,25 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
// To use with PackFontRangesGather etc., you must set it before calls
// call to PackFontRangesGatherRects.
-STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above
+STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
+// If skip != 0, this tells stb_truetype to skip any codepoints for which
+// there is no corresponding glyph. If skip=0, which is the default, then
+// codepoints without a glyph recived the font's "missing character" glyph,
+// typically an empty box by convention.
+
+STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
int char_index, // character to display
float *xpos, float *ypos, // pointers to current position in screen pixel space
stbtt_aligned_quad *q, // output: quad to draw
int align_to_integer);
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
// Calling these functions in sequence is roughly equivalent to calling
// stbtt_PackFontRanges(). If you more control over the packing of multiple
// fonts, or if you want to pack custom data into a font texture, take a look
-// at the source to of stbtt_PackFontRanges() and create a custom version
+// at the source to of stbtt_PackFontRanges() and create a custom version
// using these functions, e.g. call GatherRects multiple times,
// building up a single array of rects, then call PackRects once,
// then call RenderIntoRects repeatedly. This may result in a
@@ -608,6 +682,7 @@ struct stbtt_pack_context {
int height;
int stride_in_bytes;
int padding;
+ int skip_missing;
unsigned int h_oversample, v_oversample;
unsigned char *pixels;
void *nodes;
@@ -619,18 +694,23 @@ struct stbtt_pack_context {
//
//
+STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
+// This function will determine the number of fonts in a font file. TrueType
+// collection (.ttc) files may contain multiple fonts, while TrueType font
+// (.ttf) files only contain one font. The number of fonts can be used for
+// indexing with the previous function where the index is between zero and one
+// less than the total fonts. If an error occurs, -1 is returned.
+
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
// Each .ttf/.ttc file may have more than one font. Each font has a sequential
// index number starting from 0. Call this function to get the font offset for
// a given index; it returns -1 if the index is out of range. A regular .ttf
// file will only define one font and it always be at offset 0, so it will
-// return '0' for index 0, and -1 for all other indices. You can just skip
-// this step if you know it's that kind of font.
+// return '0' for index 0, and -1 for all other indices.
-
-// The following structure is defined publically so you can declare one on
+// The following structure is defined publicly so you can declare one on
// the stack or as a global or etc, but you should treat it as opaque.
-typedef struct stbtt_fontinfo
+struct stbtt_fontinfo
{
void * userdata;
unsigned char * data; // pointer to .ttf file
@@ -638,10 +718,17 @@ typedef struct stbtt_fontinfo
int numGlyphs; // number of glyphs, needed for range checking
- int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
+ int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
-} stbtt_fontinfo;
+
+ stbtt__buf cff; // cff font data
+ stbtt__buf charstrings; // the charstring index
+ stbtt__buf gsubrs; // global charstring subroutines index
+ stbtt__buf subrs; // private charstring subroutines index
+ stbtt__buf fontdicts; // array of font dicts
+ stbtt__buf fdselect; // map from glyph to fontdict
+};
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
// Given an offset into the file that defines a font, this function builds
@@ -660,6 +747,7 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
// and you want a speed-up, call this function with the character you're
// going to process, then use glyph-based functions instead of the
// codepoint-based functions.
+// Returns 0 if the character codepoint is not defined in the font.
//////////////////////////////////////////////////////////////////////////////
@@ -688,6 +776,12 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in
// these are expressed in unscaled coordinates, so you must multiply by
// the scale factor for a given size
+STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);
+// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
+// table (specific to MS/Windows TTF files).
+//
+// Returns 1 on success (table present), 0 on failure.
+
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
// the bounding box around all possible characters
@@ -707,6 +801,18 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
// as above, but takes one or more glyph indices for greater efficiency
+typedef struct stbtt_kerningentry
+{
+ int glyph1; // use stbtt_FindGlyphIndex
+ int glyph2;
+ int advance;
+} stbtt_kerningentry;
+
+STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info);
+STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);
+// Retrieves a complete list of all of the kerning pairs provided by the font
+// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
+// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
//////////////////////////////////////////////////////////////////////////////
//
@@ -718,7 +824,8 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in
enum {
STBTT_vmove=1,
STBTT_vline,
- STBTT_vcurve
+ STBTT_vcurve,
+ STBTT_vcubic
};
#endif
@@ -727,7 +834,7 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in
#define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
typedef struct
{
- stbtt_vertex_type x,y,cx,cy;
+ stbtt_vertex_type x,y,cx,cy,cx1,cy1;
unsigned char type,padding;
} stbtt_vertex;
#endif
@@ -740,7 +847,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
// returns # of vertices and fills *vertices with the pointer to them
// these are expressed in "unscaled" coordinates
//
-// The shape is a series of countours. Each one starts with
+// The shape is a series of contours. Each one starts with
// a STBTT_moveto, then consists of a series of mixed
// STBTT_lineto and STBTT_curveto segments. A lineto
// draws a line from previous endpoint to its x,y; a curveto
@@ -750,6 +857,12 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// frees the data allocated above
+STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);
+STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
+STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
+// fills svg with the character's SVG data.
+// returns data size or 0 if SVG not found.
+
//////////////////////////////////////////////////////////////////////////////
//
// BITMAP RENDERING
@@ -781,6 +894,10 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns
// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
// shift for the character
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
+// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
+// is performed (see stbtt_PackSetOversampling)
+
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
// get the bbox of the bitmap centered around the glyph origin; so the
// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
@@ -798,6 +915,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
@@ -822,6 +940,64 @@ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap
//////////////////////////////////////////////////////////////////////////////
//
+// Signed Distance Function (or Field) rendering
+
+STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
+// frees the SDF bitmap allocated below
+
+STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
+// These functions compute a discretized SDF field for a single character, suitable for storing
+// in a single-channel texture, sampling with bilinear filtering, and testing against
+// larger than some threshold to produce scalable fonts.
+// info -- the font
+// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
+// glyph/codepoint -- the character to generate the SDF for
+// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
+// which allows effects like bit outlines
+// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
+// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
+// if positive, > onedge_value is inside; if negative, < onedge_value is inside
+// width,height -- output height & width of the SDF bitmap (including padding)
+// xoff,yoff -- output origin of the character
+// return value -- a 2D array of bytes 0..255, width*height in size
+//
+// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
+// optimal use of the limited 0..255 for your application, trading off precision
+// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
+//
+// Example:
+// scale = stbtt_ScaleForPixelHeight(22)
+// padding = 5
+// onedge_value = 180
+// pixel_dist_scale = 180/5.0 = 36.0
+//
+// This will create an SDF bitmap in which the character is about 22 pixels
+// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
+// shape, sample the SDF at each pixel and fill the pixel if the SDF value
+// is greater than or equal to 180/255. (You'll actually want to antialias,
+// which is beyond the scope of this example.) Additionally, you can compute
+// offset outlines (e.g. to stroke the character border inside & outside,
+// or only outside). For example, to fill outside the character up to 3 SDF
+// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
+// choice of variables maps a range from 5 pixels outside the shape to
+// 2 pixels inside the shape to 0..255; this is intended primarily for apply
+// outside effects only (the interior range is needed to allow proper
+// antialiasing of the font at *smaller* sizes)
+//
+// The function computes the SDF analytically at each SDF pixel, not by e.g.
+// building a higher-res bitmap and approximating it. In theory the quality
+// should be as high as possible for an SDF of this size & representation, but
+// unclear if this is true in practice (perhaps building a higher-res bitmap
+// and computing from that can allow drop-out prevention).
+//
+// The algorithm has not been optimized at all, so expect it to be slow
+// if computing lots of characters or very large sizes.
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
// Finding the right font...
//
// You should really just solve this offline, keep your own tables
@@ -943,6 +1119,158 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS
#define STBTT_RASTERIZER_VERSION 2
#endif
+#ifdef _MSC_VER
+#define STBTT__NOTUSED(v) (void)(v)
+#else
+#define STBTT__NOTUSED(v) (void)sizeof(v)
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//
+// stbtt__buf helpers to parse data from file
+//
+
+static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
+{
+ if (b->cursor >= b->size)
+ return 0;
+ return b->data[b->cursor++];
+}
+
+static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
+{
+ if (b->cursor >= b->size)
+ return 0;
+ return b->data[b->cursor];
+}
+
+static void stbtt__buf_seek(stbtt__buf *b, int o)
+{
+ STBTT_assert(!(o > b->size || o < 0));
+ b->cursor = (o > b->size || o < 0) ? b->size : o;
+}
+
+static void stbtt__buf_skip(stbtt__buf *b, int o)
+{
+ stbtt__buf_seek(b, b->cursor + o);
+}
+
+static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
+{
+ stbtt_uint32 v = 0;
+ int i;
+ STBTT_assert(n >= 1 && n <= 4);
+ for (i = 0; i < n; i++)
+ v = (v << 8) | stbtt__buf_get8(b);
+ return v;
+}
+
+static stbtt__buf stbtt__new_buf(const void *p, size_t size)
+{
+ stbtt__buf r;
+ STBTT_assert(size < 0x40000000);
+ r.data = (stbtt_uint8*) p;
+ r.size = (int) size;
+ r.cursor = 0;
+ return r;
+}
+
+#define stbtt__buf_get16(b) stbtt__buf_get((b), 2)
+#define stbtt__buf_get32(b) stbtt__buf_get((b), 4)
+
+static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
+{
+ stbtt__buf r = stbtt__new_buf(NULL, 0);
+ if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
+ r.data = b->data + o;
+ r.size = s;
+ return r;
+}
+
+static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
+{
+ int count, start, offsize;
+ start = b->cursor;
+ count = stbtt__buf_get16(b);
+ if (count) {
+ offsize = stbtt__buf_get8(b);
+ STBTT_assert(offsize >= 1 && offsize <= 4);
+ stbtt__buf_skip(b, offsize * count);
+ stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
+ }
+ return stbtt__buf_range(b, start, b->cursor - start);
+}
+
+static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
+{
+ int b0 = stbtt__buf_get8(b);
+ if (b0 >= 32 && b0 <= 246) return b0 - 139;
+ else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
+ else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
+ else if (b0 == 28) return stbtt__buf_get16(b);
+ else if (b0 == 29) return stbtt__buf_get32(b);
+ STBTT_assert(0);
+ return 0;
+}
+
+static void stbtt__cff_skip_operand(stbtt__buf *b) {
+ int v, b0 = stbtt__buf_peek8(b);
+ STBTT_assert(b0 >= 28);
+ if (b0 == 30) {
+ stbtt__buf_skip(b, 1);
+ while (b->cursor < b->size) {
+ v = stbtt__buf_get8(b);
+ if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
+ break;
+ }
+ } else {
+ stbtt__cff_int(b);
+ }
+}
+
+static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
+{
+ stbtt__buf_seek(b, 0);
+ while (b->cursor < b->size) {
+ int start = b->cursor, end, op;
+ while (stbtt__buf_peek8(b) >= 28)
+ stbtt__cff_skip_operand(b);
+ end = b->cursor;
+ op = stbtt__buf_get8(b);
+ if (op == 12) op = stbtt__buf_get8(b) | 0x100;
+ if (op == key) return stbtt__buf_range(b, start, end-start);
+ }
+ return stbtt__buf_range(b, 0, 0);
+}
+
+static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
+{
+ int i;
+ stbtt__buf operands = stbtt__dict_get(b, key);
+ for (i = 0; i < outcount && operands.cursor < operands.size; i++)
+ out[i] = stbtt__cff_int(&operands);
+}
+
+static int stbtt__cff_index_count(stbtt__buf *b)
+{
+ stbtt__buf_seek(b, 0);
+ return stbtt__buf_get16(b);
+}
+
+static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
+{
+ int count, offsize, start, end;
+ stbtt__buf_seek(&b, 0);
+ count = stbtt__buf_get16(&b);
+ offsize = stbtt__buf_get8(&b);
+ STBTT_assert(i >= 0 && i < count);
+ STBTT_assert(offsize >= 1 && offsize <= 4);
+ stbtt__buf_skip(&b, i*offsize);
+ start = stbtt__buf_get(&b, offsize);
+ end = stbtt__buf_get(&b, offsize);
+ return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
+}
+
//////////////////////////////////////////////////////////////////////////
//
// accessors to parse data from file
@@ -955,32 +1283,22 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS
#define ttCHAR(p) (* (stbtt_int8 *) (p))
#define ttFixed(p) ttLONG(p)
-#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
-
- #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
- #define ttSHORT(p) (* (stbtt_int16 *) (p))
- #define ttULONG(p) (* (stbtt_uint32 *) (p))
- #define ttLONG(p) (* (stbtt_int32 *) (p))
-
-#else
-
- static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
- static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
- static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
- static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-
-#endif
+static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
+static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
-static int stbtt__isfont(const stbtt_uint8 *font)
+static int stbtt__isfont(stbtt_uint8 *font)
{
// check the version number
if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
+ if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts
return 0;
}
@@ -998,7 +1316,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart,
return 0;
}
-STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
+static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
{
// if it's just a font, there's only one valid index
if (stbtt__isfont(font_collection))
@@ -1017,14 +1335,59 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection,
return -1;
}
-STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
+static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
+{
+ // if it's just a font, there's only one valid font
+ if (stbtt__isfont(font_collection))
+ return 1;
+
+ // check if it's a TTC
+ if (stbtt_tag(font_collection, "ttcf")) {
+ // version 1?
+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
+ return ttLONG(font_collection+8);
+ }
+ }
+ return 0;
+}
+
+static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
+{
+ stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
+ stbtt__buf pdict;
+ stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
+ if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
+ pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
+ stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
+ if (!subrsoff) return stbtt__new_buf(NULL, 0);
+ stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
+ return stbtt__cff_get_index(&cff);
+}
+
+// since most people won't use this, find this table the first time it's needed
+static int stbtt__get_svg(stbtt_fontinfo *info)
+{
+ stbtt_uint32 t;
+ if (info->svg < 0) {
+ t = stbtt__find_table(info->data, info->fontstart, "SVG ");
+ if (t) {
+ stbtt_uint32 offset = ttULONG(info->data + t + 2);
+ info->svg = t + offset;
+ } else {
+ info->svg = 0;
+ }
+ }
+ return info->svg;
+}
+
+static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
{
- stbtt_uint8 *data = (stbtt_uint8 *) data2;
stbtt_uint32 cmap, t;
stbtt_int32 i,numTables;
info->data = data;
info->fontstart = fontstart;
+ info->cff = stbtt__new_buf(NULL, 0);
cmap = stbtt__find_table(data, fontstart, "cmap"); // required
info->loca = stbtt__find_table(data, fontstart, "loca"); // required
@@ -1033,8 +1396,62 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i
info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
- if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
+ info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
+
+ if (!cmap || !info->head || !info->hhea || !info->hmtx)
return 0;
+ if (info->glyf) {
+ // required for truetype
+ if (!info->loca) return 0;
+ } else {
+ // initialization for CFF / Type2 fonts (OTF)
+ stbtt__buf b, topdict, topdictidx;
+ stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
+ stbtt_uint32 cff;
+
+ cff = stbtt__find_table(data, fontstart, "CFF ");
+ if (!cff) return 0;
+
+ info->fontdicts = stbtt__new_buf(NULL, 0);
+ info->fdselect = stbtt__new_buf(NULL, 0);
+
+ // @TODO this should use size from table (not 512MB)
+ info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
+ b = info->cff;
+
+ // read the header
+ stbtt__buf_skip(&b, 2);
+ stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
+
+ // @TODO the name INDEX could list multiple fonts,
+ // but we just use the first one.
+ stbtt__cff_get_index(&b); // name INDEX
+ topdictidx = stbtt__cff_get_index(&b);
+ topdict = stbtt__cff_index_get(topdictidx, 0);
+ stbtt__cff_get_index(&b); // string INDEX
+ info->gsubrs = stbtt__cff_get_index(&b);
+
+ stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
+ stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
+ stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
+ stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
+ info->subrs = stbtt__get_subrs(b, topdict);
+
+ // we only support Type 2 charstrings
+ if (cstype != 2) return 0;
+ if (charstrings == 0) return 0;
+
+ if (fdarrayoff) {
+ // looks like a CID font
+ if (!fdselectoff) return 0;
+ stbtt__buf_seek(&b, fdarrayoff);
+ info->fontdicts = stbtt__cff_get_index(&b);
+ info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
+ }
+
+ stbtt__buf_seek(&b, charstrings);
+ info->charstrings = stbtt__cff_get_index(&b);
+ }
t = stbtt__find_table(data, fontstart, "maxp");
if (t)
@@ -1042,6 +1459,8 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i
else
info->numGlyphs = 0xffff;
+ info->svg = -1;
+
// find a cmap encoding table we understand *now* to avoid searching
// later. (todo: could make this installable)
// the same regardless of glyph.
@@ -1125,12 +1544,12 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
search += 2;
{
- stbtt_uint16 offset, start;
+ stbtt_uint16 offset, start, last;
stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
- if (unicode_codepoint < start)
+ last = ttUSHORT(data + endCount + 2*item);
+ if (unicode_codepoint < start || unicode_codepoint > last)
return 0;
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
@@ -1185,6 +1604,8 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
{
int g1,g2;
+ STBTT_assert(!info->cff.size);
+
if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
@@ -1199,15 +1620,21 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
return g1==g2 ? -1 : g1; // if length is 0, return -1
}
+static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
+
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
{
- int g = stbtt__GetGlyfOffset(info, glyph_index);
- if (g < 0) return 0;
+ if (info->cff.size) {
+ stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
+ } else {
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 0;
- if (x0) *x0 = ttSHORT(info->data + g + 2);
- if (y0) *y0 = ttSHORT(info->data + g + 4);
- if (x1) *x1 = ttSHORT(info->data + g + 6);
- if (y1) *y1 = ttSHORT(info->data + g + 8);
+ if (x0) *x0 = ttSHORT(info->data + g + 2);
+ if (y0) *y0 = ttSHORT(info->data + g + 4);
+ if (x1) *x1 = ttSHORT(info->data + g + 6);
+ if (y1) *y1 = ttSHORT(info->data + g + 8);
+ }
return 1;
}
@@ -1219,7 +1646,10 @@ STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, i
STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
{
stbtt_int16 numberOfContours;
- int g = stbtt__GetGlyfOffset(info, glyph_index);
+ int g;
+ if (info->cff.size)
+ return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
+ g = stbtt__GetGlyfOffset(info, glyph_index);
if (g < 0) return 1;
numberOfContours = ttSHORT(info->data + g);
return numberOfContours == 0;
@@ -1241,7 +1671,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_
return num_vertices;
}
-STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
+static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
{
stbtt_int16 numberOfContours;
stbtt_uint8 *endPtsOfContours;
@@ -1337,7 +1767,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
if (i != 0)
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- // now start the new one
+ // now start the new one
start_off = !(flags & 1);
if (start_off) {
// if we start off with an off-curve point, then when we need to find a point on the curve
@@ -1379,7 +1809,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
}
}
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- } else if (numberOfContours == -1) {
+ } else if (numberOfContours < 0) {
// Compound shapes.
int more = 1;
stbtt_uint8 *comp = data + g + 10;
@@ -1390,7 +1820,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
int comp_num_verts = 0, i;
stbtt_vertex *comp_verts = 0, *tmp = 0;
float mtx[6] = {1,0,0,1,0,0}, m, n;
-
+
flags = ttSHORT(comp); comp+=2;
gidx = ttSHORT(comp); comp+=2;
@@ -1420,7 +1850,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
}
-
+
// Find transformation scales.
m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
@@ -1446,7 +1876,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
if (comp_verts) STBTT_free(comp_verts, info->userdata);
return 0;
}
- if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
+ if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
if (vertices) STBTT_free(vertices, info->userdata);
vertices = tmp;
@@ -1456,9 +1886,6 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
// More components ?
more = flags & (1<<5);
}
- } else if (numberOfContours < 0) {
- // @TODO other compound variations?
- STBTT_assert(0);
} else {
// numberOfCounters == 0, do nothing
}
@@ -1467,6 +1894,414 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
return num_vertices;
}
+typedef struct
+{
+ int bounds;
+ int started;
+ float first_x, first_y;
+ float x, y;
+ stbtt_int32 min_x, max_x, min_y, max_y;
+
+ stbtt_vertex *pvertices;
+ int num_vertices;
+} stbtt__csctx;
+
+#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
+
+static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
+{
+ if (x > c->max_x || !c->started) c->max_x = x;
+ if (y > c->max_y || !c->started) c->max_y = y;
+ if (x < c->min_x || !c->started) c->min_x = x;
+ if (y < c->min_y || !c->started) c->min_y = y;
+ c->started = 1;
+}
+
+static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
+{
+ if (c->bounds) {
+ stbtt__track_vertex(c, x, y);
+ if (type == STBTT_vcubic) {
+ stbtt__track_vertex(c, cx, cy);
+ stbtt__track_vertex(c, cx1, cy1);
+ }
+ } else {
+ stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
+ c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
+ c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
+ }
+ c->num_vertices++;
+}
+
+static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
+{
+ if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
+ stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
+}
+
+static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
+{
+ stbtt__csctx_close_shape(ctx);
+ ctx->first_x = ctx->x = ctx->x + dx;
+ ctx->first_y = ctx->y = ctx->y + dy;
+ stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
+}
+
+static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
+{
+ ctx->x += dx;
+ ctx->y += dy;
+ stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
+}
+
+static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
+{
+ float cx1 = ctx->x + dx1;
+ float cy1 = ctx->y + dy1;
+ float cx2 = cx1 + dx2;
+ float cy2 = cy1 + dy2;
+ ctx->x = cx2 + dx3;
+ ctx->y = cy2 + dy3;
+ stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
+}
+
+static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
+{
+ int count = stbtt__cff_index_count(&idx);
+ int bias = 107;
+ if (count >= 33900)
+ bias = 32768;
+ else if (count >= 1240)
+ bias = 1131;
+ n += bias;
+ if (n < 0 || n >= count)
+ return stbtt__new_buf(NULL, 0);
+ return stbtt__cff_index_get(idx, n);
+}
+
+static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
+{
+ stbtt__buf fdselect = info->fdselect;
+ int nranges, start, end, v, fmt, fdselector = -1, i;
+
+ stbtt__buf_seek(&fdselect, 0);
+ fmt = stbtt__buf_get8(&fdselect);
+ if (fmt == 0) {
+ // untested
+ stbtt__buf_skip(&fdselect, glyph_index);
+ fdselector = stbtt__buf_get8(&fdselect);
+ } else if (fmt == 3) {
+ nranges = stbtt__buf_get16(&fdselect);
+ start = stbtt__buf_get16(&fdselect);
+ for (i = 0; i < nranges; i++) {
+ v = stbtt__buf_get8(&fdselect);
+ end = stbtt__buf_get16(&fdselect);
+ if (glyph_index >= start && glyph_index < end) {
+ fdselector = v;
+ break;
+ }
+ start = end;
+ }
+ }
+ if (fdselector == -1) stbtt__new_buf(NULL, 0);
+ return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
+}
+
+static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
+{
+ int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
+ int has_subrs = 0, clear_stack;
+ float s[48];
+ stbtt__buf subr_stack[10], subrs = info->subrs, b;
+ float f;
+
+#define STBTT__CSERR(s) (0)
+
+ // this currently ignores the initial width value, which isn't needed if we have hmtx
+ b = stbtt__cff_index_get(info->charstrings, glyph_index);
+ while (b.cursor < b.size) {
+ i = 0;
+ clear_stack = 1;
+ b0 = stbtt__buf_get8(&b);
+ switch (b0) {
+ // @TODO implement hinting
+ case 0x13: // hintmask
+ case 0x14: // cntrmask
+ if (in_header)
+ maskbits += (sp / 2); // implicit "vstem"
+ in_header = 0;
+ stbtt__buf_skip(&b, (maskbits + 7) / 8);
+ break;
+
+ case 0x01: // hstem
+ case 0x03: // vstem
+ case 0x12: // hstemhm
+ case 0x17: // vstemhm
+ maskbits += (sp / 2);
+ break;
+
+ case 0x15: // rmoveto
+ in_header = 0;
+ if (sp < 2) return STBTT__CSERR("rmoveto stack");
+ stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
+ break;
+ case 0x04: // vmoveto
+ in_header = 0;
+ if (sp < 1) return STBTT__CSERR("vmoveto stack");
+ stbtt__csctx_rmove_to(c, 0, s[sp-1]);
+ break;
+ case 0x16: // hmoveto
+ in_header = 0;
+ if (sp < 1) return STBTT__CSERR("hmoveto stack");
+ stbtt__csctx_rmove_to(c, s[sp-1], 0);
+ break;
+
+ case 0x05: // rlineto
+ if (sp < 2) return STBTT__CSERR("rlineto stack");
+ for (; i + 1 < sp; i += 2)
+ stbtt__csctx_rline_to(c, s[i], s[i+1]);
+ break;
+
+ // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
+ // starting from a different place.
+
+ case 0x07: // vlineto
+ if (sp < 1) return STBTT__CSERR("vlineto stack");
+ goto vlineto;
+ case 0x06: // hlineto
+ if (sp < 1) return STBTT__CSERR("hlineto stack");
+ for (;;) {
+ if (i >= sp) break;
+ stbtt__csctx_rline_to(c, s[i], 0);
+ i++;
+ vlineto:
+ if (i >= sp) break;
+ stbtt__csctx_rline_to(c, 0, s[i]);
+ i++;
+ }
+ break;
+
+ case 0x1F: // hvcurveto
+ if (sp < 4) return STBTT__CSERR("hvcurveto stack");
+ goto hvcurveto;
+ case 0x1E: // vhcurveto
+ if (sp < 4) return STBTT__CSERR("vhcurveto stack");
+ for (;;) {
+ if (i + 3 >= sp) break;
+ stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
+ i += 4;
+ hvcurveto:
+ if (i + 3 >= sp) break;
+ stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
+ i += 4;
+ }
+ break;
+
+ case 0x08: // rrcurveto
+ if (sp < 6) return STBTT__CSERR("rcurveline stack");
+ for (; i + 5 < sp; i += 6)
+ stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ break;
+
+ case 0x18: // rcurveline
+ if (sp < 8) return STBTT__CSERR("rcurveline stack");
+ for (; i + 5 < sp - 2; i += 6)
+ stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
+ stbtt__csctx_rline_to(c, s[i], s[i+1]);
+ break;
+
+ case 0x19: // rlinecurve
+ if (sp < 8) return STBTT__CSERR("rlinecurve stack");
+ for (; i + 1 < sp - 6; i += 2)
+ stbtt__csctx_rline_to(c, s[i], s[i+1]);
+ if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
+ stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ break;
+
+ case 0x1A: // vvcurveto
+ case 0x1B: // hhcurveto
+ if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
+ f = 0.0;
+ if (sp & 1) { f = s[i]; i++; }
+ for (; i + 3 < sp; i += 4) {
+ if (b0 == 0x1B)
+ stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
+ else
+ stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
+ f = 0.0;
+ }
+ break;
+
+ case 0x0A: // callsubr
+ if (!has_subrs) {
+ if (info->fdselect.size)
+ subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
+ has_subrs = 1;
+ }
+ // FALLTHROUGH
+ case 0x1D: // callgsubr
+ if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
+ v = (int) s[--sp];
+ if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
+ subr_stack[subr_stack_height++] = b;
+ b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
+ if (b.size == 0) return STBTT__CSERR("subr not found");
+ b.cursor = 0;
+ clear_stack = 0;
+ break;
+
+ case 0x0B: // return
+ if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
+ b = subr_stack[--subr_stack_height];
+ clear_stack = 0;
+ break;
+
+ case 0x0E: // endchar
+ stbtt__csctx_close_shape(c);
+ return 1;
+
+ case 0x0C: { // two-byte escape
+ float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
+ float dx, dy;
+ int b1 = stbtt__buf_get8(&b);
+ switch (b1) {
+ // @TODO These "flex" implementations ignore the flex-depth and resolution,
+ // and always draw beziers.
+ case 0x22: // hflex
+ if (sp < 7) return STBTT__CSERR("hflex stack");
+ dx1 = s[0];
+ dx2 = s[1];
+ dy2 = s[2];
+ dx3 = s[3];
+ dx4 = s[4];
+ dx5 = s[5];
+ dx6 = s[6];
+ stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
+ stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
+ break;
+
+ case 0x23: // flex
+ if (sp < 13) return STBTT__CSERR("flex stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dy3 = s[5];
+ dx4 = s[6];
+ dy4 = s[7];
+ dx5 = s[8];
+ dy5 = s[9];
+ dx6 = s[10];
+ dy6 = s[11];
+ //fd is s[12]
+ stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
+ stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
+ break;
+
+ case 0x24: // hflex1
+ if (sp < 9) return STBTT__CSERR("hflex1 stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dx4 = s[5];
+ dx5 = s[6];
+ dy5 = s[7];
+ dx6 = s[8];
+ stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
+ stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
+ break;
+
+ case 0x25: // flex1
+ if (sp < 11) return STBTT__CSERR("flex1 stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dy3 = s[5];
+ dx4 = s[6];
+ dy4 = s[7];
+ dx5 = s[8];
+ dy5 = s[9];
+ dx6 = dy6 = s[10];
+ dx = dx1+dx2+dx3+dx4+dx5;
+ dy = dy1+dy2+dy3+dy4+dy5;
+ if (STBTT_fabs(dx) > STBTT_fabs(dy))
+ dy6 = -dy;
+ else
+ dx6 = -dx;
+ stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
+ stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
+ break;
+
+ default:
+ return STBTT__CSERR("unimplemented");
+ }
+ } break;
+
+ default:
+ if (b0 != 255 && b0 != 28 && b0 < 32)
+ return STBTT__CSERR("reserved operator");
+
+ // push immediate
+ if (b0 == 255) {
+ f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
+ } else {
+ stbtt__buf_skip(&b, -1);
+ f = (float)(stbtt_int16)stbtt__cff_int(&b);
+ }
+ if (sp >= 48) return STBTT__CSERR("push stack overflow");
+ s[sp++] = f;
+ clear_stack = 0;
+ break;
+ }
+ if (clear_stack) sp = 0;
+ }
+ return STBTT__CSERR("no endchar");
+
+#undef STBTT__CSERR
+}
+
+static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
+{
+ // runs the charstring twice, once to count and once to output (to avoid realloc)
+ stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
+ stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
+ if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
+ *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
+ output_ctx.pvertices = *pvertices;
+ if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
+ STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
+ return output_ctx.num_vertices;
+ }
+ }
+ *pvertices = NULL;
+ return 0;
+}
+
+static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
+{
+ stbtt__csctx c = STBTT__CSCTX_INIT(1);
+ int r = stbtt__run_charstring(info, glyph_index, &c);
+ if (x0) *x0 = r ? c.min_x : 0;
+ if (y0) *y0 = r ? c.min_y : 0;
+ if (x1) *x1 = r ? c.max_x : 0;
+ if (y1) *y1 = r ? c.max_y : 0;
+ return r ? c.num_vertices : 0;
+}
+
+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
+{
+ if (!info->cff.size)
+ return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
+ else
+ return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
+}
+
STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
{
stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
@@ -1479,7 +2314,49 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde
}
}
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ return ttUSHORT(data+10);
+}
+
+STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+ int k, length;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ length = ttUSHORT(data+10);
+ if (table_length < length)
+ length = table_length;
+
+ for (k = 0; k < length; k++)
+ {
+ table[k].glyph1 = ttUSHORT(data+18+(k*6));
+ table[k].glyph2 = ttUSHORT(data+20+(k*6));
+ table[k].advance = ttSHORT(data+22+(k*6));
+ }
+
+ return length;
+}
+
+static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@@ -1509,9 +2386,242 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
return 0;
}
+static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
+{
+ stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
+ switch (coverageFormat) {
+ case 1: {
+ stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
+
+ // Binary search.
+ stbtt_int32 l=0, r=glyphCount-1, m;
+ int straw, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *glyphArray = coverageTable + 4;
+ stbtt_uint16 glyphID;
+ m = (l + r) >> 1;
+ glyphID = ttUSHORT(glyphArray + 2 * m);
+ straw = glyphID;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ return m;
+ }
+ }
+ break;
+ }
+
+ case 2: {
+ stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
+ stbtt_uint8 *rangeArray = coverageTable + 4;
+
+ // Binary search.
+ stbtt_int32 l=0, r=rangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *rangeRecord;
+ m = (l + r) >> 1;
+ rangeRecord = rangeArray + 6 * m;
+ strawStart = ttUSHORT(rangeRecord);
+ strawEnd = ttUSHORT(rangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else {
+ stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
+ return startCoverageIndex + glyph - strawStart;
+ }
+ }
+ break;
+ }
+
+ default: return -1; // unsupported
+ }
+
+ return -1;
+}
+
+static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
+{
+ stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
+ switch (classDefFormat)
+ {
+ case 1: {
+ stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
+ stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
+ stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
+
+ if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
+ return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
+ break;
+ }
+
+ case 2: {
+ stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
+ stbtt_uint8 *classRangeRecords = classDefTable + 4;
+
+ // Binary search.
+ stbtt_int32 l=0, r=classRangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ stbtt_uint8 *classRangeRecord;
+ m = (l + r) >> 1;
+ classRangeRecord = classRangeRecords + 6 * m;
+ strawStart = ttUSHORT(classRangeRecord);
+ strawEnd = ttUSHORT(classRangeRecord + 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else
+ return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
+ }
+ break;
+ }
+
+ default:
+ return -1; // Unsupported definition type, return an error.
+ }
+
+ // "All glyphs not assigned to a class fall into class 0". (OpenType spec)
+ return 0;
+}
+
+// Define to STBTT_assert(x) if you want to break on unimplemented formats.
+#define STBTT_GPOS_TODO_assert(x)
+
+static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ stbtt_uint16 lookupListOffset;
+ stbtt_uint8 *lookupList;
+ stbtt_uint16 lookupCount;
+ stbtt_uint8 *data;
+ stbtt_int32 i, sti;
+
+ if (!info->gpos) return 0;
+
+ data = info->data + info->gpos;
+
+ if (ttUSHORT(data+0) != 1) return 0; // Major version 1
+ if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
+
+ lookupListOffset = ttUSHORT(data+8);
+ lookupList = data + lookupListOffset;
+ lookupCount = ttUSHORT(lookupList);
+
+ for (i=0; i<lookupCount; ++i) {
+ stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
+ stbtt_uint8 *lookupTable = lookupList + lookupOffset;
+
+ stbtt_uint16 lookupType = ttUSHORT(lookupTable);
+ stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
+ stbtt_uint8 *subTableOffsets = lookupTable + 6;
+ if (lookupType != 2) // Pair Adjustment Positioning Subtable
+ continue;
+
+ for (sti=0; sti<subTableCount; sti++) {
+ stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
+ stbtt_uint8 *table = lookupTable + subtableOffset;
+ stbtt_uint16 posFormat = ttUSHORT(table);
+ stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
+ stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
+ if (coverageIndex == -1) continue;
+
+ switch (posFormat) {
+ case 1: {
+ stbtt_int32 l, r, m;
+ int straw, needle;
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
+ stbtt_int32 valueRecordPairSizeInBytes = 2;
+ stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
+ stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
+ stbtt_uint8 *pairValueTable = table + pairPosOffset;
+ stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
+ stbtt_uint8 *pairValueArray = pairValueTable + 2;
+
+ if (coverageIndex >= pairSetCount) return 0;
+
+ needle=glyph2;
+ r=pairValueCount-1;
+ l=0;
+
+ // Binary search.
+ while (l <= r) {
+ stbtt_uint16 secondGlyph;
+ stbtt_uint8 *pairValue;
+ m = (l + r) >> 1;
+ pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
+ secondGlyph = ttUSHORT(pairValue);
+ straw = secondGlyph;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
+ return xAdvance;
+ }
+ }
+ } else
+ return 0;
+ break;
+ }
+
+ case 2: {
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
+ stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
+ stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
+ int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
+ int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
+
+ stbtt_uint16 class1Count = ttUSHORT(table + 12);
+ stbtt_uint16 class2Count = ttUSHORT(table + 14);
+ stbtt_uint8 *class1Records, *class2Records;
+ stbtt_int16 xAdvance;
+
+ if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
+ if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
+
+ class1Records = table + 16;
+ class2Records = class1Records + 2 * (glyph1class * class2Count);
+ xAdvance = ttSHORT(class2Records + 2 * glyph2class);
+ return xAdvance;
+ } else
+ return 0;
+ break;
+ }
+
+ default:
+ return 0; // Unsupported position format
+ }
+ }
+ }
+
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
+{
+ int xAdvance = 0;
+
+ if (info->gpos)
+ xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
+ else if (info->kern)
+ xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
+
+ return xAdvance;
+}
+
STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
{
- if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
return 0;
return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
}
@@ -1528,6 +2638,17 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in
if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
}
+STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)
+{
+ int tab = stbtt__find_table(info->data, info->fontstart, "OS/2");
+ if (!tab)
+ return 0;
+ if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68);
+ if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);
+ if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);
+ return 1;
+}
+
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
{
*x0 = ttSHORT(info->data + info->head + 36);
@@ -1553,6 +2674,45 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
STBTT_free(v, info->userdata);
}
+STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)
+{
+ int i;
+ stbtt_uint8 *data = info->data;
+ stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);
+
+ int numEntries = ttUSHORT(svg_doc_list);
+ stbtt_uint8 *svg_docs = svg_doc_list + 2;
+
+ for(i=0; i<numEntries; i++) {
+ stbtt_uint8 *svg_doc = svg_docs + (12 * i);
+ if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))
+ return svg_doc;
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)
+{
+ stbtt_uint8 *data = info->data;
+ stbtt_uint8 *svg_doc;
+
+ if (info->svg == 0)
+ return 0;
+
+ svg_doc = stbtt_FindSVGDoc(info, gl);
+ if (svg_doc != NULL) {
+ *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);
+ return ttULONG(svg_doc + 8);
+ } else {
+ return 0;
+ }
+}
+
+STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)
+{
+ return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// antialiasing software rasterizer
@@ -1560,7 +2720,7 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
{
- int x0,y0,x1,y1;
+ int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
// e.g. space character
if (ix0) *ix0 = 0;
@@ -1624,7 +2784,7 @@ static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
hh->num_remaining_in_head_chunk = count;
}
--hh->num_remaining_in_head_chunk;
- return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
}
}
@@ -1676,8 +2836,9 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i
{
stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
if (!z) return z;
-
+
// round dx down to avoid overshooting
if (dxdy < 0)
z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
@@ -1697,6 +2858,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i
{
stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
//STBTT_assert(e->y0 <= start_point);
if (!z) return z;
z->fdx = dxdy;
@@ -1754,7 +2916,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac
}
}
}
-
+
e = e->next;
}
}
@@ -1768,13 +2930,10 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
int s; // vertical subsample index
unsigned char scanline_data[512], *scanline;
- if (result->w > 512) {
+ if (result->w > 512)
scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
- if (!scanline)
- return;
- } else {
+ else
scanline = scanline_data;
- }
y = off_y * vsubsample;
e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
@@ -1824,23 +2983,23 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
while (e->y0 <= scan_y) {
if (e->y1 > scan_y) {
stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
- if (!z)
- return;
- // find insertion point
- if (active == NULL)
- active = z;
- else if (z->x < active->x) {
- // insert at front
- z->next = active;
- active = z;
- } else {
- // find thing to insert AFTER
- stbtt__active_edge *p = active;
- while (p->next && p->next->x < z->x)
- p = p->next;
- // at this point, p->next->x is NOT < z->x
- z->next = p->next;
- p->next = z;
+ if (z != NULL) {
+ // find insertion point
+ if (active == NULL)
+ active = z;
+ else if (z->x < active->x) {
+ // insert at front
+ z->next = active;
+ active = z;
+ } else {
+ // find thing to insert AFTER
+ stbtt__active_edge *p = active;
+ while (p->next && p->next->x < z->x)
+ p = p->next;
+ // at this point, p->next->x is NOT < z->x
+ z->next = p->next;
+ p->next = z;
+ }
}
}
++e;
@@ -1903,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
}
}
+static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
+{
+ STBTT_assert(top_width >= 0);
+ STBTT_assert(bottom_width >= 0);
+ return (top_width + bottom_width) / 2.0f * height;
+}
+
+static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
+{
+ return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
+}
+
+static float stbtt__sized_triangle_area(float height, float width)
+{
+ return height * width / 2;
+}
+
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
{
float y_bottom = y_top+1;
@@ -1957,13 +3133,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height;
// simple case, only spans one pixel
int x = (int) x_top;
- height = sy1 - sy0;
+ height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len);
- scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
- scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
+ scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
+ scanline_fill[x] += height; // everything right of this pixel is filled
} else {
int x,x1,x2;
- float y_crossing, step, sign, area;
+ float y_crossing, y_final, step, sign, area;
// covers 2+ pixels
if (x_top > x_bottom) {
// flip scanline vertically; signed area is the same
@@ -1976,29 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
dy = -dy;
t = x0, x0 = xb, xb = t;
}
+ STBTT_assert(dy >= 0);
+ STBTT_assert(dx >= 0);
x1 = (int) x_top;
x2 = (int) x_bottom;
// compute intersection with y axis at x1+1
- y_crossing = (x1+1 - x0) * dy + y_top;
+ y_crossing = y_top + dy * (x1+1 - x0);
+
+ // compute intersection with y axis at x2
+ y_final = y_top + dy * (x2 - x0);
+
+ // x1 x_top x2 x_bottom
+ // y_top +------|-----+------------+------------+--------|---+------------+
+ // | | | | | |
+ // | | | | | |
+ // sy0 | Txxxxx|............|............|............|............|
+ // y_crossing | *xxxxx.......|............|............|............|
+ // | | xxxxx..|............|............|............|
+ // | | /- xx*xxxx........|............|............|
+ // | | dy < | xxxxxx..|............|............|
+ // y_final | | \- | xx*xxx.........|............|
+ // sy1 | | | | xxxxxB...|............|
+ // | | | | | |
+ // | | | | | |
+ // y_bottom +------------+------------+------------+------------+------------+
+ //
+ // goal is to measure the area covered by '.' in each pixel
+
+ // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
+ // @TODO: maybe test against sy1 rather than y_bottom?
+ if (y_crossing > y_bottom)
+ y_crossing = y_bottom;
sign = e->direction;
- // area of the rectangle covered from y0..y_crossing
+
+ // area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing-sy0);
- // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
- scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
- step = sign * dy;
+ // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
+ scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
+
+ // check if final y_crossing is blown up; no test case for this
+ if (y_final > y_bottom) {
+ y_final = y_bottom;
+ dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
+ }
+
+ // in second pixel, area covered by line segment found in first pixel
+ // is always a rectangle 1 wide * the height of that line segment; this
+ // is exactly what the variable 'area' stores. it also gets a contribution
+ // from the line segment within it. the THIRD pixel will get the first
+ // pixel's rectangle contribution, the second pixel's rectangle contribution,
+ // and its own contribution. the 'own contribution' is the same in every pixel except
+ // the leftmost and rightmost, a trapezoid that slides down in each pixel.
+ // the second pixel's contribution to the third pixel will be the
+ // rectangle 1 wide times the height change in the second pixel, which is dy.
+
+ step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
+ // which multiplied by 1-pixel-width is how much pixel area changes for each step in x
+ // so the area advances by 'step' every time
+
for (x = x1+1; x < x2; ++x) {
- scanline[x] += area + step/2;
+ scanline[x] += area + step/2; // area of trapezoid is 1*step/2
area += step;
}
- y_crossing += dy * (x2 - (x1+1));
+ STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
+ STBTT_assert(sy1 > y_final-0.01f);
- STBTT_assert(fabs(area) <= 1.01f);
-
- scanline[x2] += area + sign * (1-(x_bottom-x2)/2) * (sy1-y_crossing);
+ // area covered in the last pixel is the rectangle from all the pixels to the left,
+ // plus the trapezoid filled by the line segment in this pixel all the way to the right edge
+ scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
+ // the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2] += sign * (sy1-sy0);
}
} else {
@@ -2006,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute
// force implementation
+ // note though that this does happen some of the time because
+ // x_top and x_bottom can be extrapolated at the top & bottom of
+ // the shape and actually lie outside the bounding box
int x;
for (x=0; x < len; ++x) {
// cases:
@@ -2021,19 +3250,18 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// from the other y segment, and it might ignored as an empty segment. to avoid
// that, we need to explicitly produce segments based on x positions.
- // rename variables to clear pairs
+ // rename variables to clearly-defined pairs
float y0 = y_top;
float x1 = (float) (x);
float x2 = (float) (x+1);
float x3 = xb;
float y3 = y_bottom;
- float y1,y2;
// x = e->x + e->dx * (y-y_top)
// (y-y_top) = (x - e->x) / e->dx
// y = (x - e->x) / e->dx + y_top
- y1 = (x - x0) / dx + y_top;
- y2 = (x+1 - x0) / dx + y_top;
+ float y1 = (x - x0) / dx + y_top;
+ float y2 = (x+1 - x0) / dx + y_top;
if (x0 < x1 && x3 > x2) { // three segments descending down-right
stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
@@ -2073,13 +3301,12 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
int y,j=0, i;
float scanline_data[129], *scanline, *scanline2;
- if (result->w > 64) {
+ STBTT__NOTUSED(vsubsample);
+
+ if (result->w > 64)
scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
- if (!scanline)
- return;
- } else {
+ else
scanline = scanline_data;
- }
scanline2 = scanline + result->w;
@@ -2113,12 +3340,18 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
while (e->y0 <= scan_y_bottom) {
if (e->y0 != e->y1) {
stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
- if (!z)
- return;
- STBTT_assert(z->ey >= scan_y_top);
- // insert at front
- z->next = active;
- active = z;
+ if (z != NULL) {
+ if (j == 0 && off_y != 0) {
+ if (z->ey < scan_y_top) {
+ // this can happen due to subpixel positioning and some kind of fp rounding error i think
+ z->ey = scan_y_top;
+ }
+ }
+ STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
+ // insert at front
+ z->next = active;
+ active = z;
+ }
}
++e;
}
@@ -2183,7 +3416,7 @@ static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
{
- /* threshhold for transitioning to insertion sort */
+ /* threshold for transitioning to insertion sort */
while (n > 12) {
stbtt__edge t;
int c01,c12,c,m,i,j;
@@ -2318,7 +3551,7 @@ static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
points[n].y = y;
}
-// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
+// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
{
// midpoint
@@ -2339,6 +3572,48 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x
return 1;
}
+static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
+{
+ // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
+ float dx0 = x1-x0;
+ float dy0 = y1-y0;
+ float dx1 = x2-x1;
+ float dy1 = y2-y1;
+ float dx2 = x3-x2;
+ float dy2 = y3-y2;
+ float dx = x3-x0;
+ float dy = y3-y0;
+ float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
+ float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
+ float flatness_squared = longlen*longlen-shortlen*shortlen;
+
+ if (n > 16) // 65536 segments on one curve better be enough!
+ return;
+
+ if (flatness_squared > objspace_flatness_squared) {
+ float x01 = (x0+x1)/2;
+ float y01 = (y0+y1)/2;
+ float x12 = (x1+x2)/2;
+ float y12 = (y1+y2)/2;
+ float x23 = (x2+x3)/2;
+ float y23 = (y2+y3)/2;
+
+ float xa = (x01+x12)/2;
+ float ya = (y01+y12)/2;
+ float xb = (x12+x23)/2;
+ float yb = (y12+y23)/2;
+
+ float mx = (xa+xb)/2;
+ float my = (ya+yb)/2;
+
+ stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
+ stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
+ } else {
+ stbtt__add_point(points, *num_points,x3,y3);
+ *num_points = *num_points+1;
+ }
+}
+
// returns number of contours
static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
{
@@ -2395,6 +3670,14 @@ static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts,
objspace_flatness_squared, 0);
x = vertices[i].x, y = vertices[i].y;
break;
+ case STBTT_vcubic:
+ stbtt__tesselate_cubic(points, &num_points, x,y,
+ vertices[i].cx, vertices[i].cy,
+ vertices[i].cx1, vertices[i].cy1,
+ vertices[i].x, vertices[i].y,
+ objspace_flatness_squared, 0);
+ x = vertices[i].x, y = vertices[i].y;
+ break;
}
}
(*contour_lengths)[n] = num_points - start;
@@ -2411,8 +3694,9 @@ error:
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
{
- float scale = scale_x > scale_y ? scale_y : scale_x;
- int winding_count, *winding_lengths;
+ float scale = scale_x > scale_y ? scale_y : scale_x;
+ int winding_count = 0;
+ int *winding_lengths = NULL;
stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
if (windings) {
stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
@@ -2430,7 +3714,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
{
int ix0,iy0,ix1,iy1;
stbtt__bitmap gbm;
- stbtt_vertex *vertices;
+ stbtt_vertex *vertices;
int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
if (scale_x == 0) scale_x = scale_y;
@@ -2453,7 +3737,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
if (height) *height = gbm.h;
if (xoff ) *xoff = ix0;
if (yoff ) *yoff = iy0;
-
+
if (gbm.w && gbm.h) {
gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
if (gbm.pixels) {
@@ -2464,7 +3748,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
}
STBTT_free(vertices, info->userdata);
return gbm.pixels;
-}
+}
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
{
@@ -2476,7 +3760,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne
int ix0,iy0;
stbtt_vertex *vertices;
int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
- stbtt__bitmap gbm;
+ stbtt__bitmap gbm;
stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
gbm.pixels = output;
@@ -2498,7 +3782,12 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-}
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
+{
+ stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
+}
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
{
@@ -2508,7 +3797,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
-}
+}
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
{
@@ -2521,7 +3810,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned ch
//
// This is SUPER-CRAPPY packing to keep source code small
-STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
+static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
float pixel_height, // height of font in pixels
unsigned char *pixels, int pw, int ph, // bitmap to be filled in
int first_char, int num_chars, // characters to bake
@@ -2530,6 +3819,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo
float scale;
int x,y,bottom_y, i;
stbtt_fontinfo f;
+ f.userdata = NULL;
if (!stbtt_InitFont(&f, data, offset))
return -1;
STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
@@ -2566,11 +3856,11 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo
return bottom_y;
}
-STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
+STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
{
float d3d_bias = opengl_fillrule ? 0 : -0.5f;
float ipw = 1.0f / pw, iph = 1.0f / ph;
- stbtt_bakedchar *b = chardata + char_index;
+ const stbtt_bakedchar *b = chardata + char_index;
int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
@@ -2593,11 +3883,6 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int
//
#ifndef STB_RECT_PACK_VERSION
-#ifdef _MSC_VER
-#define STBTT__NOTUSED(v) (void)(v)
-#else
-#define STBTT__NOTUSED(v) (void)sizeof(v)
-#endif
typedef int stbrp_coord;
@@ -2637,7 +3922,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no
con->y = 0;
con->bottom_y = 0;
STBTT__NOTUSED(nodes);
- STBTT__NOTUSED(num_nodes);
+ STBTT__NOTUSED(num_nodes);
}
static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
@@ -2691,6 +3976,7 @@ STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, in
spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
spc->h_oversample = 1;
spc->v_oversample = 1;
+ spc->skip_missing = 0;
stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
@@ -2716,6 +4002,11 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
spc->v_oversample = v_oversample;
}
+STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
+{
+ spc->skip_missing = skip;
+}
+
#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
@@ -2723,6 +4014,7 @@ static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_i
unsigned char buffer[STBTT_MAX_OVERSAMPLE];
int safe_w = w - kernel_width;
int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
for (j=0; j < h; ++j) {
int i;
unsigned int total;
@@ -2784,6 +4076,7 @@ static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_i
unsigned char buffer[STBTT_MAX_OVERSAMPLE];
int safe_h = h - kernel_width;
int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
for (j=0; j < w; ++j) {
int i;
unsigned int total;
@@ -2853,9 +4146,10 @@ static float stbtt__oversample_shift(int oversample)
}
// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
int i,j,k;
+ int missing_glyph_added = 0;
k=0;
for (i=0; i < num_ranges; ++i) {
@@ -2867,13 +4161,19 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon
int x0,y0,x1,y1;
int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
int glyph = stbtt_FindGlyphIndex(info, codepoint);
- stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- 0,0,
- &x0,&y0,&x1,&y1);
- rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
- rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
+ rects[k].w = rects[k].h = 0;
+ } else {
+ stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ &x0,&y0,&x1,&y1);
+ rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
+ rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ if (glyph == 0)
+ missing_glyph_added = 1;
+ }
++k;
}
}
@@ -2881,10 +4181,33 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon
return k;
}
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info,
+ output,
+ out_w - (prefilter_x - 1),
+ out_h - (prefilter_y - 1),
+ out_stride,
+ scale_x,
+ scale_y,
+ shift_x,
+ shift_y,
+ glyph);
+
+ if (prefilter_x > 1)
+ stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
+
+ if (prefilter_y > 1)
+ stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
+
+ *sub_x = stbtt__oversample_shift(prefilter_x);
+ *sub_y = stbtt__oversample_shift(prefilter_y);
+}
+
// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
- int i,j,k, return_value = 1;
+ int i,j,k, missing_glyph = -1, return_value = 1;
// save current values
int old_h_over = spc->h_oversample;
@@ -2903,7 +4226,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt
sub_y = stbtt__oversample_shift(spc->v_oversample);
for (j=0; j < ranges[i].num_chars; ++j) {
stbrp_rect *r = &rects[k];
- if (r->was_packed) {
+ if (r->was_packed && r->w != 0 && r->h != 0) {
stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
int advance, lsb, x0,y0,x1,y1;
int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
@@ -2949,6 +4272,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt
bc->yoff = (float) y0 * recip_v + sub_y;
bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
+
+ if (glyph == 0)
+ missing_glyph = j;
+ } else if (spc->skip_missing) {
+ return_value = 0;
+ } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
+ ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
} else {
return_value = 0; // if any fail, report failure
}
@@ -2969,7 +4299,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect
stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
}
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
{
stbtt_fontinfo info;
int i,j,n, return_value = 1;
@@ -2987,24 +4317,25 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd
n = 0;
for (i=0; i < num_ranges; ++i)
n += ranges[i].num_chars;
-
+
rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
if (rects == NULL)
return 0;
+ info.userdata = spc->user_allocator_context;
stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
stbtt_PackFontRangesPackRects(spc, rects, n);
-
+
return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
STBTT_free(rects, spc->user_allocator_context);
return return_value;
}
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
{
stbtt_pack_range range;
@@ -3016,10 +4347,23 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontda
return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
}
-STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
+STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
+{
+ int i_ascent, i_descent, i_lineGap;
+ float scale;
+ stbtt_fontinfo info;
+ stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
+ scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
+ stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
+ *ascent = (float) i_ascent * scale;
+ *descent = (float) i_descent * scale;
+ *lineGap = (float) i_lineGap * scale;
+}
+
+STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
{
float ipw = 1.0f / pw, iph = 1.0f / ph;
- stbtt_packedchar *b = chardata + char_index;
+ const stbtt_packedchar *b = chardata + char_index;
if (align_to_integer) {
float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
@@ -3043,6 +4387,385 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i
*xpos += b->xadvance;
}
+//////////////////////////////////////////////////////////////////////////////
+//
+// sdf computation
+//
+
+#define STBTT_min(a,b) ((a) < (b) ? (a) : (b))
+#define STBTT_max(a,b) ((a) < (b) ? (b) : (a))
+
+static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
+{
+ float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
+ float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
+ float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
+ float roperp = orig[1]*ray[0] - orig[0]*ray[1];
+
+ float a = q0perp - 2*q1perp + q2perp;
+ float b = q1perp - q0perp;
+ float c = q0perp - roperp;
+
+ float s0 = 0., s1 = 0.;
+ int num_s = 0;
+
+ if (a != 0.0) {
+ float discr = b*b - a*c;
+ if (discr > 0.0) {
+ float rcpna = -1 / a;
+ float d = (float) STBTT_sqrt(discr);
+ s0 = (b+d) * rcpna;
+ s1 = (b-d) * rcpna;
+ if (s0 >= 0.0 && s0 <= 1.0)
+ num_s = 1;
+ if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
+ if (num_s == 0) s0 = s1;
+ ++num_s;
+ }
+ }
+ } else {
+ // 2*b*s + c = 0
+ // s = -c / (2*b)
+ s0 = c / (-2 * b);
+ if (s0 >= 0.0 && s0 <= 1.0)
+ num_s = 1;
+ }
+
+ if (num_s == 0)
+ return 0;
+ else {
+ float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
+ float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
+
+ float q0d = q0[0]*rayn_x + q0[1]*rayn_y;
+ float q1d = q1[0]*rayn_x + q1[1]*rayn_y;
+ float q2d = q2[0]*rayn_x + q2[1]*rayn_y;
+ float rod = orig[0]*rayn_x + orig[1]*rayn_y;
+
+ float q10d = q1d - q0d;
+ float q20d = q2d - q0d;
+ float q0rd = q0d - rod;
+
+ hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
+ hits[0][1] = a*s0+b;
+
+ if (num_s > 1) {
+ hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
+ hits[1][1] = a*s1+b;
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static int equal(float *a, float *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
+{
+ int i;
+ float orig[2], ray[2] = { 1, 0 };
+ float y_frac;
+ int winding = 0;
+
+ // make sure y never passes through a vertex of the shape
+ y_frac = (float) STBTT_fmod(y, 1.0f);
+ if (y_frac < 0.01f)
+ y += 0.01f;
+ else if (y_frac > 0.99f)
+ y -= 0.01f;
+
+ orig[0] = x;
+ orig[1] = y;
+
+ // test a ray from (-infinity,y) to (x,y)
+ for (i=0; i < nverts; ++i) {
+ if (verts[i].type == STBTT_vline) {
+ int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
+ int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
+ if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
+ float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
+ if (x_inter < x)
+ winding += (y0 < y1) ? 1 : -1;
+ }
+ }
+ if (verts[i].type == STBTT_vcurve) {
+ int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
+ int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy;
+ int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ;
+ int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
+ int by = STBTT_max(y0,STBTT_max(y1,y2));
+ if (y > ay && y < by && x > ax) {
+ float q0[2],q1[2],q2[2];
+ float hits[2][2];
+ q0[0] = (float)x0;
+ q0[1] = (float)y0;
+ q1[0] = (float)x1;
+ q1[1] = (float)y1;
+ q2[0] = (float)x2;
+ q2[1] = (float)y2;
+ if (equal(q0,q1) || equal(q1,q2)) {
+ x0 = (int)verts[i-1].x;
+ y0 = (int)verts[i-1].y;
+ x1 = (int)verts[i ].x;
+ y1 = (int)verts[i ].y;
+ if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
+ float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
+ if (x_inter < x)
+ winding += (y0 < y1) ? 1 : -1;
+ }
+ } else {
+ int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
+ if (num_hits >= 1)
+ if (hits[0][0] < 0)
+ winding += (hits[0][1] < 0 ? -1 : 1);
+ if (num_hits >= 2)
+ if (hits[1][0] < 0)
+ winding += (hits[1][1] < 0 ? -1 : 1);
+ }
+ }
+ }
+ }
+ return winding;
+}
+
+static float stbtt__cuberoot( float x )
+{
+ if (x<0)
+ return -(float) STBTT_pow(-x,1.0f/3.0f);
+ else
+ return (float) STBTT_pow( x,1.0f/3.0f);
+}
+
+// x^3 + a*x^2 + b*x + c = 0
+static int stbtt__solve_cubic(float a, float b, float c, float* r)
+{
+ float s = -a / 3;
+ float p = b - a*a / 3;
+ float q = a * (2*a*a - 9*b) / 27 + c;
+ float p3 = p*p*p;
+ float d = q*q + 4*p3 / 27;
+ if (d >= 0) {
+ float z = (float) STBTT_sqrt(d);
+ float u = (-q + z) / 2;
+ float v = (-q - z) / 2;
+ u = stbtt__cuberoot(u);
+ v = stbtt__cuberoot(v);
+ r[0] = s + u + v;
+ return 1;
+ } else {
+ float u = (float) STBTT_sqrt(-p/3);
+ float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
+ float m = (float) STBTT_cos(v);
+ float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
+ r[0] = s + u * 2 * m;
+ r[1] = s - u * (m + n);
+ r[2] = s - u * (m - n);
+
+ //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
+ //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
+ //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
+ return 3;
+ }
+}
+
+STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
+{
+ float scale_x = scale, scale_y = scale;
+ int ix0,iy0,ix1,iy1;
+ int w,h;
+ unsigned char *data;
+
+ if (scale == 0) return NULL;
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
+
+ // if empty, return NULL
+ if (ix0 == ix1 || iy0 == iy1)
+ return NULL;
+
+ ix0 -= padding;
+ iy0 -= padding;
+ ix1 += padding;
+ iy1 += padding;
+
+ w = (ix1 - ix0);
+ h = (iy1 - iy0);
+
+ if (width ) *width = w;
+ if (height) *height = h;
+ if (xoff ) *xoff = ix0;
+ if (yoff ) *yoff = iy0;
+
+ // invert for y-downwards bitmaps
+ scale_y = -scale_y;
+
+ {
+ int x,y,i,j;
+ float *precompute;
+ stbtt_vertex *verts;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
+ data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
+ precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
+
+ for (i=0,j=num_verts-1; i < num_verts; j=i++) {
+ if (verts[i].type == STBTT_vline) {
+ float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
+ float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
+ float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
+ precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
+ } else if (verts[i].type == STBTT_vcurve) {
+ float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
+ float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
+ float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
+ float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
+ float len2 = bx*bx + by*by;
+ if (len2 != 0.0f)
+ precompute[i] = 1.0f / (bx*bx + by*by);
+ else
+ precompute[i] = 0.0f;
+ } else
+ precompute[i] = 0.0f;
+ }
+
+ for (y=iy0; y < iy1; ++y) {
+ for (x=ix0; x < ix1; ++x) {
+ float val;
+ float min_dist = 999999.0f;
+ float sx = (float) x + 0.5f;
+ float sy = (float) y + 0.5f;
+ float x_gspace = (sx / scale_x);
+ float y_gspace = (sy / scale_y);
+
+ int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
+
+ for (i=0; i < num_verts; ++i) {
+ float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
+
+ if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
+ float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
+
+ float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
+ if (dist2 < min_dist*min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+
+ // coarse culling against bbox
+ //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
+ // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
+ dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
+ STBTT_assert(i != 0);
+ if (dist < min_dist) {
+ // check position along line
+ // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
+ // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
+ float dx = x1-x0, dy = y1-y0;
+ float px = x0-sx, py = y0-sy;
+ // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
+ // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
+ float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
+ if (t >= 0.0f && t <= 1.0f)
+ min_dist = dist;
+ }
+ } else if (verts[i].type == STBTT_vcurve) {
+ float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
+ float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y;
+ float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
+ float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
+ float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
+ float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
+ // coarse culling against bbox to avoid computing cubic unnecessarily
+ if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
+ int num=0;
+ float ax = x1-x0, ay = y1-y0;
+ float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
+ float mx = x0 - sx, my = y0 - sy;
+ float res[3] = {0.f,0.f,0.f};
+ float px,py,t,it,dist2;
+ float a_inv = precompute[i];
+ if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
+ float a = 3*(ax*bx + ay*by);
+ float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
+ float c = mx*ax+my*ay;
+ if (a == 0.0) { // if a is 0, it's linear
+ if (b != 0.0) {
+ res[num++] = -c/b;
+ }
+ } else {
+ float discriminant = b*b - 4*a*c;
+ if (discriminant < 0)
+ num = 0;
+ else {
+ float root = (float) STBTT_sqrt(discriminant);
+ res[0] = (-b - root)/(2*a);
+ res[1] = (-b + root)/(2*a);
+ num = 2; // don't bother distinguishing 1-solution case, as code below will still work
+ }
+ }
+ } else {
+ float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
+ float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
+ float d = (mx*ax+my*ay) * a_inv;
+ num = stbtt__solve_cubic(b, c, d, res);
+ }
+ dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
+ if (dist2 < min_dist*min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+
+ if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
+ t = res[0], it = 1.0f - t;
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
+ if (dist2 < min_dist * min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+ }
+ if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
+ t = res[1], it = 1.0f - t;
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
+ if (dist2 < min_dist * min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+ }
+ if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
+ t = res[2], it = 1.0f - t;
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
+ if (dist2 < min_dist * min_dist)
+ min_dist = (float) STBTT_sqrt(dist2);
+ }
+ }
+ }
+ }
+ if (winding == 0)
+ min_dist = -min_dist; // if outside the shape, value is negative
+ val = onedge_value + pixel_dist_scale * min_dist;
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+ data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
+ }
+ }
+ STBTT_free(precompute, info->userdata);
+ STBTT_free(verts, info->userdata);
+ }
+ return data;
+}
+
+STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
+}
+
+STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
+{
+ STBTT_free(bitmap, userdata);
+}
//////////////////////////////////////////////////////////////////////////////
//
@@ -3050,7 +4773,7 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i
//
// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2)
+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
{
stbtt_int32 i=0;
@@ -3089,9 +4812,9 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8
return i;
}
-STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
+static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
{
- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2);
+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
}
// returns results in whatever encoding you request... but note that 2-byte encodings
@@ -3147,7 +4870,7 @@ static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name,
return 1;
} else if (matchlen < nlen && name[matchlen] == ' ') {
++matchlen;
- if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
+ if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
return 1;
}
} else {
@@ -3193,7 +4916,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam
return 0;
}
-STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
+static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
{
stbtt_int32 i;
for (i=0;;++i) {
@@ -3204,11 +4927,71 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const
}
}
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
+STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
+ float pixel_height, unsigned char *pixels, int pw, int ph,
+ int first_char, int num_chars, stbtt_bakedchar *chardata)
+{
+ return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
+}
+
+STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
+{
+ return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
+}
+
+STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
+{
+ return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
+}
+
+STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
+{
+ return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
+}
+
+STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
+{
+ return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
+}
+
+STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
+{
+ return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
#endif // STB_TRUETYPE_IMPLEMENTATION
// FULL VERSION HISTORY
//
+// 1.25 (2021-07-11) many fixes
+// 1.24 (2020-02-05) fix warning
+// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
+// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
+// 1.21 (2019-02-25) fix warning
+// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
+// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
+// 1.18 (2018-01-29) add missing function
+// 1.17 (2017-07-23) make more arguments const; doc fix
+// 1.16 (2017-07-12) SDF support
+// 1.15 (2017-03-03) make more arguments const
+// 1.14 (2017-01-16) num-fonts-in-TTC function
+// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
+// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) allow user-defined fabs() replacement
+// fix memory leak if fontsize=0.0
+// fix warning from duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
// allow PackFontRanges to pack and render in separate phases;
@@ -3250,3 +5033,45 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const
// 0.2 (2009-03-11) Fix unsigned/signed char warnings
// 0.1 (2009-03-09) First public release
//
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index a21fde0e1d..05f9304780 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -596,6 +596,48 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size)
return ops->select_font(dev, name, size);
}
+int vidconsole_measure(struct udevice *dev, const char *name, uint size,
+ const char *text, struct vidconsole_bbox *bbox)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+ int ret;
+
+ if (ops->select_font) {
+ ret = ops->measure(dev, name, size, text, bbox);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+
+ bbox->valid = true;
+ bbox->x0 = 0;
+ bbox->y0 = 0;
+ bbox->x1 = priv->x_charsize * strlen(text);
+ bbox->y1 = priv->y_charsize;
+
+ return 0;
+}
+
+void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg,
+ enum colour_idx bg, struct vidconsole_colour *old)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+
+ old->colour_fg = vid_priv->colour_fg;
+ old->colour_bg = vid_priv->colour_bg;
+
+ vid_priv->colour_fg = video_index_to_colour(vid_priv, fg);
+ vid_priv->colour_bg = video_index_to_colour(vid_priv, bg);
+}
+
+void vidconsole_pop_colour(struct udevice *dev, struct vidconsole_colour *old)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+
+ vid_priv->colour_fg = old->colour_fg;
+ vid_priv->colour_bg = old->colour_bg;
+}
+
/* Set up the number of rows and colours (rotated drivers override this) */
static int vidconsole_pre_probe(struct udevice *dev)
{
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index 1b66a8061a..95e874b770 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -142,6 +142,58 @@ int video_reserve(ulong *addrp)
return 0;
}
+int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
+ int yend, u32 colour)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ void *start, *line;
+ int pixels = xend - xstart;
+ int row, i, ret;
+
+ start = priv->fb + ystart * priv->line_length;
+ start += xstart * VNBYTES(priv->bpix);
+ line = start;
+ for (row = ystart; row < yend; row++) {
+ switch (priv->bpix) {
+ case VIDEO_BPP8: {
+ u8 *dst = line;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
+ for (i = 0; i < pixels; i++)
+ *dst++ = colour;
+ }
+ break;
+ }
+ case VIDEO_BPP16: {
+ u16 *dst = line;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
+ for (i = 0; i < pixels; i++)
+ *dst++ = colour;
+ }
+ break;
+ }
+ case VIDEO_BPP32: {
+ u32 *dst = line;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
+ for (i = 0; i < pixels; i++)
+ *dst++ = colour;
+ }
+ break;
+ }
+ default:
+ return -ENOSYS;
+ }
+ line += priv->line_length;
+ }
+ ret = video_sync_copy(dev, start, line);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
int video_fill(struct udevice *dev, u32 colour)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
@@ -208,7 +260,7 @@ static const struct vid_rgb colours[VID_COLOUR_COUNT] = {
{ 0xff, 0xff, 0xff }, /* white */
};
-u32 video_index_to_colour(struct video_priv *priv, unsigned int idx)
+u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx)
{
switch (priv->bpix) {
case VIDEO_BPP16:
diff --git a/fs/fs.c b/fs/fs.c
index 8324b4a22f..2b815b1db0 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -13,6 +13,7 @@
#include <env.h>
#include <lmb.h>
#include <log.h>
+#include <malloc.h>
#include <mapmem.h>
#include <part.h>
#include <ext4fs.h>
@@ -26,6 +27,7 @@
#include <asm/io.h>
#include <div64.h>
#include <linux/math64.h>
+#include <linux/sizes.h>
#include <efi_loader.h>
#include <squashfs.h>
#include <erofs.h>
@@ -1008,3 +1010,59 @@ int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
puts("\n");
return CMD_RET_SUCCESS;
}
+
+int fs_read_alloc(const char *fname, ulong size, uint align, void **bufp)
+{
+ loff_t bytes_read;
+ ulong addr;
+ char *buf;
+ int ret;
+
+ buf = memalign(align, size + 1);
+ if (!buf)
+ return log_msg_ret("buf", -ENOMEM);
+ addr = map_to_sysmem(buf);
+
+ ret = fs_read(fname, addr, 0, size, &bytes_read);
+ if (ret) {
+ free(buf);
+ return log_msg_ret("read", ret);
+ }
+ if (size != bytes_read)
+ return log_msg_ret("bread", -EIO);
+ buf[size] = '\0';
+
+ *bufp = buf;
+
+ return 0;
+}
+
+int fs_load_alloc(const char *ifname, const char *dev_part_str,
+ const char *fname, ulong max_size, ulong align, void **bufp,
+ ulong *sizep)
+{
+ loff_t size;
+ void *buf;
+ int ret;
+
+ if (fs_set_blk_dev(ifname, dev_part_str, FS_TYPE_ANY))
+ return log_msg_ret("set", -ENOMEDIUM);
+
+ ret = fs_size(fname, &size);
+ if (ret)
+ return log_msg_ret("sz", -ENOENT);
+
+ if (size >= (max_size ?: SZ_1G))
+ return log_msg_ret("sz", -E2BIG);
+
+ if (fs_set_blk_dev(ifname, dev_part_str, FS_TYPE_ANY))
+ return log_msg_ret("set", -ENOMEDIUM);
+
+ ret = fs_read_alloc(fname, size, align, &buf);
+ if (ret)
+ return log_msg_ret("al", ret);
+ *sizep = size;
+ *bufp = buf;
+
+ return 0;
+}
diff --git a/include/dm/of.h b/include/dm/of.h
index fce7cef0ff..b1c934f610 100644
--- a/include/dm/of.h
+++ b/include/dm/of.h
@@ -63,6 +63,8 @@ struct device_node {
struct device_node *sibling;
};
+#define BAD_OF_ROOT 0xdead11e3
+
#define OF_MAX_PHANDLE_ARGS 16
/**
diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h
index 443db6252d..0f38b3e736 100644
--- a/include/dm/ofnode.h
+++ b/include/dm/ofnode.h
@@ -353,6 +353,16 @@ static inline oftree oftree_from_np(struct device_node *root)
}
/**
+ * oftree_dispose() - Dispose of an oftree
+ *
+ * This can be used to dispose of a tree that has been created (other than
+ * the control FDT which must not be disposed)
+ *
+ * @tree: Tree to dispose
+ */
+void oftree_dispose(oftree tree);
+
+/**
* ofnode_name_eq() - Check if the node name is equivalent to a given name
* ignoring the unit address
*
diff --git a/include/expo.h b/include/expo.h
index d242f48e30..0b1d944a16 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -7,22 +7,31 @@
#ifndef __SCENE_H
#define __SCENE_H
+#include <dm/ofnode_decl.h>
#include <linux/list.h>
struct udevice;
+struct video_priv;
/**
* enum expoact_type - types of actions reported by the expo
*
* @EXPOACT_NONE: no action
- * @EXPOACT_POINT: menu item was highlighted (@id indicates which)
+ * @EXPOACT_POINT_OBJ: object was highlighted (@id indicates which)
+ * @EXPOACT_POINT_ITEM: menu item was highlighted (@id indicates which)
* @EXPOACT_SELECT: menu item was selected (@id indicates which)
+ * @EXPOACT_OPEN: menu was opened, so an item can be selected (@id indicates
+ * which menu object)
+ * @EXPOACT_CLOSE: menu was closed (@id indicates which menu object)
* @EXPOACT_QUIT: request to exit the menu
*/
enum expoact_type {
EXPOACT_NONE,
- EXPOACT_POINT,
+ EXPOACT_POINT_OBJ,
+ EXPOACT_POINT_ITEM,
EXPOACT_SELECT,
+ EXPOACT_OPEN,
+ EXPOACT_CLOSE,
EXPOACT_QUIT,
};
@@ -30,7 +39,7 @@ enum expoact_type {
* struct expo_action - an action report by the expo
*
* @type: Action type (EXPOACT_NONE if there is no action)
- * @select: Used for EXPOACT_POINT and EXPOACT_SELECT
+ * @select: Used for EXPOACT_POINT_ITEM and EXPOACT_SELECT
* @id: ID number of the object affected.
*/
struct expo_action {
@@ -43,6 +52,19 @@ struct expo_action {
};
/**
+ * struct expo_theme - theme for the expo
+ *
+ * @font_size: Default font size for all text
+ * @menu_inset: Inset width (on each side and top/bottom) for menu items
+ * @menuitem_gap_y: Gap between menu items in pixels
+ */
+struct expo_theme {
+ u32 font_size;
+ u32 menu_inset;
+ u32 menuitem_gap_y;
+};
+
+/**
* struct expo - information about an expo
*
* A group of scenes which can be presented to the user, typically to obtain
@@ -50,23 +72,29 @@ struct expo_action {
*
* @name: Name of the expo (allocated)
* @display: Display to use (`UCLASS_VIDEO`), or NULL to use text mode
+ * @cons: Console to use (`UCLASS_VIDEO_CONSOLE`), or NULL to use text mode
* @scene_id: Current scene ID (0 if none)
* @next_id: Next ID number to use, for automatic allocation
* @action: Action selected by user. At present only one is supported, with the
* type set to EXPOACT_NONE if there is no action
* @text_mode: true to use text mode for the menu (no vidconsole)
+ * @popup: true to use popup menus, instead of showing all items
* @priv: Private data for the controller
+ * @theme: Information about fonts styles, etc.
* @scene_head: List of scenes
* @str_head: list of strings
*/
struct expo {
char *name;
struct udevice *display;
+ struct udevice *cons;
uint scene_id;
uint next_id;
struct expo_action action;
bool text_mode;
+ bool popup;
void *priv;
+ struct expo_theme theme;
struct list_head scene_head;
struct list_head str_head;
};
@@ -92,7 +120,8 @@ struct expo_string {
* @expo: Expo this scene is part of
* @name: Name of the scene (allocated)
* @id: ID number of the scene
- * @title: Title of the scene (allocated)
+ * @title_id: String ID of title of the scene (allocated)
+ * @highlight_id: ID of highlighted object, if any
* @sibling: Node to link this scene to its siblings
* @obj_head: List of objects in the scene
*/
@@ -100,7 +129,8 @@ struct scene {
struct expo *expo;
char *name;
uint id;
- char *title;
+ uint title_id;
+ uint highlight_id;
struct list_head sibling;
struct list_head obj_head;
};
@@ -121,15 +151,43 @@ enum scene_obj_t {
};
/**
+ * struct scene_dim - Dimensions of an object
+ *
+ * @x: x position, in pixels from left side
+ * @y: y position, in pixels from top
+ * @w: width, in pixels
+ * @h: height, in pixels
+ */
+struct scene_dim {
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+/**
+ * enum scene_obj_flags_t - flags for objects
+ *
+ * @SCENEOF_HIDE: object should be hidden
+ * @SCENEOF_POINT: object should be highlighted
+ * @SCENEOF_OPEN: object should be opened (e.g. menu is opened so that an option
+ * can be selected)
+ */
+enum scene_obj_flags_t {
+ SCENEOF_HIDE = 1 << 0,
+ SCENEOF_POINT = 1 << 1,
+ SCENEOF_OPEN = 1 << 2,
+};
+
+/**
* struct scene_obj - information about an object in a scene
*
* @scene: Scene that this object relates to
* @name: Name of the object (allocated)
* @id: ID number of the object
* @type: Type of this object
- * @x: x position, in pixels from left side
- * @y: y position, in pixels from top
- * @hide: true if the object should be hidden
+ * @dim: Dimensions for this object
+ * @flags: Flags for this object
* @sibling: Node to link this object to its siblings
*/
struct scene_obj {
@@ -137,9 +195,8 @@ struct scene_obj {
char *name;
uint id;
enum scene_obj_t type;
- int x;
- int y;
- bool hide;
+ struct scene_dim dim;
+ int flags;
struct list_head sibling;
};
@@ -256,6 +313,25 @@ int expo_new(const char *name, void *priv, struct expo **expp);
void expo_destroy(struct expo *exp);
/**
+ * expo_set_dynamic_start() - Set the start of the 'dynamic' IDs
+ *
+ * It is common for a set of 'static' IDs to be used to refer to objects in the
+ * expo. These typically use an enum so that they are defined in sequential
+ * order.
+ *
+ * Dynamic IDs (for objects not in the enum) are intended to be used for
+ * objects to which the code does not need to refer. These are ideally located
+ * above the static IDs.
+ *
+ * Use this function to set the start of the dynamic range, making sure that the
+ * value is higher than all the statically allocated IDs.
+ *
+ * @exp: Expo to update
+ * @dyn_start: Start ID that expo should use for dynamic allocation
+ */
+void expo_set_dynamic_start(struct expo *exp, uint dyn_start);
+
+/**
* expo_str() - add a new string to an expo
*
* @exp: Expo to update
@@ -285,6 +361,16 @@ const char *expo_get_str(struct expo *exp, uint id);
int expo_set_display(struct expo *exp, struct udevice *dev);
/**
+ * expo_calc_dims() - Calculate the dimensions of the objects
+ *
+ * Updates the width and height of all objects based on their contents
+ *
+ * @exp: Expo to update
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int expo_calc_dims(struct expo *exp);
+
+/**
* expo_set_scene_id() - Set the current scene ID
*
* @exp: Expo to update
@@ -294,6 +380,14 @@ int expo_set_display(struct expo *exp, struct udevice *dev);
int expo_set_scene_id(struct expo *exp, uint scene_id);
/**
+ * expo_first_scene_id() - Get the ID of the first scene
+ *
+ * @exp: Expo to check
+ * Returns: Scene ID of first scene, or -ENOENT if there are no scenes
+ */
+int expo_first_scene_id(struct expo *exp);
+
+/**
* expo_render() - render the expo on the display / console
*
* @exp: Expo to render
@@ -304,12 +398,12 @@ int expo_set_scene_id(struct expo *exp, uint scene_id);
int expo_render(struct expo *exp);
/**
- * exp_set_text_mode() - Controls whether the expo renders in text mode
+ * expo_set_text_mode() - Controls whether the expo renders in text mode
*
* @exp: Expo to update
* @text_mode: true to use text mode, false to use the console
*/
-void exp_set_text_mode(struct expo *exp, bool text_mode);
+void expo_set_text_mode(struct expo *exp, bool text_mode);
/**
* scene_new() - create a new scene in a expo
@@ -335,13 +429,43 @@ int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp);
struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id);
/**
+ * scene_highlight_first() - Highlight the first item in a scene
+ *
+ * This highlights the first item, so that the user can see that it is pointed
+ * to
+ *
+ * @scn: Scene to update
+ */
+void scene_highlight_first(struct scene *scn);
+
+/**
+ * scene_set_highlight_id() - Set the object which is highlighted
+ *
+ * Sets a new object to highlight in the scene
+ *
+ * @scn: Scene to update
+ * @id: ID of object to highlight
+ */
+void scene_set_highlight_id(struct scene *scn, uint id);
+
+/**
+ * scene_set_open() - Set whether an item is open or not
+ *
+ * @scn: Scene to update
+ * @id: ID of object to update
+ * @open: true to open the object, false to close it
+ * Returns: 0 if OK, -ENOENT if @id is invalid
+ */
+int scene_set_open(struct scene *scn, uint id, bool open);
+
+/**
* scene_title_set() - set the scene title
*
* @scn: Scene to update
- * @title: Title to set, NULL if none (this is allocated by this call)
- * Returns: 0 if OK, -ENOMEM if out of memory
+ * @title_id: Title ID to set
+ * Returns: 0 if OK
*/
-int scene_title_set(struct scene *scn, const char *title);
+int scene_title_set(struct scene *scn, uint title_id);
/**
* scene_obj_count() - Count the number of objects in a scene
@@ -426,6 +550,17 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
int scene_obj_set_pos(struct scene *scn, uint id, int x, int y);
/**
+ * scene_obj_set_size() - Set the size of an object
+ *
+ * @scn: Scene to update
+ * @id: ID of object to update
+ * @w: width in pixels
+ * @h: height in pixels
+ * Returns: 0 if OK, -ENOENT if @id is invalid
+ */
+int scene_obj_set_size(struct scene *scn, uint id, int w, int h);
+
+/**
* scene_obj_set_hide() - Set whether an object is hidden
*
* The update happens when the expo is next rendered.
@@ -519,4 +654,46 @@ int expo_send_key(struct expo *exp, int key);
*/
int expo_action_get(struct expo *exp, struct expo_action *act);
+/**
+ * expo_apply_theme() - Apply a theme to an expo
+ *
+ * @exp: Expo to update
+ * @node: Node containing the theme
+ */
+int expo_apply_theme(struct expo *exp, ofnode node);
+
+/**
+ * expo_build() - Build an expo from an FDT description
+ *
+ * Build a complete expo from a description in the provided devicetree.
+ *
+ * See doc/developer/expo.rst for a description of the format
+ *
+ * @root: Root node for expo description
+ * @expp: Returns the new expo
+ * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
+ * error, -ENOENT if there is a references to a non-existent string
+ */
+int expo_build(ofnode root, struct expo **expp);
+
+/**
+ * cedit_arange() - Arrange objects in a configuration-editor scene
+ *
+ * @exp: Expo to update
+ * @vid_priv: Private info of the video device
+ * @scene_id: scene ID to arrange
+ * Returns: 0 if OK, -ve on error
+ */
+int cedit_arange(struct expo *exp, struct video_priv *vid_priv, uint scene_id);
+
+/**
+ * cedit_run() - Run a configuration editor
+ *
+ * This accepts input until the user quits with Escape
+ *
+ * @exp: Expo to use
+ * Returns: 0 if OK, -ve on error
+ */
+int cedit_run(struct expo *exp);
+
#endif /*__SCENE_H */
diff --git a/include/fs.h b/include/fs.h
index 8370d88cb2..e341a0ed01 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -300,4 +300,42 @@ int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
*/
int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
+/**
+ * fs_read_alloc() - Allocate space for a file and read it
+ *
+ * You must call fs_set_blk_dev() or a similar function before calling this,
+ * since that sets up the block device to use.
+ *
+ * The file is terminated with a nul character
+ *
+ * @fname: Filename to read
+ * @size: Size of file to read (must be correct!)
+ * @align: Alignment to use for memory allocation (0 for default)
+ * @bufp: On success, returns the allocated buffer with the nul-terminated file
+ * in it
+ * Return: 0 if OK, -ENOMEM if out of memory, -EIO if read failed
+ */
+int fs_read_alloc(const char *fname, ulong size, uint align, void **bufp);
+
+/**
+ * fs_load_alloc() - Load a file into allocated space
+ *
+ * The file is terminated with a nul character
+ *
+ * @ifname: Interface name to read from (e.g. "mmc")
+ * @dev_part_str: Device and partition string (e.g. "1:2")
+ * @fname: Filename to read
+ * @max_size: Maximum allowed size for the file (use 0 for 1GB)
+ * @align: Alignment to use for memory allocation (0 for default)
+ * @bufp: On success, returns the allocated buffer with the nul-terminated file
+ * in it
+ * @sizep: On success, returns the size of the file
+ * Return: 0 if OK, -ENOMEM if out of memory, -ENOENT if the file does not
+ * exist, -ENOMEDIUM if the device does not exist, -E2BIG if the file is too
+ * large (greater than @max_size), -EIO if read failed
+ */
+int fs_load_alloc(const char *ifname, const char *dev_part_str,
+ const char *fname, ulong max_size, ulong align, void **bufp,
+ ulong *sizep);
+
#endif /* _FS_H */
diff --git a/include/log.h b/include/log.h
index 3bab40b617..6e84f080ef 100644
--- a/include/log.h
+++ b/include/log.h
@@ -102,6 +102,8 @@ enum log_category_t {
LOGC_EVENT,
/** @LOGC_FS: Related to filesystems */
LOGC_FS,
+ /** @LOGC_EXPO: Related to expo handling */
+ LOGC_EXPO,
/** @LOGC_COUNT: Number of log categories */
LOGC_COUNT,
/** @LOGC_END: Sentinel value for lists of log categories */
diff --git a/include/of_live.h b/include/of_live.h
index f59d6af335..05e86ac06b 100644
--- a/include/of_live.h
+++ b/include/of_live.h
@@ -36,4 +36,14 @@ int of_live_build(const void *fdt_blob, struct device_node **rootp);
*/
int unflatten_device_tree(const void *blob, struct device_node **mynodes);
+/**
+ * of_live_free() - Dispose of a livetree
+ *
+ * This frees memory used by the tree, after which @root becomes invalid and
+ * cannot be used
+ *
+ * @root: Tree to dispose
+ */
+void of_live_free(struct device_node *root);
+
#endif
diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h
new file mode 100644
index 0000000000..349df75b16
--- /dev/null
+++ b/include/test/cedit-test.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Binding shared between cedit.dtsi and test/boot/expo.c
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __cedit_test_h
+#define __cedit_test_h
+
+#define ID_PROMPT 1
+#define ID_SCENE1 2
+#define ID_SCENE1_TITLE 3
+
+#define ID_CPU_SPEED 4
+#define ID_CPU_SPEED_TITLE 5
+#define ID_CPU_SPEED_1 6
+#define ID_CPU_SPEED_2 7
+#define ID_CPU_SPEED_3 8
+
+#define ID_POWER_LOSS 9
+#define ID_AC_OFF 10
+#define ID_AC_ON 11
+#define ID_AC_MEMORY 12
+
+#define ID_DYNAMIC_START 13
+
+#endif
diff --git a/include/test/ut.h b/include/test/ut.h
index dddf9ad241..ea6ee95d73 100644
--- a/include/test/ut.h
+++ b/include/test/ut.h
@@ -130,7 +130,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
\
if (!(cond)) { \
ut_fail(uts, __FILE__, __LINE__, __func__, #cond); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -142,7 +142,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
if (!(cond)) { \
ut_failf(uts, __FILE__, __LINE__, __func__, #cond, \
fmt, ##args); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -157,7 +157,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
#expr1 " == " #expr2, \
"Expected %#x (%d), got %#x (%d)", \
_val1, _val1, _val2, _val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -175,7 +175,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
(unsigned long long)_val1, \
(unsigned long long)_val2, \
(unsigned long long)_val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -189,7 +189,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
#expr1 " = " #expr2, \
"Expected \"%s\", got \"%s\"", _val1, _val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -208,7 +208,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
#expr1 " = " #expr2, \
"Expected \"%.*s\", got \"%.*s\"", \
_len, _val1, _len, _val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -228,7 +228,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
#expr1 " = " #expr2, \
"Expected \"%s\", got \"%s\"", \
__buf1, __buf2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -242,7 +242,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
#expr1 " = " #expr2, \
"Expected %p, got %p", _val1, _val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -257,7 +257,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
#expr1 " = " #expr2, \
"Expected %lx, got %lx", _val1, _val2); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -271,7 +271,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
#expr " != NULL", \
"Expected NULL, got %p", _val); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -285,7 +285,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
#expr " = NULL", \
"Expected non-null, got NULL"); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -300,7 +300,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
#expr " = NULL", \
"Expected pointer, got error %ld", \
PTR_ERR(_val)); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -316,7 +316,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
"console", "\nExpected '%s',\n got '%s'", \
uts->expect_str, uts->actual_str); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -329,7 +329,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
"console", "\nExpected '%s',\n got '%s'", \
uts->expect_str, uts->actual_str); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -341,7 +341,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
if (ut_check_skipline(uts)) { \
ut_failf(uts, __FILE__, __LINE__, __func__, \
"console", "\nExpected a line, got end"); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -354,7 +354,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
"console", "\nExpected '%s',\n got to '%s'", \
uts->expect_str, uts->actual_str); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -367,7 +367,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
ut_failf(uts, __FILE__, __LINE__, __func__, \
"console", "Expected no more output, got '%s'",\
uts->actual_str); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
@@ -381,7 +381,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
"console", \
"Expected dump of length %x bytes, got '%s'", \
total_bytes, uts->actual_str); \
- __ret = CMD_RET_FAILURE; \
+ return CMD_RET_FAILURE; \
} \
__ret; \
})
diff --git a/include/video.h b/include/video.h
index 03434a8123..e98d0f9c89 100644
--- a/include/video.h
+++ b/include/video.h
@@ -163,11 +163,11 @@ enum colour_idx {
* The caller has to guarantee that the color index is less than
* VID_COLOR_COUNT.
*
- * @priv private data of the console device
- * @idx color index
+ * @priv private data of the video device (UCLASS_VIDEO)
+ * @idx color index (e.g. VID_YELLOW)
* Return: color value
*/
-u32 video_index_to_colour(struct video_priv *priv, unsigned int idx);
+u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx);
/**
* video_reserve() - Reserve frame-buffer memory for video devices
@@ -205,6 +205,22 @@ int video_clear(struct udevice *dev);
int video_fill(struct udevice *dev, u32 colour);
/**
+ * video_fill_part() - Erase a region
+ *
+ * Erase a rectangle of the display within the given bounds.
+ *
+ * @dev: Device to update
+ * @xstart: X start position in pixels from the left
+ * @ystart: Y start position in pixels from the top
+ * @xend: X end position in pixels from the left
+ * @yend: Y end position in pixels from the top
+ * @colour: Value to write
+ * Return: 0 if OK, -ENOSYS if the display depth is not supported
+ */
+int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend,
+ int yend, u32 colour);
+
+/**
* video_sync() - Sync a device's frame buffer with its hardware
*
* @vid: Device to sync
diff --git a/include/video_console.h b/include/video_console.h
index 3db9a7e1fb..2694e44f6e 100644
--- a/include/video_console.h
+++ b/include/video_console.h
@@ -72,6 +72,38 @@ struct vidfont_info {
};
/**
+ * struct vidconsole_colour - Holds colour information
+ *
+ * @colour_fg: Foreground colour (pixel value)
+ * @colour_bg: Background colour (pixel value)
+ */
+struct vidconsole_colour {
+ u32 colour_fg;
+ u32 colour_bg;
+};
+
+/**
+ * struct vidconsole_bbox - Bounding box of text
+ *
+ * This describes the bounding box of something, measured in pixels. The x0/y0
+ * pair is inclusive; the x1/y2 pair is exclusive, meaning that it is one pixel
+ * beyond the extent of the object
+ *
+ * @valid: Values are valid (bounding box is known)
+ * @x0: left x position, in pixels from left side
+ * @y0: top y position, in pixels from top
+ * @x1: right x position + 1
+ * @y1: botton y position + 1
+ */
+struct vidconsole_bbox {
+ bool valid;
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+};
+
+/**
* struct vidconsole_ops - Video console operations
*
* These operations work on either an absolute console position (measured
@@ -178,6 +210,20 @@ struct vidconsole_ops {
* Returns: 0 on success, -ENOENT if no such font
*/
int (*select_font)(struct udevice *dev, const char *name, uint size);
+
+ /**
+ * measure() - Measure the bounds of some text
+ *
+ * @dev: Device to adjust
+ * @name: Font name to use (NULL to use default)
+ * @size: Font size to use (0 to use default)
+ * @text: Text to measure
+ * @bbox: Returns bounding box of text, assuming it is positioned
+ * at 0,0
+ * 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);
};
/* Get a pointer to the driver operations for a video console device */
@@ -204,6 +250,38 @@ int vidconsole_get_font(struct udevice *dev, int seq,
*/
int vidconsole_select_font(struct udevice *dev, const char *name, uint size);
+/*
+ * vidconsole_measure() - Measuring the bounding box of some text
+ *
+ * @dev: Console device to use
+ * @name: Font name, NULL for default
+ * @size: Font size, ignored if @name is NULL
+ * @text: Text to measure
+ * @bbox: Returns nounding box of text
+ * Returns: 0 if OK, -ve on error
+ */
+int vidconsole_measure(struct udevice *dev, const char *name, uint size,
+ const char *text, struct vidconsole_bbox *bbox);
+
+/**
+ * vidconsole_push_colour() - Temporarily change the font colour
+ *
+ * @dev: Device to adjust
+ * @fg: Foreground colour to select
+ * @bg: Background colour to select
+ * @old: Place to store the current colour, so it can be restored
+ */
+void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg,
+ enum colour_idx bg, struct vidconsole_colour *old);
+
+/**
+ * vidconsole_pop_colour() - Restore the original colour
+ *
+ * @dev: Device to adjust
+ * @old: Old colour to be restored
+ */
+void vidconsole_pop_colour(struct udevice *dev, struct vidconsole_colour *old);
+
/**
* vidconsole_putc_xy() - write a single character to a position
*
diff --git a/lib/of_live.c b/lib/of_live.c
index 1b5964d09a..25f7af6106 100644
--- a/lib/of_live.c
+++ b/lib/of_live.c
@@ -287,9 +287,12 @@ int unflatten_device_tree(const void *blob, struct device_node **mynodes)
debug(" size is %lx, allocating...\n", size);
/* Allocate memory for the expanded device tree */
- mem = malloc(size + 4);
+ mem = memalign(__alignof__(struct device_node), size + 4);
memset(mem, '\0', size);
+ /* Set up value for dm_test_livetree_align() */
+ *(u32 *)mem = BAD_OF_ROOT;
+
*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
debug(" unflattening %p...\n", mem);
@@ -327,3 +330,9 @@ int of_live_build(const void *fdt_blob, struct device_node **rootp)
return ret;
}
+
+void of_live_free(struct device_node *root)
+{
+ /* the tree is stored as a contiguous block of memory */
+ free(root);
+}
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 7104dff05e..3898f853a7 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -5,6 +5,7 @@
*/
#include <common.h>
+#include <command.h>
#include <dm.h>
#include <expo.h>
#include <menu.h>
@@ -13,6 +14,7 @@
#include <test/suites.h>
#include <test/ut.h>
#include "bootstd_common.h"
+#include <test/cedit-test.h>
#include "../../boot/scene_internal.h"
enum {
@@ -28,6 +30,8 @@ enum {
OBJ_MENU_TITLE,
/* strings */
+ STR_SCENE_TITLE,
+
STR_TEXT,
STR_TEXT2,
STR_MENU_TITLE,
@@ -120,7 +124,7 @@ static int expo_scene(struct unit_test_state *uts)
struct expo *exp;
ulong start_mem;
char name[100];
- int id;
+ int id, title_id;
start_mem = ut_check_free();
@@ -141,21 +145,20 @@ static int expo_scene(struct unit_test_state *uts)
ut_asserteq_str(SCENE_NAME1, scn->name);
/* Set the title */
- strcpy(name, SCENE_TITLE);
- ut_assertok(scene_title_set(scn, name));
- *name = '\0';
- ut_assertnonnull(scn->title);
- ut_asserteq_str(SCENE_TITLE, scn->title);
+ title_id = expo_str(exp, "title", STR_SCENE_TITLE, SCENE_TITLE);
+ ut_assert(title_id >= 0);
- /* Use an allocated ID */
+ /* Use an allocated ID - this will be allocated after the title str */
scn = NULL;
id = scene_new(exp, SCENE_NAME2, 0, &scn);
ut_assertnonnull(scn);
- ut_asserteq(SCENE2, id);
- ut_asserteq(SCENE2 + 1, exp->next_id);
+ ut_assertok(scene_title_set(scn, title_id));
+ ut_asserteq(STR_SCENE_TITLE + 1, id);
+ ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id);
ut_asserteq_ptr(exp, scn->expo);
ut_asserteq_str(SCENE_NAME2, scn->name);
+ ut_asserteq(title_id, scn->title_id);
expo_destroy(exp);
@@ -225,7 +228,7 @@ static int expo_object(struct unit_test_state *uts)
}
BOOTSTD_TEST(expo_object, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
-/* Check setting object attributes */
+/* Check setting object attributes and using themes */
static int expo_object_attr(struct unit_test_state *uts)
{
struct scene_obj_menu *menu;
@@ -235,6 +238,7 @@ static int expo_object_attr(struct unit_test_state *uts)
struct expo *exp;
ulong start_mem;
char name[100];
+ ofnode node;
char *data;
int id;
@@ -249,8 +253,8 @@ static int expo_object_attr(struct unit_test_state *uts)
ut_assert(id > 0);
ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456));
- ut_asserteq(123, img->obj.x);
- ut_asserteq(456, img->obj.y);
+ ut_asserteq(123, img->obj.dim.x);
+ ut_asserteq(456, img->obj.dim.y);
ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0));
@@ -272,6 +276,11 @@ static int expo_object_attr(struct unit_test_state *uts)
ut_asserteq(-ENOENT, scene_menu_set_title(scn, OBJ_TEXT2, OBJ_TEXT));
ut_asserteq(-EINVAL, scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT2));
+ node = ofnode_path("/bootstd/theme");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(expo_apply_theme(exp, node));
+ ut_asserteq(30, txt->font_size);
+
expo_destroy(exp);
ut_assertok(ut_check_delta(start_mem));
@@ -306,8 +315,8 @@ static int expo_object_menu(struct unit_test_state *uts)
ut_asserteq(0, menu->pointer_id);
ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
- ut_asserteq(50, menu->obj.x);
- ut_asserteq(400, menu->obj.y);
+ ut_asserteq(50, menu->obj.dim.x);
+ ut_asserteq(400, menu->obj.dim.y);
id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
"Main Menu", &tit);
@@ -347,29 +356,31 @@ static int expo_object_menu(struct unit_test_state *uts)
ut_asserteq(desc_id, item->desc_id);
ut_asserteq(preview_id, item->preview_id);
- /* adding an item should cause the first item to become current */
+ ut_assertok(scene_arrange(scn));
+
+ /* arranging the scene should cause the first item to become current */
ut_asserteq(id, menu->cur_item_id);
/* the title should be at the top */
- ut_asserteq(menu->obj.x, tit->obj.x);
- ut_asserteq(menu->obj.y, tit->obj.y);
+ ut_asserteq(menu->obj.dim.x, tit->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y, tit->obj.dim.y);
/* the first item should be next */
- ut_asserteq(menu->obj.x, name1->obj.x);
- ut_asserteq(menu->obj.y + 32, name1->obj.y);
+ ut_asserteq(menu->obj.dim.x, name1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, name1->obj.dim.y);
- ut_asserteq(menu->obj.x + 230, key1->obj.x);
- ut_asserteq(menu->obj.y + 32, key1->obj.y);
+ ut_asserteq(menu->obj.dim.x + 230, key1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, key1->obj.dim.y);
- ut_asserteq(menu->obj.x + 200, ptr->obj.x);
- ut_asserteq(menu->obj.y + 32, ptr->obj.y);
+ ut_asserteq(menu->obj.dim.x + 200, ptr->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, ptr->obj.dim.y);
- ut_asserteq(menu->obj.x + 280, desc1->obj.x);
- ut_asserteq(menu->obj.y + 32, desc1->obj.y);
+ ut_asserteq(menu->obj.dim.x + 280, desc1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, desc1->obj.dim.y);
- ut_asserteq(-4, prev1->obj.x);
- ut_asserteq(menu->obj.y + 32, prev1->obj.y);
- ut_asserteq(false, prev1->obj.hide);
+ ut_asserteq(-4, prev1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y);
+ ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE);
expo_destroy(exp);
@@ -470,6 +481,48 @@ static int expo_render_image(struct unit_test_state *uts)
/* render without a scene */
ut_asserteq(-ECHILD, expo_render(exp));
+ ut_assertok(expo_calc_dims(exp));
+ ut_assertok(scene_arrange(scn));
+
+ /* check dimensions of text */
+ obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(400, obj->dim.x);
+ ut_asserteq(100, obj->dim.y);
+ ut_asserteq(126, obj->dim.w);
+ ut_asserteq(40, obj->dim.h);
+
+ /* check dimensions of image */
+ obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(20, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
+ /* check dimensions of menu labels - both should be the same width */
+ obj = scene_obj_find(scn, ITEM1_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(436, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(454, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ /* check dimensions of menu */
+ obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(400, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
/* render it */
expo_set_scene_id(exp, SCENE1);
ut_assertok(expo_render(exp));
@@ -479,16 +532,16 @@ static int expo_render_image(struct unit_test_state *uts)
ut_assertok(expo_action_get(exp, &act));
- ut_asserteq(EXPOACT_POINT, act.type);
+ ut_asserteq(EXPOACT_POINT_ITEM, act.type);
ut_asserteq(ITEM2, act.select.id);
ut_assertok(expo_render(exp));
/* make sure only the preview for the second item is shown */
obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE);
- ut_asserteq(true, obj->hide);
+ ut_asserteq(true, obj->flags & SCENEOF_HIDE);
obj = scene_obj_find(scn, ITEM2_PREVIEW, SCENEOBJT_NONE);
- ut_asserteq(false, obj->hide);
+ ut_asserteq(false, obj->flags & SCENEOF_HIDE);
/* select it */
ut_assertok(expo_send_key(exp, BKEY_SELECT));
@@ -504,7 +557,7 @@ static int expo_render_image(struct unit_test_state *uts)
ut_assert_console_end();
/* now try in text mode */
- exp_set_text_mode(exp, true);
+ expo_set_text_mode(exp, true);
ut_assertok(expo_render(exp));
ut_assert_nextline("U-Boot : Boot Menu");
@@ -519,7 +572,7 @@ static int expo_render_image(struct unit_test_state *uts)
ut_assertok(expo_action_get(exp, &act));
- ut_asserteq(EXPOACT_POINT, act.type);
+ ut_asserteq(EXPOACT_POINT_ITEM, act.type);
ut_asserteq(ITEM1, act.select.id);
ut_assertok(expo_render(exp));
@@ -537,3 +590,125 @@ static int expo_render_image(struct unit_test_state *uts)
return 0;
}
BOOTSTD_TEST(expo_render_image, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check building an expo from a devicetree description */
+static int expo_test_build(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu;
+ struct scene_menitem *item;
+ struct scene_obj_txt *txt;
+ struct scene_obj *obj;
+ struct scene *scn;
+ struct expo *exp;
+ int count;
+ ofnode node;
+
+ node = ofnode_path("/cedit");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(expo_build(node, &exp));
+
+ ut_asserteq_str("name", exp->name);
+ ut_asserteq(0, exp->scene_id);
+ ut_asserteq(ID_DYNAMIC_START + 20, exp->next_id);
+ ut_asserteq(false, exp->popup);
+
+ /* check the scene */
+ scn = expo_lookup_scene_id(exp, ID_SCENE1);
+ ut_assertnonnull(scn);
+ ut_asserteq_str("main", scn->name);
+ ut_asserteq(ID_SCENE1, scn->id);
+ ut_asserteq(ID_DYNAMIC_START + 1, scn->title_id);
+ ut_asserteq(0, scn->highlight_id);
+
+ /* check the title */
+ txt = scene_obj_find(scn, scn->title_id, SCENEOBJT_NONE);
+ ut_assertnonnull(txt);
+ obj = &txt->obj;
+ ut_asserteq_ptr(scn, obj->scene);
+ ut_asserteq_str("title", obj->name);
+ ut_asserteq(scn->title_id, obj->id);
+ ut_asserteq(SCENEOBJT_TEXT, obj->type);
+ ut_asserteq(0, obj->flags);
+ ut_asserteq_str("Test Configuration", expo_get_str(exp, txt->str_id));
+
+ /* check the menu */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_NONE);
+ obj = &menu->obj;
+ ut_asserteq_ptr(scn, obj->scene);
+ ut_asserteq_str("cpu-speed", obj->name);
+ ut_asserteq(ID_CPU_SPEED, obj->id);
+ ut_asserteq(SCENEOBJT_MENU, obj->type);
+ ut_asserteq(0, obj->flags);
+
+ txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
+ ut_asserteq_str("CPU speed", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(0, menu->cur_item_id);
+ ut_asserteq(0, menu->pointer_id);
+
+ /* check the items */
+ item = list_first_entry(&menu->item_head, struct scene_menitem,
+ sibling);
+ ut_asserteq_str("00", item->name);
+ ut_asserteq(ID_CPU_SPEED_1, item->id);
+ ut_asserteq(0, item->key_id);
+ ut_asserteq(0, item->desc_id);
+ ut_asserteq(0, item->preview_id);
+ ut_asserteq(0, item->flags);
+
+ txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
+ ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id));
+
+ count = 0;
+ list_for_each_entry(item, &menu->item_head, sibling)
+ count++;
+ ut_asserteq(3, count);
+
+ expo_destroy(exp);
+
+ return 0;
+}
+BOOTSTD_TEST(expo_test_build, UT_TESTF_DM);
+
+/* Check the cedit command */
+static int expo_cedit(struct unit_test_state *uts)
+{
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene_obj_txt *txt;
+ struct expo *exp;
+ struct scene *scn;
+
+ if (!IS_ENABLED(CONFIG_CMD_CEDIT))
+ return -EAGAIN;
+
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ console_record_reset_enable();
+
+ /*
+ * ^N Move down to second menu
+ * ^M Open menu
+ * ^N Move down to second item
+ * ^M Select item
+ * \e Quit
+ */
+ console_in_puts("\x0e\x0d\x0e\x0d\e");
+ ut_assertok(run_command("cedit run", 0));
+
+ exp = cur_exp;
+ scn = expo_lookup_scene_id(exp, exp->scene_id);
+ ut_assertnonnull(scn);
+
+ menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE);
+ ut_assertnonnull(menu);
+
+ txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
+ ut_assertnonnull(txt);
+ ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(ID_AC_ON, menu->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(expo_cedit, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts
new file mode 100644
index 0000000000..55d5c910dd
--- /dev/null
+++ b/test/boot/files/expo_layout.dts
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Sample expo screen layout
+ */
+
+/dts-v1/;
+
+/*
+enum {
+ ZERO,
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
+
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
+
+ ID_DYNAMIC_START,
+};
+*/
+
+/ {
+ dynamic-start = <ID_DYNAMIC_START>;
+
+ scenes {
+ main {
+ id = <ID_SCENE1>;
+
+ /* value refers to the matching id in /strings */
+ title-id = <ID_SCENE1_TITLE>;
+
+ /* simple string is used as it is */
+ prompt = "UP and DOWN to choose, ENTER to select";
+
+ /* defines a menu within the scene */
+ cpu-speed {
+ type = "menu";
+ id = <ID_CPU_SPEED>;
+
+ /*
+ * has both string and ID. The string is ignored
+ * if the ID is present and points to a string
+ */
+ title = "CPU speed";
+ title-id = <ID_CPU_SPEED_TITLE>;
+
+ /* menu items as simple strings */
+ item-label = "2 GHz", "2.5 GHz", "3 GHz";
+
+ /* IDs for the menu items */
+ item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
+ ID_CPU_SPEED_3>;
+ };
+
+ power-loss {
+ type = "menu";
+ id = <ID_POWER_LOSS>;
+
+ title = "AC Power";
+ item-label = "Always Off", "Always On",
+ "Memory";
+
+ item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ };
+ };
+ };
+
+ strings {
+ title {
+ id = <ID_SCENE1_TITLE>;
+ value = "Test Configuration";
+ value-es = "configuraciĆ³n de prueba";
+ };
+ };
+};
diff --git a/test/cmd/bdinfo.c b/test/cmd/bdinfo.c
index 9068df79c4..cddf1a46d4 100644
--- a/test/cmd/bdinfo.c
+++ b/test/cmd/bdinfo.c
@@ -27,19 +27,25 @@ DECLARE_GLOBAL_DATA_PTR;
/* Declare a new bdinfo test */
#define BDINFO_TEST(_name, _flags) UNIT_TEST(_name, _flags, bdinfo_test)
-static void bdinfo_test_num_l(struct unit_test_state *uts,
- const char *name, ulong value)
+static int test_num_l(struct unit_test_state *uts, const char *name,
+ ulong value)
{
- ut_assert_nextline("%-12s= 0x%0*lx", name, 2 * (int)sizeof(value), value);
+ ut_assert_nextline("%-12s= 0x%0*lx", name, 2 * (int)sizeof(value),
+ value);
+
+ return 0;
}
-static void bdinfo_test_num_ll(struct unit_test_state *uts,
- const char *name, unsigned long long value)
+static int test_num_ll(struct unit_test_state *uts, const char *name,
+ unsigned long long value)
{
- ut_assert_nextline("%-12s= 0x%.*llx", name, 2 * (int)sizeof(ulong), value);
+ ut_assert_nextline("%-12s= 0x%.*llx", name, 2 * (int)sizeof(ulong),
+ value);
+
+ return 0;
}
-static void test_eth(struct unit_test_state *uts)
+static int test_eth(struct unit_test_state *uts)
{
const int idx = eth_get_dev_index();
uchar enetaddr[6];
@@ -59,9 +65,11 @@ static void test_eth(struct unit_test_state *uts)
else
ut_assert_nextline("%-12s= %pM", name, enetaddr);
ut_assert_nextline("IP addr = %s", env_get("ipaddr"));
+
+ return 0;
}
-static void test_video_info(struct unit_test_state *uts)
+static int test_video_info(struct unit_test_state *uts)
{
const struct udevice *dev;
struct uclass *uc;
@@ -73,22 +81,25 @@ static void test_video_info(struct unit_test_state *uts)
struct video_priv *upriv = dev_get_uclass_priv(dev);
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
- bdinfo_test_num_ll(uts, "FB base", (ulong)upriv->fb);
+ ut_assertok(test_num_ll(uts, "FB base",
+ (ulong)upriv->fb));
if (upriv->copy_fb) {
- bdinfo_test_num_ll(uts, "FB copy",
- (ulong)upriv->copy_fb);
- bdinfo_test_num_l(uts, " copy size",
- plat->copy_size);
+ ut_assertok(test_num_ll(uts, "FB copy",
+ (ulong)upriv->copy_fb));
+ ut_assertok(test_num_l(uts, " copy size",
+ plat->copy_size));
}
ut_assert_nextline("%-12s= %dx%dx%d", "FB size",
upriv->xsize, upriv->ysize,
1 << upriv->bpix);
}
}
+
+ return 0;
}
-static void lmb_test_dump_region(struct unit_test_state *uts,
- struct lmb_region *rgn, char *name)
+static int lmb_test_dump_region(struct unit_test_state *uts,
+ struct lmb_region *rgn, char *name)
{
unsigned long long base, size, end;
enum lmb_flags flags;
@@ -105,13 +116,17 @@ static void lmb_test_dump_region(struct unit_test_state *uts,
ut_assert_nextline(" %s[%d]\t[0x%llx-0x%llx], 0x%08llx bytes flags: %x",
name, i, base, end, size, flags);
}
+
+ return 0;
}
-static void lmb_test_dump_all(struct unit_test_state *uts, struct lmb *lmb)
+static int lmb_test_dump_all(struct unit_test_state *uts, struct lmb *lmb)
{
ut_assert_nextline("lmb_dump_all:");
lmb_test_dump_region(uts, &lmb->memory, "memory");
lmb_test_dump_region(uts, &lmb->reserved, "reserved");
+
+ return 0;
}
static int bdinfo_test_move(struct unit_test_state *uts)
@@ -123,44 +138,48 @@ static int bdinfo_test_move(struct unit_test_state *uts)
ut_assertok(console_record_reset_enable());
ut_assertok(run_commandf("bdinfo"));
- bdinfo_test_num_l(uts, "boot_params", 0);
+ ut_assertok(test_num_l(uts, "boot_params", 0));
for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
if (bd->bi_dram[i].size) {
- bdinfo_test_num_l(uts, "DRAM bank", i);
- bdinfo_test_num_ll(uts, "-> start", bd->bi_dram[i].start);
- bdinfo_test_num_ll(uts, "-> size", bd->bi_dram[i].size);
+ ut_assertok(test_num_l(uts, "DRAM bank", i));
+ ut_assertok(test_num_ll(uts, "-> start",
+ bd->bi_dram[i].start));
+ ut_assertok(test_num_ll(uts, "-> size",
+ bd->bi_dram[i].size));
}
}
/* CONFIG_SYS_HAS_SRAM testing not supported */
- bdinfo_test_num_l(uts, "flashstart", 0);
- bdinfo_test_num_l(uts, "flashsize", 0);
- bdinfo_test_num_l(uts, "flashoffset", 0);
+ ut_assertok(test_num_l(uts, "flashstart", 0));
+ ut_assertok(test_num_l(uts, "flashsize", 0));
+ ut_assertok(test_num_l(uts, "flashoffset", 0));
ut_assert_nextline("baudrate = %lu bps",
env_get_ulong("baudrate", 10, 1234));
- bdinfo_test_num_l(uts, "relocaddr", gd->relocaddr);
- bdinfo_test_num_l(uts, "reloc off", gd->reloc_off);
+ ut_assertok(test_num_l(uts, "relocaddr", gd->relocaddr));
+ ut_assertok(test_num_l(uts, "reloc off", gd->reloc_off));
ut_assert_nextline("%-12s= %u-bit", "Build", (uint)sizeof(void *) * 8);
if (IS_ENABLED(CONFIG_CMD_NET))
- test_eth(uts);
+ ut_assertok(test_eth(uts));
/*
* Make sure environment variable "fdtcontroladdr" address
* matches mapped control DT address.
*/
ut_assert(map_to_sysmem(gd->fdt_blob) == env_get_hex("fdtcontroladdr", 0x1234));
- bdinfo_test_num_l(uts, "fdt_blob", (ulong)map_to_sysmem(gd->fdt_blob));
- bdinfo_test_num_l(uts, "new_fdt", (ulong)map_to_sysmem(gd->new_fdt));
- bdinfo_test_num_l(uts, "fdt_size", (ulong)gd->fdt_size);
+ ut_assertok(test_num_l(uts, "fdt_blob",
+ (ulong)map_to_sysmem(gd->fdt_blob)));
+ ut_assertok(test_num_l(uts, "new_fdt",
+ (ulong)map_to_sysmem(gd->new_fdt)));
+ ut_assertok(test_num_l(uts, "fdt_size", (ulong)gd->fdt_size));
if (IS_ENABLED(CONFIG_VIDEO))
test_video_info(uts);
/* The gd->multi_dtb_fit may not be available, hence, #if below. */
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
- bdinfo_test_num_l(uts, "multi_dtb_fit", (ulong)gd->multi_dtb_fit);
+ ut_assertok(test_num_l(uts, "multi_dtb_fit", (ulong)gd->multi_dtb_fit));
#endif
if (IS_ENABLED(CONFIG_LMB) && gd->fdt_blob) {
diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c
index 473a8cef57..6fbebc7da0 100644
--- a/test/dm/ofnode.c
+++ b/test/dm/ofnode.c
@@ -1240,3 +1240,48 @@ static int dm_test_ofnode_copy_props_ot(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_ofnode_copy_props_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* check that the livetree is aligned to a structure boundary */
+static int dm_test_livetree_align(struct unit_test_state *uts)
+{
+ const int align = __alignof__(struct unit_test_state);
+ struct device_node *node;
+ u32 *sentinel;
+ ulong start;
+
+ start = (ulong)gd_of_root();
+ ut_asserteq(start, ALIGN(start, align));
+
+ node = gd_of_root();
+ sentinel = (void *)node - sizeof(u32);
+
+ /*
+ * The sentinel should be overwritten with the root node. If it isn't,
+ * then the root node is not at the very start of the livetree memory
+ * area, and free(root) will fail to free the memory used by the
+ * livetree.
+ */
+ ut_assert(*sentinel != BAD_OF_ROOT);
+
+ return 0;
+}
+DM_TEST(dm_test_livetree_align, UT_TESTF_LIVE_TREE);
+
+/* check that it is possible to load an arbitrary livetree */
+static int dm_test_livetree_ensure(struct unit_test_state *uts)
+{
+ oftree tree;
+ ofnode node;
+
+ /* read from other.dtb */
+ ut_assertok(test_load_other_fdt(uts));
+ tree = oftree_from_fdt(uts->other_fdt);
+ ut_assert(oftree_valid(tree));
+ node = oftree_path(tree, "/node/subnode");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("sandbox-other2",
+ ofnode_read_string(node, "compatible"));
+
+ return 0;
+}
+DM_TEST(dm_test_livetree_ensure, 0);
diff --git a/test/dm/video.c b/test/dm/video.c
index 30778157d9..0534ee93a3 100644
--- a/test/dm/video.c
+++ b/test/dm/video.c
@@ -556,7 +556,7 @@ static int dm_test_video_truetype(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
- ut_asserteq(12237, compress_frame_buffer(uts, dev));
+ ut_asserteq(12174, compress_frame_buffer(uts, dev));
return 0;
}
@@ -577,7 +577,7 @@ static int dm_test_video_truetype_scroll(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
- ut_asserteq(35030, compress_frame_buffer(uts, dev));
+ ut_asserteq(34287, compress_frame_buffer(uts, dev));
return 0;
}
@@ -598,7 +598,7 @@ static int dm_test_video_truetype_bs(struct unit_test_state *uts)
ut_assertok(video_get_nologo(uts, &dev));
ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
vidconsole_put_string(con, test_string);
- ut_asserteq(29018, compress_frame_buffer(uts, dev));
+ ut_asserteq(29471, compress_frame_buffer(uts, dev));
return 0;
}
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index 0b45863b43..aa1d477cd5 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -282,6 +282,15 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
copy_prepared_image(cons, mmc_dev, fname)
+def setup_cedit_file(cons):
+ infname = os.path.join(cons.config.source_dir,
+ 'test/boot/files/expo_layout.dts')
+ expo_tool = os.path.join(cons.config.source_dir, 'tools/expo.py')
+ outfname = 'cedit.dtb'
+ u_boot_utils.run_and_log(
+ cons, f'{expo_tool} -e {infname} -l {infname} -o {outfname}')
+
+
@pytest.mark.buildconfigspec('ut_dm')
def test_ut_dm_init(u_boot_console):
"""Initialize data for ut dm tests."""
@@ -319,6 +328,7 @@ def test_ut_dm_init_bootstd(u_boot_console):
setup_bootflow_image(u_boot_console)
setup_bootmenu_image(u_boot_console)
+ setup_cedit_file(u_boot_console)
# Restart so that the new mmc1.img is picked up
u_boot_console.restart_uboot()
diff --git a/tools/binman/control.py b/tools/binman/control.py
index 68597c4e77..7e2dd3541b 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -9,7 +9,7 @@ from collections import OrderedDict
import glob
try:
import importlib.resources
-except ImportError:
+except ImportError: # pragma: no cover
# for Python 3.6
import importlib_resources
import os
diff --git a/tools/expo.py b/tools/expo.py
new file mode 100755
index 0000000000..c6eb87aec7
--- /dev/null
+++ b/tools/expo.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0+
+
+"""
+Expo utility - used for testing of expo features
+
+Copyright 2023 Google LLC
+Written by Simon Glass <sjg@chromium.org>
+"""
+
+import argparse
+import collections
+import io
+import re
+import subprocess
+import sys
+
+#from u_boot_pylib import cros_subprocess
+from u_boot_pylib import tools
+
+# Parse:
+# SCENE1 = 7,
+# or SCENE2,
+RE_ENUM = re.compile(r'(\S*)(\s*= (\d))?,')
+
+# Parse #define <name> "string"
+RE_DEF = re.compile(r'#define (\S*)\s*"(.*)"')
+
+def calc_ids(fname):
+ """Figure out the value of the enums in a C file
+
+ Args:
+ fname (str): Filename to parse
+
+ Returns:
+ OrderedDict():
+ key (str): enum name
+ value (int or str):
+ Value of enum, if int
+ Value of #define, if string
+ """
+ vals = collections.OrderedDict()
+ with open(fname, 'r', encoding='utf-8') as inf:
+ in_enum = False
+ cur_id = 0
+ for line in inf.readlines():
+ line = line.strip()
+ if line == 'enum {':
+ in_enum = True
+ continue
+ if in_enum and line == '};':
+ in_enum = False
+
+ if in_enum:
+ if not line or line.startswith('/*'):
+ continue
+ m_enum = RE_ENUM.match(line)
+ if m_enum.group(3):
+ cur_id = int(m_enum.group(3))
+ vals[m_enum.group(1)] = cur_id
+ cur_id += 1
+ else:
+ m_def = RE_DEF.match(line)
+ if m_def:
+ vals[m_def.group(1)] = tools.to_bytes(m_def.group(2))
+
+ return vals
+
+
+def run_expo(args):
+ """Run the expo program"""
+ ids = calc_ids(args.enum_fname)
+
+ indata = tools.read_file(args.layout)
+
+ outf = io.BytesIO()
+
+ for name, val in ids.items():
+ if isinstance(val, int):
+ outval = b'%d' % val
+ else:
+ outval = b'"%s"' % val
+ find_str = r'\b%s\b' % name
+ indata = re.sub(tools.to_bytes(find_str), outval, indata)
+
+ outf.write(indata)
+ data = outf.getvalue()
+
+ with open('/tmp/asc', 'wb') as outf:
+ outf.write(data)
+ proc = subprocess.run('dtc', input=data, capture_output=True, check=True)
+ edtb = proc.stdout
+ if proc.stderr:
+ print(proc.stderr)
+ return 1
+ tools.write_file(args.outfile, edtb)
+ return 0
+
+
+def parse_args(argv):
+ """Parse the command-line arguments
+
+ Args:
+ argv (list of str): List of string arguments
+
+ Returns:
+ tuple: (options, args) with the command-line options and arugments.
+ options provides access to the options (e.g. option.debug)
+ args is a list of string arguments
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-e', '--enum-fname', type=str,
+ help='C file containing enum declaration for expo items')
+ parser.add_argument('-l', '--layout', type=str,
+ help='Devicetree file source .dts for expo layout')
+ parser.add_argument('-o', '--outfile', type=str,
+ help='Filename to write expo layout dtb')
+
+ return parser.parse_args(argv)
+
+def start_expo():
+ """Start the expo program"""
+ args = parse_args(sys.argv[1:])
+
+ ret_code = run_expo(args)
+ sys.exit(ret_code)
+
+
+if __name__ == "__main__":
+ start_expo()