Skip to content

Commit 35ddf50

Browse files
authored
Merge pull request #70 from p96114175/main
Support 16/24/32 bit color depth
2 parents e4e0e70 + 2cc4b56 commit 35ddf50

File tree

1 file changed

+92
-24
lines changed

1 file changed

+92
-24
lines changed

backend/fbdev.c

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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

6279
static 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+
88126
static 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

Comments
 (0)