LVGL库入门教程05-颜色和图像

颜色

构造颜色

在 LVGL 中,颜色以结构 lv_color_t 表示。在最开始移植整个工程时,曾经在 lv_conf.h 中修改过颜色深度:

/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 32

LVGL 会自动根据所选的颜色深度创建合适的颜色结构。在接下来几处位置还有几个与颜色有关的配置选项,可以参照注释修改。

例如,16 位 big-endian 的颜色定义为:

typedef union {
    struct {
        uint16_t blue  : 5;
        uint16_t green : 6;
        uint16_t red   : 5;
    } ch;
    uint16_t full;
} lv_color16_t;
typedef lv_color16_t lv_color_t;

那么就可以根据该结构创建合适的颜色值了:

lv_color_t orange = {
    .ch = {
        .red   = 0b11111,
        .green = 0b101001,
        .blue  = 0
    }
};

直接创建 RGB565 的颜色格式有点难以调色,不过可以借用以下函数从十六位颜色中生成合适的颜色值:

lv_color_t orange = lv_color_make(0xFF, 0xA5, 0);  // 从颜色通道创建
lv_color_t aqua = lv_color_hex(0x00FFFF);          // 从十六进制创建
lv_color_t lightgrey = lv_color_hex3(0xddd);       // 从十六进制简写创建

这些颜色在创建时,每种颜色通道的值都使用 0~255 表示即可,创建过程中会自动转换为合适的颜色值。

LVGL 还提供了 HSV 格式的颜色支持,

lv_color_t red = lv_color_hsv_to_rgb(0, 100, 100);   // 从 HSV 颜色空间创建颜色
lv_color_hsv_t blue = lv_color_rgb_to_hsv(r, g, b);  // 将 RGB 颜色转换为 HSV 颜色

除此之外,lv_color_t 、RGB 颜色、HSV 颜色之间也能互相转换。


如果觉得 16 进制的颜色还是不够直观,还可以使用调色板功能。LVGL 提供了常用颜色的色值表示,可以直接使用、微调、混合这些颜色。

例如,以下直接调出了一个紫色:

lv_color_t purple = lv_palette_main(LV_PALETTE_PURPLE)

如果觉得默认的紫色太深或太浅的话,还可以在调色板中更改亮度:

lv_color_t dark_purple = lv_palette_darken(LV_PALETTE_PURPLE, 2)  // 调深两级,最多可以调深或浅 4 级
lv_color_t light_purple = lv_color_lighten(purple, 60);  // 调浅一些,调到 255 就变成纯白

甚至还可以将两种颜色混合:

lv_color_t orange = lv_color_mix(red, yellow, 156);

比例的取值为 0~255 ,例如设定为 0 就是全红,128 就是红黄各占一半等。

可以将一个颜色类型直接应用到以下样式属性中:

属性名 含义
bg_color 背景颜色
border_color 边框颜色
outline_color 轮廓颜色
shadow_color 阴影颜色
text_color 文本颜色

以及上一节提到的直线和弧线颜色。

透明度

有时候两个控件间可能发生重叠,这个时候就可以给它们设置一个透明度。

透明度使用类型 lv_opa_t 表示,LVGL 预定义了几个表示透明度的宏:LV_OPA_TRANSP 表示完全透明,LV_OPA_COVER 表示完全不透明,其余的 LV_OPA_10 ~ LV_OPA_90 整十表示的透明度依次递减。

可以将透明度应用到以下样式属性中:

属性名 含义
bg_opa 背景透明度
border_opa 边框透明度
outline_opa 轮廓透明度
shadow_opa 阴影透明度
text_opa 文本透明度
opa 整体透明度

以及直线和弧线透明度。例如,以下创建了两个部分重叠的控件,并在一个的背景上加透明度:

static lv_style_t style_grass;
lv_style_init(&style_grass);
lv_style_set_opa(&style_grass, LV_OPA_30);
lv_obj_t* obj = lv_obj_create(lv_scr_act());
lv_obj_t* cover = lv_obj_create(lv_scr_act());
lv_obj_add_style(cover, &style_grass, 0);

这样就可以看见被遮挡的控件了:

image

注意需要给上层,即后创建的的控件加透明度才会有这样的效果。透明度其实就是为控件重新调色,因此不是 32 位颜色的屏幕也可以使用透明度。

