@@ -40,24 +40,41 @@ typedef struct {
4040 size_t fb_len ;
4141} twin_fbdev_t ;
4242
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 );
51-
52- if (tx -> fb_base == MAP_FAILED )
53- return ;
43+ /* color conversion */
44+ #define ARGB32_TO_RGB565_PERLINE (dest , pixels , width ) \
45+ do { \
46+ for (int i = 0; i < width; i++) \
47+ dest[i] = ((pixels[i] & 0x00f80000) >> 8) | \
48+ ((pixels[i] & 0x0000fc00) >> 5) | \
49+ ((pixels[i] & 0x000000f8) >> 3); \
50+ } while (0)
51+
52+ /* Requires validation in 24-bit per pixel environments */
53+ #define ARGB32_TO_RGB888_PERLINE (dest , pixels , width ) \
54+ do { \
55+ for (int i = 0; i < width; i++) \
56+ dest[i] = 0xff000000 | pixels[i]; \
57+ } while (0)
58+
59+ #define ARGB32_TO_ARGB32_PERLINE (dest , pixels , width ) \
60+ memcpy(dest, pixels, width * sizeof(*dest))
61+
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_fbdev_t *tx = PRIV(closure); \
69+ off_t off = sizeof(*dest) * left + top * tx->fb_fix.line_length; \
70+ dest = (uint32_t *) ((uintptr_t) tx->fb_base + off); \
71+ twin_coord_t width = right - left; \
72+ op(dest, pixels, width); \
73+ }
5474
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- }
75+ FBDEV_PUT_SPAN_IMPL (16 , ARGB32_TO_RGB565_PERLINE )
76+ FBDEV_PUT_SPAN_IMPL (24 , ARGB32_TO_RGB888_PERLINE )
77+ FBDEV_PUT_SPAN_IMPL (32 , ARGB32_TO_ARGB32_PERLINE )
6178
6279static void twin_fbdev_get_screen_size (twin_fbdev_t * tx ,
6380 int * width ,
@@ -85,6 +102,27 @@ static bool twin_fbdev_work(void *closure)
85102 return true;
86103}
87104
105+ static inline bool twin_fbdev_is_rgb565 (twin_fbdev_t * tx )
106+ {
107+ return tx -> fb_var .red .offset == 11 && tx -> fb_var .red .length == 5 &&
108+ tx -> fb_var .green .offset == 5 && tx -> fb_var .green .length == 6 &&
109+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 5 ;
110+ }
111+
112+ static inline bool twin_fbdev_is_rgb888 (twin_fbdev_t * tx )
113+ {
114+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
115+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
116+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
117+ }
118+
119+ static inline bool twin_fbdev_is_argb32 (twin_fbdev_t * tx )
120+ {
121+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
122+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
123+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
124+ }
125+
88126static bool twin_fbdev_apply_config (twin_fbdev_t * tx )
89127{
90128 /* Read changable information of the framebuffer */
@@ -96,7 +134,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
96134 /* Set the virtual screen size to be the same as the physical screen */
97135 tx -> fb_var .xres_virtual = tx -> fb_var .xres ;
98136 tx -> fb_var .yres_virtual = tx -> fb_var .yres ;
99- tx -> fb_var .bits_per_pixel = 32 ;
100137 if (ioctl (tx -> fb_fd , FBIOPUT_VSCREENINFO , & tx -> fb_var ) < 0 ) {
101138 log_error ("Failed to set framebuffer mode" );
102139 return false;
@@ -108,10 +145,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
108145 return false;
109146 }
110147
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;
148+ /* Examine the framebuffer format */
149+ switch (tx -> fb_var .bits_per_pixel ) {
150+ case 16 : /* RGB565 */
151+ if (!twin_fbdev_is_rgb565 (tx )) {
152+ log_error ("Invalid framebuffer format for 16 bpp" );
153+ return false;
154+ }
155+ break ;
156+ case 24 : /* RGB888 */
157+ if (!twin_fbdev_is_rgb888 (tx )) {
158+ log_error ("Invalid framebuffer format for 24 bpp" );
159+ return false;
160+ }
161+ break ;
162+ case 32 : /* ARGB32 */
163+ if (!twin_fbdev_is_argb32 (tx )) {
164+ log_error ("Invalid framebuffer format for 32 bpp" );
165+ return false;
166+ }
167+ break ;
168+ default :
169+ log_error ("Unsupported bits per pixel: %d" , tx -> fb_var .bits_per_pixel );
170+ break ;
115171 }
116172
117173 /* Read unchangable information of the framebuffer */
@@ -172,9 +228,21 @@ twin_context_t *twin_fbdev_init(int width, int height)
172228 goto bail_vt_fd ;
173229 }
174230
231+ /* Examine if framebuffer mapping is valid */
232+ if (tx -> fb_base == MAP_FAILED ) {
233+ log_error ("Failed to map framebuffer memory" );
234+ return ;
235+ }
236+
237+ const twin_put_span_t fbdev_put_spans [] = {
238+ _twin_fbdev_put_span16 ,
239+ _twin_fbdev_put_span24 ,
240+ _twin_fbdev_put_span32 ,
241+ };
175242 /* Create TWIN screen */
176- ctx -> screen =
177- twin_screen_create (width , height , NULL , _twin_fbdev_put_span , ctx );
243+ ctx -> screen = twin_screen_create (
244+ width , height , NULL , fbdev_put_spans [tx -> fb_var .bits_per_pixel / 8 - 2 ],
245+ ctx );
178246
179247 /* Create Linux input system object */
180248 tx -> input = twin_linux_input_create (ctx -> screen );
0 commit comments