RGB565 和 RGB888是两种不同的颜色表示方式,在计算机图形学和显示领域中被广泛使用。RGB565使用16位数据表示一种颜色,每个颜色通道(红色、绿色、蓝色)使用5位、6位和5位的数字分别表示,而RGB888则使用24位数据表示一种颜色,每个颜色通道都使用8位数字。相互转换时,需要对颜色值进行折算和补偿,以确保转换后的颜色与原始颜色尽可能接近。

RGB888 -> RGB565

1.取RGB888中第一个字节的高5位作为转换后的RGB565的第二个字节的高5位
2.取RGB888中第二个字节的高3位作为转换后的RGB565第二个字节的低3位
3.取RGB888中第二个字节的第4--6位,作为转换后的RGB565第一个字节的高3位
4.取RGB888中第二个字节的第三个字节的高5位作为转换后的RGB565第一个字节的低5位

可以看出24位色转换为16位色时, 低位会被舍弃, 相应的精度也会丢失

  • 原始的 24bit 图片

  • 没有抖动的 16bit 图片 (有阶梯状条纹, 色彩不连续)

  • 抖动后的 16bit 图片

参考资料:

根据资料所示,抖动算法有多种

  • Floyd-Steinberg : 需要更多的计算, 速度慢
  • Ordered_dithering(有序抖动) : 计算量少, 速度快

有序抖动算法参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Dither Tresshold for Red Channel */
static const uint8_t dither_tresshold_r[64] = {
1, 7, 3, 5, 0, 8, 2, 6,
7, 1, 5, 3, 8, 0, 6, 2,
3, 5, 0, 8, 2, 6, 1, 7,
5, 3, 8, 0, 6, 2, 7, 1,

0, 8, 2, 6, 1, 7, 3, 5,
8, 0, 6, 2, 7, 1, 5, 3,
2, 6, 1, 7, 3, 5, 0, 8,
6, 2, 7, 1, 5, 3, 8, 0
};

/* Dither Tresshold for Green Channel */
static const uint8_t dither_tresshold_g[64] = {
1, 3, 2, 2, 3, 1, 2, 2,
2, 2, 0, 4, 2, 2, 4, 0,
3, 1, 2, 2, 1, 3, 2, 2,
2, 2, 4, 0, 2, 2, 0, 4,

1, 3, 2, 2, 3, 1, 2, 2,
2, 2, 0, 4, 2, 2, 4, 0,
3, 1, 2, 2, 1, 3, 2, 2,
2, 2, 4, 0, 2, 2, 0, 4
};

/* Dither Tresshold for Blue Channel */
static const uint8_t dither_tresshold_b[64] = {
5, 3, 8, 0, 6, 2, 7, 1,
3, 5, 0, 8, 2, 6, 1, 7,
8, 0, 6, 2, 7, 1, 5, 3,
0, 8, 2, 6, 1, 7, 3, 5,

6, 2, 7, 1, 5, 3, 8, 0,
2, 6, 1, 7, 3, 5, 0, 8,
7, 1, 5, 3, 8, 0, 6, 2,
1, 7, 3, 5, 0, 8, 2, 6
};

/* Get 16bit closest color */
uint8_t closest_rb(uint8_t c) {
return (c >> 3 << 3); /* red & blue */
}
uint8_t closest_g(uint8_t c) {
return (c >> 2 << 2); /* green */
}

/* RGB565 */
uint16_t RGB16BIT(uint8_t r, uint8_t g, uint8_t b) {
return ((uint16_t)((r>>3)<<11)|((g>>2)<<5)|(b>>3));
}

#define MIN(x, y) ( (x <= y) ? (x) : (y) )

/* Dithering by individual subpixel */
uint16_t dither_xy(
int x,
int y,
uint8_t r,
uint8_t g,
uint8_t b
){
/* Get Tresshold Index */
uint8_t tresshold_id = ((y & 7) << 3) + (x & 7);

r = closest_rb(
MIN(r + dither_tresshold_r[tresshold_id], 0xff)
);
g = closest_g(
MIN(g + dither_tresshold_g[tresshold_id], 0xff)
);
b = closest_rb(
MIN(b + dither_tresshold_b[tresshold_id], 0xff)
);
return RGB16BIT(r, g, b);
}

/* Dithering Pixel from 32/24bit RGB
*
* GetR, GetG, GetB -> Function to get individual color in pixel
*
*/
uint16_t dither_color_xy(int x, int y, uint32_t col) {
return dither_xy(x, y, (col >> 16) & 0xFF , ((col) >> 8) & 0xFF, ((col) & 0xFF));
}

/* EXAMPLES */
void ExampleDither1(uint16_t * dest, uint32_t * src, int width, int height){
int x, y;
for (y=0; y<height; y++){
for (x=0; x<width; x++){
int pos = y * width + x;
dest[pos] = dither_color_xy(x,y,src[pos]);
}
}
}
/* EXAMPLES */
void ExampleDither2(uint16_t * dest, uint8_t * src, int width, int height){
int x, y;
for (y=0; y<height; y++){
for (x=0; x<width; x++){
int pos = y * width + x;
dest[pos] = dither_xy(x,y,src[pos*3],src[pos*3+1],src[pos*3+2]);
}
}
}



typedef union
{
struct
{
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
} ch;
uint32_t full;
} color32_t;


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


void argb8888_to_rgb565(uint8_t *in_argb8888, uint8_t *out_rgb565, uint16_t w, uint16_t h)
{
color32_t *col_32 = (color32_t *)in_argb8888;
color16_t *col_16 = (color16_t *)out_rgb565;

/* 不使用抖动算法
// uint32_t size = w * h;
// for (uint32_t i = 0; i < size; i++)
// {
// col_16->ch.red = col_32->ch.red >> 3;
// col_16->ch.green = col_32->ch.green >> 2;
// col_16->ch.blue = col_32->ch.blue >> 3;

// col_32++;
// col_16++;
// }
*/

uint8_t tresshold_id;
uint8_t r;
uint8_t g;
uint8_t b;

for (uint16_t y=0; y<h; y++) {
for (uint16_t x=0; x<w; x++) {
tresshold_id = ((y & 7) << 3) + (x & 7);
r = closest_rb(MIN(col_32->ch.red + dither_tresshold_r[tresshold_id], 0xff));
g = closest_g (MIN(col_32->ch.green + dither_tresshold_g[tresshold_id], 0xff));
b = closest_rb(MIN(col_32->ch.blue + dither_tresshold_b[tresshold_id], 0xff));

col_16->full = ((uint16_t)((r>>3)<<11)|((g>>2)<<5)|(b>>3));

col_32++;
col_16++;
}
}

}