lv_opa_t 类型的本质就是 8 位无符号整数,因此可以自行创建一个透明度数值,设为 255 就代表完全透明;还可以将透明度应用到 lv_color_mix() 的第三个参数上。

渐变色

可以使用渐变色给控件加上更美观的效果。

只有背景颜色能设置渐变色。一个渐变色的效果由以下几个属性支配:

属性名 含义
bg_color 主要颜色
bg_grad_color 渐变颜色
bg_grad_dir 渐变方向
bg_main_stop 渐变开始位置
bg_grad_stop 渐变结束位置
bg_dither_mode 渲染模式

当确定了渐变方向后,渐变从 bg_main_stop 位置开始,由 bg_color 过度到 bg_grad_color ,在 bg_grad_stop 位置结束。这里的位置是由比例衡量的,渐变区域在每个方向都被划分为 256 份,例如 128 代表中间位置,255 代表结束位置等。

例如,以下代码:

lv_obj_t* obj01 = lv_obj_create(lv_scr_act());
lv_obj_set_style_bg_color(obj01, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_obj_set_style_bg_grad_color(obj01, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_set_style_bg_grad_dir(obj01, LV_GRAD_DIR_HOR, 0);

渐变效果为水平方向从蓝色一直渐变到红色:

image

再如,以下代码:

lv_obj_t* obj02 = lv_obj_create(lv_scr_act());
lv_obj_set_style_bg_color(obj02, lv_palette_main(LV_PALETTE_GREEN), 0);
lv_obj_set_style_bg_grad_color(obj02, lv_palette_main(LV_PALETTE_PURPLE), 0);
lv_obj_set_style_bg_grad_stop(obj02, 128, 0);
lv_obj_set_style_bg_grad_dir(obj02, LV_GRAD_DIR_VER, 0);

渐变效果为竖直方向从绿色一直渐变到紫色,但实际渐变区域只有上半部分:

image

还可以使用简写属性 bg_grad 设置完整的渐变属性。这种情况下,渐变使用结构 lv_grad_dsc_t 描述:

typedef struct {
    lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS];
    uint8_t            stops_count;
    lv_grad_dir_t      dir    : 3;
    lv_dither_mode_t   dither : 3;
} lv_grad_dsc_t;

LV_GRADIENT_MAX_STOPS 决定了最大拥有的渐变颜色数,可以在 lv_conf_internal.h 大约 377 行修改该宏的数量:

#ifndef LV_GRADIENT_MAX_STOPS
    #ifdef CONFIG_LV_GRADIENT_MAX_STOPS
        #define LV_GRADIENT_MAX_STOPS CONFIG_LV_GRADIENT_MAX_STOPS
    #else
        #define LV_GRADIENT_MAX_STOPS 3
    #endif
#endif

然后就可以自定义多种颜色的渐变了:

static lv_grad_dsc_t grad_sunset;
grad_sunset.stops[0] = (lv_gradient_stop_t){ .color = lv_palette_main(LV_PALETTE_RED), .frac = 96 };
grad_sunset.stops[1] = (lv_gradient_stop_t){ .color = lv_palette_main(LV_PALETTE_ORANGE), .frac = 128 };
grad_sunset.stops[2] = (lv_gradient_stop_t){ .color = lv_palette_main(LV_PALETTE_BLUE), .frac = 216 };
grad_sunset.stops_count = 3;
grad_sunset.dir = LV_GRAD_DIR_VER;
lv_obj_t* obj03 = lv_obj_create(lv_scr_act());
lv_obj_set_style_bg_grad(obj03, &grad_sunset, 0);

效果为:

image

颜色的其它内容

LVGL 还提供了许多处理颜色的滤镜。可以使用样式属性 blend_mode 设置颜色和背景色的融合。例如,以下将控件的颜色设置为背景色的反色:

lv_obj_set_style_blend_mode(obj03, LV_BLEND_MODE_SUBTRACTIVE, 0);

效果为:

image

注意边框的颜色也变成反色了。

最后,LVGL 中还要一个控件 color wheel ,可以快速创建一个颜色选择器。它的默认表现形式为:

image

它类似于圆弧,并可以通过长按切换模式。可以使用函数 lv_colorwheel_get_rgb() 获取当前选择的颜色。

图片

创建图片

图片可以以两种方式存储:一是作为一个数组之类的变量,二是通过二进制文件的形式存储。由于还没有介绍文件相关的内容,这里仅介绍使用数组的方式来存储并使用图片。

LVGL 已经提供了在线图片转换器,可以直接在 https://lvgl.io/tools/imageconverter 将一般的 PNG 或 JPG 图片转换为符合要求的 C 语言对象:

image

注意转换完成后得到的是一个完整的源文件,文件名同时也是图片的变量名。以上唯一值的注意的一点是图片所用的颜色格式,一般来说颜色格式可以分为以下几类:

  1. True color :自动适配当前项目使用的颜色深度
  2. Indexed :从调色板创建较少的颜色数目
  3. Alpha only :单色图像,只使用透明度
  4. Raw :使用图像原本的颜色格式

最后一个 RBG565-A8 就不必多说了。值的注意的是,以上有一种叫“Chroma key” 的颜色格式,它对应 lv_conf.h 的第 42 行的配置,注释是这样说的:

/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00)         /*pure green*/

