移植轻量级通用型图形库到OpenHarmony
本文正在参加星光计划3.0–夏日挑战赛
一、项目简介
1.1 littleVGL 主要特性如下
- 具有非常丰富的内置控件,像 buttons, charts, lists, sliders, images 等
- 高级图形效果:动画,反锯齿,透明度,平滑滚动
- 支持多种输入设备,像 touchpad, mouse, keyboard, encoder 等
- 支持多语言的 UTF-8 编码 • 支持多个和多种显示设备,例如同步显示在多个彩色屏或单色屏上
- 完全自定制的图形元素
- 硬件独立于任何微控制器或显示器
- 可以缩小到最小内存 (64 kB Flash, 16 kB RAM)
- 支持操作系统、外部储存和 GPU(非必须)
- 仅仅单个帧缓冲设备就可以呈现高级视觉特效
- 使用 C 编写以获得最大兼容性(兼容 C++)
- 支持 PC 模拟器
- 为加速 GUI 设计,提供教程,案例和主题,支持响应式布局
- 提供了在线和离线文档
- 基于自由和开源的 MIT 协议
1.2 littleVGL 的要求如下:
- 16、32 或 64 位的单片机(微控制器)或处理器
- 微处理器的主频最好高于 16MHZ
- Flash/ROM:如果只用 littleVGL 核心组件的话,则至少需要 64kB 的容量,如果想完整使用的 话,最好保证 180kB 以上的容量
- RAM:
- 静态 RAM: 大约 8 到 16 kB,这取决于你所用的组件功能和 objects 控件对象类型
- 栈: 至少为 2Kb,一般推荐值为 4kBo 动态数据(堆): 至少 4kB,如果你用到了多个或多种控件的话,那么最好设置为 16kB 以 上,这个是可以通过 lv_conf.h 配置文件中的 LV_MEM_SIZE 宏来定义的
- 显示缓冲区: 至少要比”水平分辨率像素”要大,一般推介值为 10 倍的”水平分辨率像 素”,取个例子,假如我们屏幕的水平分辨率为480个像素,采用16位的颜色深度进行显 示,即一个像素占 2 个字节,那么推介的显示缓冲区大小为 104802=9600 个字节
- C99 或更新的编译器,如果是用 keil 开发的话,一定得勾选”c99”模式,否则编译会报错的
- 基本的 c(或者 c++)语言知识,如:指针,结构体,回调函数
1.3 开源项目链接
- littleVGL 的官方网址为: https://littlevgl.com
- littleVGL 的 github 网址为: https://github.com/littlevgl/lvgl
- littleVGL 的在线文档网址为: https://docs.littlevgl.com/zh-CN/html/index.html
二、 littleVGL 移植
2.1 开发前准备
- 本次分享硬件采用的是HI3861开发板和SPI驱动的TFT显示屏
- 搭建环境请参考上一篇文章
- 软件包已上传附件,感兴趣可下载体验移植的乐趣
2.2 项目创建
2.2.1 在applications/sample/wifi-iot/app目录下创建一个GUI文件夹
GUI 目录是用来存放跟 littleVGL 库相关的所有文件的
2.2.2 解压lv_pc_simulator.zip包
接着把 lv_pc_simulator.zip 压缩包里面的 lv_examples.zip 和 lvgl.zip 俩个子压缩包直接拷 贝到GUI 目录下,拷贝完成之后,接着分别对lv_examples.zip 和 lvgl.zip 俩个子压缩包 在当前目录下进行解压缩操作,解压缩完成后,可以把 lv_examples.zip 和 lvgl.zip 都删除了
2.2.3 拷贝配置模板文件
接 着把 GUI/lvgl/lv_conf_template.h 和 GUI/lv_examples/lv_ex_conf_templ.h 俩 个配置模板文件统统拷贝到 GUI 目录下,然后对这个 2 文件分别重命名为 lv_conf.h和 lv_ex_conf.h
2.2.4 新建lvgl_driver子目录
接着还要在 GUI 目录下新建一个 lvgl_driver子目录,这个目录是用来 放底层显示驱动和触摸驱动文件的,最后 GUI 的目录结构如下图所示
注:在实际项目中,可以删除掉 lv_examples 目录来减少项目所占的磁盘空间,因为此目录就是 专门用来存放官方的演示 demo,对我们的实际项目没有任何作用
2.3 编写BUILD.gn文件
在GUI目录下添加BUILD.gn文件
group("GUI") {
deps = [
"lvgl:lvgl",
"lvgl_driver:lvgl_port",
]
}
在GUI/lvgl_driver目录下添加BUILD.gn文件
static_library("lvgl_port") {
sources = [
"lv_port_disp.c",
]
include_dirs = [
"//base/iot_hardware/peripheral/interfaces/kits",
"../../",
"../lvgl_driver",
]
}
在GUI/lv_examples目录下添加BUILD.gn文件
static_library("lv_examples") {
sources = [
# "src/lv_demo_benchmark/lv_demo_benchmark.c",
# "src/lv_demo_keypad_encoder/lv_demo_keypad_encoder.c",
# "src/lv_demo_music/lv_demo_music.c",
# "src/lv_demo_printer/lv_demo_printer.c",
# "src/lv_demo_stress/lv_demo_stress.c",
"src/lv_demo_widgets/lv_demo_widgets.c",
# "src/lv_ex_get_started/lv_ex_get_started.c",
# "src/lv_demo_widgets/lv_demo_widgets.c",
]
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
include_dirs = [
"src/lv_demo_widgets",
]
}
在GUI/lvgl/BUILD.gn目录下添加BUILD.gn文件
```c++
static_library("lvgl") {
sources = [
"src/lv_core/lv_disp.c",
"src/lv_core/lv_group.c",
"src/lv_core/lv_indev.c",
"src/lv_core/lv_obj.c",
"src/lv_core/lv_refr.c",
"src/lv_core/lv_style.c",
"src/lv_draw/lv_draw_basic.c",
"src/lv_draw/lv_draw.c",
"src/lv_draw/lv_draw_rect.c",
"src/lv_draw/lv_draw_label.c",
"src/lv_draw/lv_draw_line.c",
"src/lv_draw/lv_draw_img.c",
"src/lv_draw/lv_draw_arc.c",
"src/lv_draw/lv_draw_triangle.c",
"src/lv_draw/lv_img_decoder.c",
"src/lv_draw/lv_img_cache.c",
"src/lv_font/lv_font.c",
"src/lv_font/lv_font_fmt_txt.c",
"src/lv_font/lv_font_roboto_12.c",
"src/lv_font/lv_font_roboto_16.c",
"src/lv_font/lv_font_roboto_22.c",
"src/lv_font/lv_font_roboto_28.c",
"src/lv_font/lv_font_unscii_8.c",
"src/lv_hal/lv_hal_disp.c",
"src/lv_hal/lv_hal_indev.c",
"src/lv_hal/lv_hal_tick.c",
"src/lv_misc/lv_circ.c",
"src/lv_misc/lv_area.c",
"src/lv_misc/lv_task.c",
"src/lv_misc/lv_fs.c",
"src/lv_misc/lv_anim.c",
"src/lv_misc/lv_mem.c",
"src/lv_misc/lv_ll.c",
"src/lv_misc/lv_color.c",
"src/lv_misc/lv_txt.c",
"src/lv_misc/lv_math.c",
"src/lv_misc/lv_log.c",
"src/lv_misc/lv_gc.c",
"src/lv_misc/lv_utils.c",
"src/lv_misc/lv_async.c",
"src/lv_themes/lv_theme_alien.c",
"src/lv_themes/lv_theme.c",
"src/lv_themes/lv_theme_default.c",
"src/lv_themes/lv_theme_night.c",
"src/lv_themes/lv_theme_templ.c",
"src/lv_themes/lv_theme_zen.c",
"src/lv_themes/lv_theme_material.c",
"src/lv_themes/lv_theme_nemo.c",
"src/lv_themes/lv_theme_mono.c",
"src/lv_objx/lv_arc.c",
"src/lv_objx/lv_bar.c",
"src/lv_objx/lv_cb.c",
"src/lv_objx/lv_ddlist.c",
"src/lv_objx/lv_kb.c",
"src/lv_objx/lv_line.c",
"src/lv_objx/lv_mbox.c",
"src/lv_objx/lv_preload.c",
"src/lv_objx/lv_roller.c",
"src/lv_objx/lv_table.c",
"src/lv_objx/lv_tabview.c",
"src/lv_objx/lv_tileview.c",
"src/lv_objx/lv_btn.c",
"src/lv_objx/lv_calendar.c",
"src/lv_objx/lv_chart.c",
"src/lv_objx/lv_canvas.c",
"src/lv_objx/lv_gauge.c",
"src/lv_objx/lv_label.c",
"src/lv_objx/lv_list.c",
"src/lv_objx/lv_slider.c",
"src/lv_objx/lv_ta.c",
"src/lv_objx/lv_spinbox.c",
"src/lv_objx/lv_btnm.c",
"src/lv_objx/lv_cont.c",
"src/lv_objx/lv_img.c",
"src/lv_objx/lv_imgbtn.c",
"src/lv_objx/lv_led.c",
"src/lv_objx/lv_lmeter.c",
"src/lv_objx/lv_page.c",
"src/lv_objx/lv_sw.c",
"src/lv_objx/lv_win.c",
]
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
include_dirs = [
"lvgl",
]
}
# 移植littleVGL库到OpenHarmony过程分享
### 一、宏定义文件修改——修改 lv_conf.h 和 lv_ex_conf.h 配置文件
先打开 GUI 下的 lv_conf.h 文件,里面的配置项还是很多的,大家先不要纠结每个 配置项到底什么意思,后面我会专门来讲解的

#### 1.1 修改液晶屏分辨率
打开文件之后,第一个#if 后面的 0 改为 1,使整个文件生效,接着修改 LV_HOR_RES_MAX 和 LV_HOR_RES_MAX 宏的值,这个是告诉 littleVGL 你所用的液晶屏 分辨率是多少,请根据自己手头液晶屏的实际分辨率大小相应设置,比如我购买的是240*240,,因此 我这里就设置如下:
```c
#define LV_HOR_RES_MAX (240)
#define LV_VER_RES_MAX (240)
1.2 修改颜色深度
接着我们修改 LV_COLOR_DEPTH 颜色深度,最常见的设置就是 1 或者 16 了,1 是用于单色 屏,而 16 是用于彩色屏,这里我们设置成 16 即可,即如下所示
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB233
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 16
1.3 设置 界面缩放比例
再接着我们来设置 LV_DPI 的值,默认值为 100,我们不改变它,这个宏是用来调节界面缩 放比例的,此值越大,控件分布的就越散,控件自身的间隔也会变大
```c++
#define LV_DPI 100
#### 1.4 修改动态分配空间大小
再接着修改 LV_MEM_SIZE 的大小,这个就是控制 littleVGL 中所谓的动态数据堆的大小,是 用来给控件的创建动态分配空间的,我们这里设置为 32KB 的大小
```c
# define LV_MEM_SIZE (32U * 1024U)
1.5 修改 GPU使能情况
再接着修改 LV_USE_GPU 的值,默认值是 1,我们把它设置为 0,即不使能 GPU 功能
#define LV_USE_GPU 0
1.6 设置主题
再接着再把 LV_THEME_LIVE_UPDATE, LV_USE_THEME_TEMPL, LV_USE_THEME_DEFAULT, LV_USE_THEME_ALIEN, LV_USE_THEME_NIGHT, LV_USE_THEME_MONO, LV_USE_THEME_MATERIAL, LV_USE_THEME_ZEN, LV_USE_THEME_NEMO 等所有宏 的值都设置为0,即全部不使能,这些宏都是跟littleVGL自带的主题相关的,因为后面我们要自己写一个,所以这里我们先全部不使能
#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
#define LV_USE_THEME_TEMPL 0 /*Just for test*/
#define LV_USE_THEME_DEFAULT 0 /*Built mainly from the built-in styles. Consumes very few RAM*/
#define LV_USE_THEME_ALIEN 0 /*Dark futuristic theme*/
#define LV_USE_THEME_NIGHT 0 /*Dark elegant theme*/
#define LV_USE_THEME_MONO 0 /*Mono color theme for monochrome displays*/
#define LV_USE_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/
#define LV_USE_THEME_ZEN 0 /*Peaceful, mainly light theme */
#define LV_USE_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/
注意,在实际项目中,我们一般最多使能一个, 如果我们项目根本就用不到其自带的主题,那么我们应该把这些宏全部禁止,因为这样可以节 省 flash 和 ram。
1.7 修改 lv_ex_conf.h 文件
至此 lv_conf.h 文件修改就完成了,接下来我们要修改 lv_ex_conf.h 文件,这个文件就简单 很多了,而且这个文件也不是很重要,只有当我们要演示官方自带的例程时,才会用到,下面就 简单做一下说明.
把 LV_EX_KEYBOARD, LV_EX_MOUSEWHEEL, LV_USE_TESTS, LV_USE_TUTORIALS, LV_USE_BENCHMARK, LV_USE_DEMO, LV_USE_TERMINAL 等宏的值全部设置为 0,其 他宏保持默认即可。
#define LV_EX_PRINTF 0 /*Enable printf-ing data in demoes and examples*/
#define LV_EX_KEYBOARD 0 /*Add PC keyboard support to some examples (`lv_drivers` repository is required)*/
#define LV_EX_MOUSEWHEEL 0 /*Add 'encoder' (mouse wheel) support to some examples (`lv_drivers` repository is required)*/
/*Show some widget*/
#define LV_USE_DEMO_WIDGETS 1
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Printer demo, optimized for 800x480*/
#define LV_USE_DEMO_PRINTER 0
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
/*Music player for LVGL*/
#define LV_USE_DEMO_MUSIC 0
#if LV_USE_DEMO_MUSIC
#define LV_DEMO_MUSIC_AUTO_PLAY 0
二、创建OpenHarmony字体
2.1 littleVGL 字体介绍
在 littleVGL 中的字体功能也是非常强大的,支持最高 8bpp(在老版本中存在)的抗锯齿, 另外还有 1bpp,2bpp,4bpp 三个值可选,实现灵活性配置,选择越大的 bpp 值时,要求的 flash 存 储资源也是成倍的增加的,比如 4bpp 的存储消耗是 1bpp 存储消耗的 4 倍,除了存储消耗变大 之外,bpp 值越大,在界面上进行字体渲染时,绘制速度也会越慢,当然了,bpp 值越大,也是有好 处的,那就是绘制出来的字体边缘越平滑,没有毛刺,使我们产品的 UI 界面看上去更高大上,所 以在实际项目中,我们应该根据硬件平台的实际性能来选出一个比较合适的 bpp 值,一般的项 目都是默认采用 4bpp 就可以了。
2.2 使用字体
不论是使用 littleVGL 系统自带的字体,还是使用我们自己创建的字体,他们的使用方法 都是一样的,为了大家更容易接受,我先给大家介绍怎么使用字体,后面我再给大家介绍怎么 创建字体并使用它,我们就先以系统自带的字体为例来讲解怎么使用它,littleVGL 内置了 5 个 字库,如下所示:
字库 |
---|
lv_font_roboto_16 |
lv_font_roboto_22 |
lv_font_roboto_28 |
lv_font_unscii_8 |
lv_font_roboto_12 |
2.2.1 字体类型介绍
其中的 lv_font_roboto_12, lv_font_roboto_16, lv_font_roboto_22,lv_font_roboto_28 都是 Roboto 字体,只不过是细分出了 4 个不同的字体大小,这四个字库全都是 4bpp 抗锯齿的,这四 个字库中每一个字库除了包含所有的可见英文字符外,还都包含了一套相同的图标字符,也就 是我们前面所说的图标字体,这套图标字符有一个自己的字体名为 FontAwesome ,然后 lv_font_unscii_8 是一种非常好的单色屏字体,是 1bpp 的,相当于无抗锯齿的功能,另外 lv_font_unscii_8 字库只包含英文字符,不包含上面所说的图标字符.
本次移植我用的是OpenHarmony开源的字体,所以都没有使能, 如果想使能其他的字体或者想设置默认的字体等,就必须得在 lv_conf.h 头文件中进行配置, 相关的配置宏如下:
#define LV_FONT_ROBOTO_12 0
#define LV_FONT_ROBOTO_16 0
#define LV_FONT_ROBOTO_22 0
#define LV_FONT_ROBOTO_28 0
/*Pixel perfect monospace font
* http://pelulamu.net/unscii/ */
#define LV_FONT_UNSCII_8 0
2.3 如何创建自己的字体
2.3.1 安装node.js
离线工具是用 node.js 开发出来的,所以我们需要先安装 node.js 的运行环境,安装 node.js,过程有点久,请耐心等待!
我这里给出一个网上的参考教程:Node.js 安装配置 | 菜鸟教程 (runoob.com)
2.3.2 安装离线工具包
在安装完 node.js(会附带把 npm 包管 理器给装好)之后,我们接下来要安装 lv_font_conv 离线转换工具了,操作很简单,先打开 cmd 命令窗口,然后直接输入 npm i lv_font_conv -g 命令,回车运行就会把 lv_font_conv 给安装 好
```c++
npm i lv_font_conv -g

##### 2.3.3 lv_font_conv 的主要命令行参数
**--bpp**: 抗锯齿大小,可选值为 1-4
**--size**: 字体的大小,实际就是指字符的高度
**-o(或者--output)**: 输出路径,比如为 C:\Users\han\Desktop\my_font.c
**--format**: 输出格式,可选值有 dump,bin,lvgl,我们只用 lvgl 就行了
**--font**: ttf/woff/woff2 字体文件的路径
**-r(或--range)**: 所需字符的 unicode 编码范围,可选值为单个字符,字符范围,加可选的映射 地址,首先你得必须保证你所需要的字符范围在--font 指定的字体文件中能找得到,-r 命令行 参数可以在一条命令中多次出现,如下面例子所示:
```c++
-r 0xF450 单个字符,十六进制和十进制都行
-r 0xF450-0xF470 字符范围
-r '0xF450=>0x81' 单个字符加映射地址
-r '0xF450-0xF470=>0x81' 字符范围加映射地址
-r 0xF450 -r 0xF451-0xF470 一下使用 2 次-r 命令行参数
-r 0xF450,0xF451-0xF470 用单个-r 表示多种可选值
--symbols: 和-r 的作用差不多,都是用来指示所要用到的字符,不过他们的表达形式不一样, 对--symbols 而言,它是接受字符的字面量形式,而-r 是接受字符的编码形式, --symbols 和-r 可以同时使用,也可以只使用它们其中的一个,请看如下例子:
```c++
--symbols 0123456789 中 ABCD 国 EFG
**--no-compress:** 禁止进行 RLE 压缩,我们在生成字体时,请禁止进行压缩
#### 2.3.4 字体下载
这里我们要下载一下Open Harmony的字体,附件已给出
下载链接:https://developer.harmonyos.com/cn/docs/design/des-resources/general-0000001157315901

下载后挑选自己喜欢的安装即可

#### 2.3.5 字体创建
下面给出一条完整的创建命令示例(复制到 cmd 命令窗口运行的):
lv_font_conv --no-compress --format lvgl --font C:\Users\han\Desktop\HarmonyOS_Sans_SC_Thin.ttf -o C:\Users\han\Desktop\my_font.c --bpp 4 --size 25 --symbols 我爱OpenHarmonyar -r 0x30-0x39 -r 0x41-0x5A -r 0x61-0x7A -r 0x95F4

##### 注意:
1.上面的文件路径要改成自己的
2.上面给出的代码可能会出现换行,请自行调整
生成的代码如下

将代码拉取到GUI文件目录下

#### 2.4 测试代码
##### 2.4.1 声明字体
```c++
LV_FONT_DECLARE(my_font);//申明字体
2.4.2 入口函数
```c++
void lv_font_test_start()
{
lv_obj_t scr = lv_scr_act();
lv_obj_t label1 = lv_label_create(scr,NULL);
static lv_style_t style1;
lv_style_copy(&style1,&lv_style_plain_color);
style1.text.font = &my_font;//在样式中使用这个字体
lv_label_set_style(label1,LV_LABEL_STYLE_MAIN,&style1);
lv_label_set_text(label1,"我爱OpenHarmony");//设置文本
lv_label_set_body_draw(label1,true);
lv_obj_align(label1,NULL,LV_ALIGN_CENTER,0,0);
}
#### 2.5 测试效果

附件链接:
lvgl-master.zip(https://ost.51cto.com/resource/2087)
lv_pc_simulator.zip(https://ost.51cto.com/resource/2086)
HarmonyOS Sans.zip(https://ost.51cto.com/resource/2090)
[想了解更多关于开源的内容,请访问:](https://ost.51cto.com/#bkwz)
[51CTO 开源基础软件社区](https://ost.51cto.com#bkwz)
https://ost.51cto.com/#bkwz