From 2cc4b56dae0ec0084606781f01e4c712d42a664d Mon Sep 17 00:00:00 2001 From: Huaxin Date: Thu, 7 Nov 2024 22:35:47 +0800 Subject: [PATCH] Support 16/24/32 bit color depth Mado currently supports 32-bit color depth for desktop and laptop display devices. To enhance its compatibility across diverse Linux framebuffer environments, the following changes have been introduced to support 16-bit, 24-bit, and 32-bit color depths: - Distinguish the color format and perform color format conversion. - Ensure the correctness of the framebuffer format based on the color format. Additionally, initial support for color format conversion in 24-bit per pixel environments has been implemented. However, this implementation requires further testing to verify its correctness and stability. These updates expand Mado's compatibility, enabling its use on devices with limited memory resources, such as the Raspberry Pi, while maintaining compatibility with desktop display devices. --- backend/fbdev.c | 116 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 24 deletions(-) diff --git a/backend/fbdev.c b/backend/fbdev.c index e19b093..9f3e362 100644 --- a/backend/fbdev.c +++ b/backend/fbdev.c @@ -40,24 +40,41 @@ 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) -{ - twin_screen_t *screen = SCREEN(closure); - twin_fbdev_t *tx = PRIV(closure); - - if (tx->fb_base == MAP_FAILED) - return; +/* color conversion */ +#define ARGB32_TO_RGB565_PERLINE(dest, pixels, width) \ + do { \ + for (int i = 0; i < width; i++) \ + dest[i] = ((pixels[i] & 0x00f80000) >> 8) | \ + ((pixels[i] & 0x0000fc00) >> 5) | \ + ((pixels[i] & 0x000000f8) >> 3); \ + } while (0) + +/* Requires validation in 24-bit per pixel environments */ +#define ARGB32_TO_RGB888_PERLINE(dest, pixels, width) \ + do { \ + for (int i = 0; i < width; i++) \ + dest[i] = 0xff000000 | pixels[i]; \ + } while (0) + +#define ARGB32_TO_ARGB32_PERLINE(dest, pixels, width) \ + memcpy(dest, pixels, width * sizeof(*dest)) + +#define FBDEV_PUT_SPAN_IMPL(bpp, op) \ + static void _twin_fbdev_put_span##bpp( \ + twin_coord_t left, twin_coord_t top, twin_coord_t right, \ + twin_argb32_t *pixels, void *closure) \ + { \ + uint32_t *dest; \ + twin_fbdev_t *tx = PRIV(closure); \ + off_t off = sizeof(*dest) * left + top * tx->fb_fix.line_length; \ + dest = (uint32_t *) ((uintptr_t) tx->fb_base + off); \ + twin_coord_t width = right - left; \ + op(dest, pixels, width); \ + } - twin_coord_t width = right - left; - uint32_t *dest; - off_t off = sizeof(*dest) * left + top * tx->fb_fix.line_length; - dest = (uint32_t *) ((uintptr_t) tx->fb_base + off); - memcpy(dest, pixels, width * sizeof(*dest)); -} +FBDEV_PUT_SPAN_IMPL(16, ARGB32_TO_RGB565_PERLINE) +FBDEV_PUT_SPAN_IMPL(24, ARGB32_TO_RGB888_PERLINE) +FBDEV_PUT_SPAN_IMPL(32, ARGB32_TO_ARGB32_PERLINE) static void twin_fbdev_get_screen_size(twin_fbdev_t *tx, int *width, @@ -85,6 +102,27 @@ static bool twin_fbdev_work(void *closure) return true; } +static inline 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 inline 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 inline 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 */ @@ -96,7 +134,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx) /* Set the virtual screen size to be the same as the physical screen */ tx->fb_var.xres_virtual = tx->fb_var.xres; tx->fb_var.yres_virtual = tx->fb_var.yres; - tx->fb_var.bits_per_pixel = 32; if (ioctl(tx->fb_fd, FBIOPUT_VSCREENINFO, &tx->fb_var) < 0) { log_error("Failed to set framebuffer mode"); return false; @@ -108,10 +145,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx) return false; } - /* Check bits per pixel */ - if (tx->fb_var.bits_per_pixel != 32) { - log_error("Failed to set framebuffer bpp to 32"); - return false; + /* 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; } /* Read unchangable information of the framebuffer */ @@ -172,9 +228,21 @@ 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; + } + + const twin_put_span_t fbdev_put_spans[] = { + _twin_fbdev_put_span16, + _twin_fbdev_put_span24, + _twin_fbdev_put_span32, + }; /* Create TWIN screen */ - ctx->screen = - twin_screen_create(width, height, NULL, _twin_fbdev_put_span, ctx); + ctx->screen = twin_screen_create( + width, height, NULL, fbdev_put_spans[tx->fb_var.bits_per_pixel / 8 - 2], + ctx); /* Create Linux input system object */ tx->input = twin_linux_input_create(ctx->screen);