Skip to content

Commit

Permalink
Support 16/32 bit color depth
Browse files Browse the repository at this point in the history
Mado currently supports 32-bit color depth for desktop and laptop
display devices. To enhance its compatibility across diverse Linux
framebuffer environments, I have made the following changes to support
both 16-bit and 32-bit color depths:

- Distinguish the color format and perform color format conversion.
- Examine the correctness of the framebuffer format based on the color
  format.

These updates improve Mado's compatibility, enabling it to be used on
devices with low memory overhead, such as the Raspberry Pi, in addition
to desktop display devices.
  • Loading branch information
huaxinliao committed Nov 21, 2024
1 parent b6a91e5 commit 8f5b338
Showing 1 changed file with 109 additions and 14 deletions.
123 changes: 109 additions & 14 deletions backend/fbdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
#define FBDEV_DEFAULT "/dev/fb0"
#define SCREEN(x) ((twin_context_t *) x)->screen
#define PRIV(x) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
#define ARGB32_TO_RGB565(pixel) \
(((pixel & 0x00f80000) >> 8) | ((pixel & 0x0000fc00) >> 5) | \
((pixel & 0x000000f8) >> 3))

/* Requires validation in 24-bit per pixel environments. */
#define ARGB32_TO_RGB888(pixel) (0xff000000 | (pixel))
#define GET_TWIN_FBDEV(left, top, closure, dest) \
do { \
twin_screen_t *screen = SCREEN(closure); \
twin_fbdev_t *tx = PRIV(closure); \
off_t off = (top) * (screen->width) + (left); \
*(dest) = \
(uint32_t *) ((uintptr_t) tx->fb_base + (off * sizeof(uint32_t))); \
} while (0)

typedef struct {
twin_screen_t *screen;
Expand All @@ -43,22 +57,43 @@ typedef struct {
size_t fb_len;
} twin_fbdev_t;

static void _twin_fbdev_put_span(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_argb32_t *pixels,
void *closure)
static void _twin_fbdev_put_span16(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_argb32_t *pixels,
void *closure)
{
twin_screen_t *screen = SCREEN(closure);
twin_fbdev_t *tx = PRIV(closure);
uint32_t *dest;
GET_TWIN_FBDEV(left, top, closure, &dest);
twin_coord_t width = right - left;
for (int i = 0; i < width; i++) {
dest[i] = ARGB32_TO_RGB565(pixels[i]);
}
}

if (tx->fb_base == MAP_FAILED)
return;
static void _twin_fbdev_put_span24(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_argb32_t *pixels,
void *closure)
{
uint32_t *dest;
GET_TWIN_FBDEV(left, top, closure, &dest);
twin_coord_t width = right - left;
for (int i = 0; i < width; i++) {
dest[i] = ARGB32_TO_RGB888(pixels[i]);
}
}

static void _twin_fbdev_put_span32(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_argb32_t *pixels,
void *closure)
{
uint32_t *dest;
GET_TWIN_FBDEV(left, top, closure, &dest);
twin_coord_t width = right - left;
off_t off = top * screen->width + left;
uint32_t *dest =
(uint32_t *) ((uintptr_t) tx->fb_base + (off * sizeof(*dest)));
memcpy(dest, pixels, width * sizeof(*dest));
}

Expand Down Expand Up @@ -88,6 +123,27 @@ static bool twin_fbdev_work(void *closure)
return true;
}

static bool twin_fbdev_is_rgb565(twin_fbdev_t *tx)
{
return tx->fb_var.red.offset == 11 && tx->fb_var.red.length == 5 &&
tx->fb_var.green.offset == 5 && tx->fb_var.green.length == 6 &&
tx->fb_var.blue.offset == 0 && tx->fb_var.blue.length == 5;
}

static bool twin_fbdev_is_rgb888(twin_fbdev_t *tx)
{
return tx->fb_var.red.offset == 16 && tx->fb_var.red.length == 8 &&
tx->fb_var.green.offset == 8 && tx->fb_var.green.length == 8 &&
tx->fb_var.blue.offset == 0 && tx->fb_var.blue.length == 8;
}

static bool twin_fbdev_is_argb32(twin_fbdev_t *tx)
{
return tx->fb_var.red.offset == 16 && tx->fb_var.red.length == 8 &&
tx->fb_var.green.offset == 8 && tx->fb_var.green.length == 8 &&
tx->fb_var.blue.offset == 0 && tx->fb_var.blue.length == 8;
}

static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
{
/* Read changable information of the framebuffer */
Expand All @@ -110,6 +166,34 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
return false;
}

<<<<<<< HEAD
=======
/* Examine the framebuffer format */
switch (tx->fb_var.bits_per_pixel) {
case 16: // RGB565
if (!twin_fbdev_is_rgb565(tx)) {
log_error("Invalid framebuffer format for 16 bpp");
return false;
}
break;
case 24: // RGB888
if (!twin_fbdev_is_rgb888(tx)) {
log_error("Invalid framebuffer format for 24 bpp");
return false;
}
break;
case 32: // ARGB32
if (!twin_fbdev_is_argb32(tx)) {
log_error("Invalid framebuffer format for 32 bpp");
return false;
}
break;
default:
log_error("Unsupported bits per pixel: %d", tx->fb_var.bits_per_pixel);
break;
}

>>>>>>> f56a638 (Support 16/32 bit color depth)
/* Read unchangable information of the framebuffer */
ioctl(tx->fb_fd, FBIOGET_FSCREENINFO, &tx->fb_fix);

Expand Down Expand Up @@ -213,9 +297,20 @@ twin_context_t *twin_fbdev_init(int width, int height)
goto bail_vt_fd;
}

/* Examine if framebuffer mapping is valid */
if (tx->fb_base == MAP_FAILED) {
log_error("Failed to map framebuffer memory");
return;
}

/* Create TWIN screen */
ctx->screen =
twin_screen_create(width, height, NULL, _twin_fbdev_put_span, ctx);
ctx->screen = twin_screen_create(
width, height, NULL,
(tx->fb_var.bits_per_pixel == 16) ? _twin_fbdev_put_span16
: (tx->fb_var.bits_per_pixel == 24) ? _twin_fbdev_put_span24
: (tx->fb_var.bits_per_pixel == 32) ? _twin_fbdev_put_span32
: NULL,
ctx);

/* Create Linux input system object */
tx->input = twin_linux_input_create(ctx->screen);
Expand Down

0 comments on commit 8f5b338

Please sign in to comment.