@@ -40,24 +40,41 @@ typedef struct {
40
40
size_t fb_len ;
41
41
} twin_fbdev_t ;
42
42
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
+ }
54
74
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 )
61
78
62
79
static void twin_fbdev_get_screen_size (twin_fbdev_t * tx ,
63
80
int * width ,
@@ -85,6 +102,27 @@ static bool twin_fbdev_work(void *closure)
85
102
return true;
86
103
}
87
104
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
+
88
126
static bool twin_fbdev_apply_config (twin_fbdev_t * tx )
89
127
{
90
128
/* Read changable information of the framebuffer */
@@ -96,7 +134,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
96
134
/* Set the virtual screen size to be the same as the physical screen */
97
135
tx -> fb_var .xres_virtual = tx -> fb_var .xres ;
98
136
tx -> fb_var .yres_virtual = tx -> fb_var .yres ;
99
- tx -> fb_var .bits_per_pixel = 32 ;
100
137
if (ioctl (tx -> fb_fd , FBIOPUT_VSCREENINFO , & tx -> fb_var ) < 0 ) {
101
138
log_error ("Failed to set framebuffer mode" );
102
139
return false;
@@ -108,10 +145,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
108
145
return false;
109
146
}
110
147
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 ;
115
171
}
116
172
117
173
/* Read unchangable information of the framebuffer */
@@ -172,9 +228,21 @@ twin_context_t *twin_fbdev_init(int width, int height)
172
228
goto bail_vt_fd ;
173
229
}
174
230
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
+ };
175
242
/* 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 );
178
246
179
247
/* Create Linux input system object */
180
248
tx -> input = twin_linux_input_create (ctx -> screen );
0 commit comments