更多有关于此的介绍可以阅读维基百科 https://en.wikipedia.org/wiki/Chroma_key

转换完成后,将得到的源文件添加到当前工程内,然后通过以下几行代码就可以显示该图像:

LV_IMG_DECLARE(lvgl_logo);
lv_obj_t* img01 = lv_img_create(lv_scr_act());
lv_img_set_src(img01, &lvgl_logo);

这里第一个宏的作用本质就是一个 extern 语句。显示的效果为:

image

注意这里在模拟器上创建的图片是具有透明度的。

图片的属性

像直线和圆弧一样,图片对象也是有特殊的属性的,不过比较少:

属性 简介
img_opa 图片透明度
img_recolor 可以给图片加上一层颜色滤镜
img_recolor_opa 这层滤镜的透明度

默认情况下,图片控件会自动调整宽度以适应图片大小。如果控件过小,那么图片的额外部分会被去除;如果控件过大,那么图片会像地砖一样重复铺开来填补剩下的区域。

可以通过 lv_img_set_offset_x(img, x_ofs) 与 y 轴对应的函数给图片设置一个偏移量来修改显示范围。例如,可以通过偏移量结合控件宽度来裁剪图片:

lv_img_set_offset_x(img01, -2);
lv_img_set_offset_y(img01, -7);
lv_obj_set_size(img01, 74, 74);

这里通过负值来将图片向左上角偏移,从而框选出合适的区域:

image

图片按钮

最后再介绍一个内容,可以通过图片来创建一个按钮。这种情况下,需要准备三张图片,分别描述按钮的左边、中间和右边。

例如,以下准备图片如下:

imageimageimage

由于标签的宽度是不确定的,因此中间的图片必须是水平可平铺的。将其转换为对应的图片格式后,可以通过以下代码创建一个图片按钮:

lv_obj_t* imgbtn = lv_imgbtn_create(lv_scr_act());
lv_imgbtn_set_src(imgbtn, LV_IMGBTN_STATE_RELEASED, &imgbtn_left, &imgbtn_mid, &imgbtn_right);
lv_obj_t* label = lv_label_create(imgbtn);
lv_label_set_text(label, "Image Button");
lv_obj_set_style_img_recolor_opa(imgbtn, LV_OPA_30, LV_STATE_PRESSED);
lv_obj_set_style_img_recolor(imgbtn, lv_color_black(), LV_STATE_PRESSED);

注意在创建的过程中,将以上图片应用到按钮的普通状态(即什么事件都没有的状态)的外观中。这里通过给点击事件加上一层深色的滤镜使点击时外观可以发生改变:

image

这样按钮就可以变得很花哨了。

以上对于图片的介绍比较简单,不过也基本足以应付一般的使用场景了。更多细节可以参考官方文档。

参考资料/延伸阅读

https://docs.lvgl.io/master/overview/color.html

颜色参考文档

https://docs.lvgl.io/master/overview/image.html
https://docs.lvgl.io/master/widgets/core/img.html

有关图片及图片控件的完整使用描述

京ICP备2021034974号
contact me by hello@frozencandles.fun