LVGL是一个可以适配不同颜色深度的gui,显示像素可以从1,8,16,32等都可以很好的支持。
其中有一个很关键的函数,转换r,g,b显示像素到对应的目标显示器的格式,使用这个函数来适配不同的目标设备。
在文件lv_color.h中有相关的定义,我简单的梳理说明分析一下,详细的大家可以参考该源文件:
该函数如下,采用了inline方式定义,编译时会展开和替代,加快执行速度。
static inline lv_color_t lv_color_make(uint8_t r, uint8_t g, uint8_t b)
{return _LV_COLOR_MAKE_TYPE_HELPER LV_COLOR_MAKE(r, g, b);
}
关于颜色通用类型 lv_color_t的定义,它是如何去自动适配1,8,16,32等不同颜色深度的呢?看看如下定义:
typedef union {uint8_t full; /*must be declared first to set all bits of byte via initializer list*/union {uint8_t blue : 1;uint8_t green : 1;uint8_t red : 1;} ch;
} lv_color1_t;typedef union {struct {uint8_t blue : 2;uint8_t green : 3;uint8_t red : 3;} ch;uint8_t full;
} lv_color8_t;typedef union {struct {
#if LV_COLOR_16_SWAP == 0uint16_t blue : 5;uint16_t green : 6;uint16_t red : 5;
#elseuint16_t green_h : 3;uint16_t red : 5;uint16_t blue : 5;uint16_t green_l : 3;
#endif} ch;uint16_t full;
} lv_color16_t;typedef union {struct {uint8_t blue;uint8_t green;uint8_t red;uint8_t alpha;} ch;uint32_t full;
} lv_color32_t;typedef LV_CONCAT3(uint, LV_COLOR_SIZE, _t) lv_color_int_t;
typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;
系统分别用lv_color1_t,lv_color8_t,lv_color16_t,lv_color32_t的union类型定义了不同的颜色深度,这也将是最终使用的变量类型。
然后通过下面两个宏的连接和转换,最终转换为上面的变量类型。
typedef LV_CONCAT3(uint, LV_COLOR_SIZE, _t) lv_color_int_t;
typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;
这个宏定义了目标系统的颜色深度:
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 32
其中两个重要的宏:
- _LV_COLOR_MAKE_TYPE_HELPER,定义如下:
#if _LV_COLOR_HAS_MODERN_CPP
/*Fix msvc compiler error C4576 inside C++ code*/
#define _LV_COLOR_MAKE_TYPE_HELPER lv_color_t
#else
#define _LV_COLOR_MAKE_TYPE_HELPER (lv_color_t)
#endif
为什么要用这个宏来定义呢?差异就在那一组括号,主要原因就是c和c++对类型转换的语法差异。避免出现编译时候出现错误:
warning :C4576 后跟初始值设定项列表的带圆括号类型是一个非标准的显式类型转换语法
- 另外一个宏:
#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8)
#define _LV_CONCAT(x, y) x ## y
#define LV_CONCAT(x, y) _LV_CONCAT(x, y)
//这里定义了一系列的32bit颜色操作宏
# define LV_COLOR_SET_R32(c, v) (c).ch.red = (uint8_t)((v) & 0xFF)
# define LV_COLOR_SET_G32(c, v) (c).ch.green = (uint8_t)((v) & 0xFF)
# define LV_COLOR_SET_B32(c, v) (c).ch.blue = (uint8_t)((v) & 0xFF)
# define LV_COLOR_SET_A32(c, v) (c).ch.alpha = (uint8_t)((v) & 0xFF)# define LV_COLOR_GET_R32(c) (c).ch.red
# define LV_COLOR_GET_G32(c) (c).ch.green
# define LV_COLOR_GET_B32(c) (c).ch.blue
# define LV_COLOR_GET_A32(c) (c).ch.alpha# define _LV_COLOR_ZERO_INITIALIZER32 {{0x00, 0x00, 0x00, 0x00}}
# define LV_COLOR_MAKE32(r8, g8, b8) {{b8, g8, r8, 0xff}} /*Fix 0xff alpha*/
//类似的也定义了很多其他颜色位数操作的宏
# define LV_COLOR_SET_R16(c, v) (c).ch.red = (uint8_t)((v) & 0x1FU)
#if LV_COLOR_16_SWAP == 0
# define LV_COLOR_SET_G16(c, v) (c).ch.green = (uint8_t)((v) & 0x3FU)
#else
# define LV_COLOR_SET_G16(c, v) {(c).ch.green_h = (uint8_t)(((v) >> 3) & 0x7); (c).ch.green_l = (uint8_t)((v) & 0x7);}
#endif
# define LV_COLOR_SET_B16(c, v) (c).ch.blue = (uint8_t)((v) & 0x1FU)
# define LV_COLOR_SET_A16(c, v) do {} while(0)# define LV_COLOR_GET_R16(c) (c).ch.red
#if LV_COLOR_16_SWAP == 0
# define LV_COLOR_GET_G16(c) (c).ch.green
#else
# define LV_COLOR_GET_G16(c) (((c).ch.green_h << 3) + (c).ch.green_l)
#endif
# define LV_COLOR_GET_B16(c) (c).ch.blue
# define LV_COLOR_GET_A16(c) 0xFF#if LV_COLOR_16_SWAP == 0
# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00}}
# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x3FU), (uint8_t)((r8 >> 3) & 0x1FU)}}
#else
# define _LV_COLOR_ZERO_INITIALIZER16 {{0x00, 0x00, 0x00, 0x00}}
# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 3) & 0x1FU), (uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x7U)}}
#endif# define LV_COLOR_MAKE16(r8, g8, b8) {{(uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 3) & 0x1FU), (uint8_t)((b8 >> 3) & 0x1FU), (uint8_t)((g8 >> 2) & 0x7U)}}
我们看完这几个宏的定义后,来逐步展开一下最开始的那个函数调用,看看最终是什么样子:
return _LV_COLOR_MAKE_TYPE_HELPER LV_COLOR_MAKE(r, g, b);//第一步:_LV_COLOR_MAKE_TYPE_HELPER 替换后return lv_color_t LV_COLOR_MAKE(r, g, b);//第二步 LV_COLOR_MAKE宏被替换后return lv_color_t LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8);//第三步 LV_CONCAT宏被替换后,假如此时LV_COLOR_DEPTH等于32return lv_color_t LV_COLOR_MAKE32((r8, g8, b8);//第四步 LV_COLOR_MAKE32宏被替换return lv_color_t {{b8, g8, r8, 0xff}};/*第五步 调用lv_color_make被替换,比如:bg_color = lv_color_make(0xf1, 0xf2, 0xf3);根据目标系统的大小端不同,最终会被替换为如下之一的赋值调用*/bg_color = 0xFFF1F2F3;//或者bg_color = 0xF3F2F1FF;
;
看完这一系列“眼花缭乱”的转换,替换后,你也许在嘀咕,简单的赋值不是更好吗,搞得这么“花里胡哨”的,有必要吗?
我可以很负责人的告诉你,很有必要。为什么?
因为lvgl是一个通用的可以适配不同颜色深度像素的gui系统,要做到最大化,最简单的兼容这些系统,让用户无感,对应用代码不产生影响,具有最大的移植性,这是非常必要的。系统编译的时候就可以自动根据配置信息来选择合适的目标颜色系统,完成对应的操作。