2222#define SCREEN (x ) ((twin_context_t *) x)->screen
2323#define PRIV (x ) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
2424
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+
2544typedef struct {
2645 twin_screen_t * screen ;
2746
@@ -40,24 +59,23 @@ typedef struct {
4059 size_t fb_len ;
4160} twin_fbdev_t ;
4261
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+ }
5175
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 )
6179
6280static void twin_fbdev_get_screen_size (twin_fbdev_t * tx ,
6381 int * width ,
@@ -85,6 +103,27 @@ static bool twin_fbdev_work(void *closure)
85103 return true;
86104}
87105
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+
88127static bool twin_fbdev_apply_config (twin_fbdev_t * tx )
89128{
90129 /* Read changable information of the framebuffer */
@@ -96,7 +135,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
96135 /* Set the virtual screen size to be the same as the physical screen */
97136 tx -> fb_var .xres_virtual = tx -> fb_var .xres ;
98137 tx -> fb_var .yres_virtual = tx -> fb_var .yres ;
99- tx -> fb_var .bits_per_pixel = 32 ;
100138 if (ioctl (tx -> fb_fd , FBIOPUT_VSCREENINFO , & tx -> fb_var ) < 0 ) {
101139 log_error ("Failed to set framebuffer mode" );
102140 return false;
@@ -108,10 +146,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
108146 return false;
109147 }
110148
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 ;
115172 }
116173
117174 /* Read unchangable information of the framebuffer */
@@ -172,9 +229,21 @@ twin_context_t *twin_fbdev_init(int width, int height)
172229 goto bail_vt_fd ;
173230 }
174231
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+ };
175243 /* 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 );
178247
179248 /* Create Linux input system object */
180249 tx -> input = twin_linux_input_create (ctx -> screen );
0 commit comments