22
22
#define SCREEN (x ) ((twin_context_t *) x)->screen
23
23
#define PRIV (x ) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
24
24
25
+ /* color conversion */
26
+ #define ARGB32_TO_RGB565_PERLINE (dest , pixels , width ) \
27
+ do { \
28
+ for (int i = 0; i < width; i++) \
29
+ dest[i] = ((pixels[i] & 0x00f80000) >> 8) | \
30
+ ((pixels[i] & 0x0000fc00) >> 5) | \
31
+ ((pixels[i] & 0x000000f8) >> 3); \
32
+ } while (0)
33
+
34
+ /* Requires validation in 24-bit per pixel environments */
35
+ #define ARGB32_TO_RGB888_PERLINE (dest , pixels , width ) \
36
+ do { \
37
+ for (int i = 0; i < width; i++) \
38
+ dest[i] = 0xff000000 | pixels[i]; \
39
+ } while (0)
40
+
41
+ #define ARGB32_TO_ARGB32_PERLINE (dest , pixels , width ) \
42
+ memcpy(dest, pixels, width * sizeof(*dest))
43
+
25
44
typedef struct {
26
45
twin_screen_t * screen ;
27
46
@@ -40,24 +59,23 @@ typedef struct {
40
59
size_t fb_len ;
41
60
} twin_fbdev_t ;
42
61
43
- static void _twin_fbdev_put_span (twin_coord_t left ,
44
- twin_coord_t top ,
45
- twin_coord_t right ,
46
- twin_argb32_t * pixels ,
47
- void * closure )
48
- {
49
- twin_screen_t * screen = SCREEN (closure );
50
- twin_fbdev_t * tx = PRIV (closure );
62
+ #define FBDEV_PUT_SPAN_IMPL (bpp , op ) \
63
+ static void _twin_fbdev_put_span##bpp( \
64
+ twin_coord_t left, twin_coord_t top, twin_coord_t right, \
65
+ twin_argb32_t *pixels, void *closure) \
66
+ { \
67
+ uint32_t *dest; \
68
+ twin_screen_t *screen = SCREEN(closure); \
69
+ twin_fbdev_t *tx = PRIV(closure); \
70
+ off_t off = sizeof(*dest) * left + top * tx->fb_fix.line_length; \
71
+ dest = (uint32_t *) ((uintptr_t) tx->fb_base + off); \
72
+ twin_coord_t width = right - left; \
73
+ op(dest, pixels, width); \
74
+ }
51
75
52
- if (tx -> fb_base == MAP_FAILED )
53
- return ;
54
-
55
- twin_coord_t width = right - left ;
56
- uint32_t * dest ;
57
- off_t off = sizeof (* dest ) * left + top * tx -> fb_fix .line_length ;
58
- dest = (uint32_t * ) ((uintptr_t ) tx -> fb_base + off );
59
- memcpy (dest , pixels , width * sizeof (* dest ));
60
- }
76
+ FBDEV_PUT_SPAN_IMPL (16 , ARGB32_TO_RGB565_PERLINE )
77
+ FBDEV_PUT_SPAN_IMPL (24 , ARGB32_TO_RGB888_PERLINE )
78
+ FBDEV_PUT_SPAN_IMPL (32 , ARGB32_TO_ARGB32_PERLINE )
61
79
62
80
static void twin_fbdev_get_screen_size (twin_fbdev_t * tx ,
63
81
int * width ,
@@ -85,6 +103,27 @@ static bool twin_fbdev_work(void *closure)
85
103
return true;
86
104
}
87
105
106
+ static inline bool twin_fbdev_is_rgb565 (twin_fbdev_t * tx )
107
+ {
108
+ return tx -> fb_var .red .offset == 11 && tx -> fb_var .red .length == 5 &&
109
+ tx -> fb_var .green .offset == 5 && tx -> fb_var .green .length == 6 &&
110
+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 5 ;
111
+ }
112
+
113
+ static inline bool twin_fbdev_is_rgb888 (twin_fbdev_t * tx )
114
+ {
115
+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
116
+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
117
+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
118
+ }
119
+
120
+ static inline bool twin_fbdev_is_argb32 (twin_fbdev_t * tx )
121
+ {
122
+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
123
+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
124
+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
125
+ }
126
+
88
127
static bool twin_fbdev_apply_config (twin_fbdev_t * tx )
89
128
{
90
129
/* Read changable information of the framebuffer */
@@ -96,7 +135,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
96
135
/* Set the virtual screen size to be the same as the physical screen */
97
136
tx -> fb_var .xres_virtual = tx -> fb_var .xres ;
98
137
tx -> fb_var .yres_virtual = tx -> fb_var .yres ;
99
- tx -> fb_var .bits_per_pixel = 32 ;
100
138
if (ioctl (tx -> fb_fd , FBIOPUT_VSCREENINFO , & tx -> fb_var ) < 0 ) {
101
139
log_error ("Failed to set framebuffer mode" );
102
140
return false;
@@ -108,10 +146,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
108
146
return false;
109
147
}
110
148
111
- /* Check bits per pixel */
112
- if (tx -> fb_var .bits_per_pixel != 32 ) {
113
- log_error ("Failed to set framebuffer bpp to 32" );
114
- return false;
149
+ /* Examine the framebuffer format */
150
+ switch (tx -> fb_var .bits_per_pixel ) {
151
+ case 16 : /* RGB565 */
152
+ if (!twin_fbdev_is_rgb565 (tx )) {
153
+ log_error ("Invalid framebuffer format for 16 bpp" );
154
+ return false;
155
+ }
156
+ break ;
157
+ case 24 : /* RGB888 */
158
+ if (!twin_fbdev_is_rgb888 (tx )) {
159
+ log_error ("Invalid framebuffer format for 24 bpp" );
160
+ return false;
161
+ }
162
+ break ;
163
+ case 32 : /* ARGB32 */
164
+ if (!twin_fbdev_is_argb32 (tx )) {
165
+ log_error ("Invalid framebuffer format for 32 bpp" );
166
+ return false;
167
+ }
168
+ break ;
169
+ default :
170
+ log_error ("Unsupported bits per pixel: %d" , tx -> fb_var .bits_per_pixel );
171
+ break ;
115
172
}
116
173
117
174
/* Read unchangable information of the framebuffer */
@@ -172,9 +229,21 @@ twin_context_t *twin_fbdev_init(int width, int height)
172
229
goto bail_vt_fd ;
173
230
}
174
231
232
+ /* Examine if framebuffer mapping is valid */
233
+ if (tx -> fb_base == MAP_FAILED ) {
234
+ log_error ("Failed to map framebuffer memory" );
235
+ return ;
236
+ }
237
+
238
+ const twin_put_span_t fbdev_put_spans [] = {
239
+ _twin_fbdev_put_span16 ,
240
+ _twin_fbdev_put_span24 ,
241
+ _twin_fbdev_put_span32 ,
242
+ };
175
243
/* Create TWIN screen */
176
- ctx -> screen =
177
- twin_screen_create (width , height , NULL , _twin_fbdev_put_span , ctx );
244
+ ctx -> screen = twin_screen_create (
245
+ width , height , NULL , fbdev_put_spans [tx -> fb_var .bits_per_pixel / 8 - 2 ],
246
+ ctx );
178
247
179
248
/* Create Linux input system object */
180
249
tx -> input = twin_linux_input_create (ctx -> screen );
0 commit comments