diff --git a/src/uterm_fbdev_internal.h b/src/uterm_fbdev_internal.h index f89a1994..e263e4a8 100644 --- a/src/uterm_fbdev_internal.h +++ b/src/uterm_fbdev_internal.h @@ -55,6 +55,7 @@ struct fbdev_display { unsigned int stride; bool xrgb32; + bool rgb24; bool rgb16; unsigned int Bpp; unsigned int off_r; diff --git a/src/uterm_fbdev_render.c b/src/uterm_fbdev_render.c index a06bf08b..2dda19a2 100644 --- a/src/uterm_fbdev_render.c +++ b/src/uterm_fbdev_render.c @@ -100,6 +100,21 @@ static uint_fast32_t xrgb32_to_device(struct uterm_display *disp, return res; } +static void write_24bit(uint8_t *dst, uint_fast32_t value) +{ + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + dst[0] = value; + dst[1] = value >> 8; + dst[2] = value >> 16; + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + dst[0] = value >> 16; + dst[1] = value >> 8; + dst[2] = value; + #else + #error "Unknown endianness" + #endif +} + int uterm_fbdev_display_blit(struct uterm_display *disp, const struct uterm_video_buffer *buf, unsigned int x, unsigned int y) @@ -151,6 +166,16 @@ int uterm_fbdev_display_blit(struct uterm_display *disp, dst += fbdev->stride; src += buf->stride; } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width; ++i) { + val = ((uint32_t*)src)[i]; + uint_fast32_t full = xrgb32_to_device(disp, val); + write_24bit(&dst[i * 3], full); + } + dst += fbdev->stride; + src += buf->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) { @@ -272,6 +297,35 @@ int uterm_fbdev_display_fake_blendv(struct uterm_display *disp, dst += fbdev->stride; src += req->buf->stride; } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width; ++i) { + if (src[i] == 0) { + r = req->br; + g = req->bg; + b = req->bb; + } else if (src[i] == 255) { + r = req->fr; + g = req->fg; + b = req->fb; + } else { + r = req->fr * src[i] + + req->br * (255 - src[i]); + r /= 256; + g = req->fg * src[i] + + req->bg * (255 - src[i]); + g /= 256; + b = req->fb * src[i] + + req->bb * (255 - src[i]); + b /= 256; + } + val = (r << 16) | (g << 8) | b; + uint_fast32_t full = xrgb32_to_device(disp, val); + write_24bit(&dst[i * 3], full); + } + dst += fbdev->stride; + src += req->buf->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) { @@ -358,6 +412,13 @@ int uterm_fbdev_display_fill(struct uterm_display *disp, dst += fbdev->stride; } } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width * 3; i += 3) { + write_24bit(&dst[i], full_val); + } + dst += fbdev->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) diff --git a/src/uterm_fbdev_video.c b/src/uterm_fbdev_video.c index 06bddcae..c977b8f1 100644 --- a/src/uterm_fbdev_video.c +++ b/src/uterm_fbdev_video.c @@ -132,9 +132,7 @@ static int display_activate_force(struct uterm_display *disp, struct uterm_mode *mode, bool force) { - /* TODO: Add support for 24-bpp. However, we need to check how 3-bytes - * integers are assembled in big/little/mixed endian systems. */ - static const char depths[] = { 32, 16, 0 }; + static const char depths[] = { 32, 24, 16, 0 }; struct fbdev_display *dfb = disp->data; struct uterm_mode *m; struct fbdev_mode *mfb; @@ -217,14 +215,19 @@ static int display_activate_force(struct uterm_display *disp, if (finfo->visual != FB_VISUAL_TRUECOLOR || vinfo->bits_per_pixel != 32) { for (i = 0; depths[i]; ++i) { - vinfo->bits_per_pixel = depths[i]; - vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + /* Try to set a new mode and if it's successful... */ + struct fb_var_screeninfo vinfo_new = *vinfo; + vinfo_new.bits_per_pixel = depths[i]; + vinfo_new.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO, - vinfo); + &vinfo_new); if (ret < 0) continue; + /* ... keep it. */ + *vinfo = vinfo_new; + ret = refresh_info(disp); if (ret) goto err_close; @@ -235,6 +238,7 @@ static int display_activate_force(struct uterm_display *disp, } if (vinfo->bits_per_pixel != 32 && + vinfo->bits_per_pixel != 24 && vinfo->bits_per_pixel != 16) { log_error("device %s does not support 16/32 bpp but: %u", dfb->node, vinfo->bits_per_pixel); @@ -333,6 +337,10 @@ static int display_activate_force(struct uterm_display *disp, dfb->off_r == 11 && dfb->off_g == 5 && dfb->off_b == 0 && dfb->Bpp == 2) dfb->rgb16 = true; + else if (dfb->len_r == 8 && dfb->len_g == 8 && dfb->len_b == 8 && + dfb->off_r == 16 && dfb->off_g == 8 && dfb->off_b == 0 && + dfb->Bpp == 3) + dfb->rgb24 = true; /* TODO: make dithering configurable */ disp->flags |= DISPLAY_DITHERING; @@ -455,6 +463,8 @@ static int display_get_buffers(struct uterm_display *disp, f = UTERM_FORMAT_XRGB32; else if (dfb->rgb16) f = UTERM_FORMAT_RGB16; + else if (dfb->rgb24) + f = UTERM_FORMAT_RGB24; if (!(formats & f)) return -EOPNOTSUPP; diff --git a/src/uterm_video.h b/src/uterm_video.h index 9f516c55..fe3cb787 100644 --- a/src/uterm_video.h +++ b/src/uterm_video.h @@ -100,6 +100,7 @@ enum uterm_video_format { UTERM_FORMAT_GREY = 0x01, UTERM_FORMAT_XRGB32 = 0x02, UTERM_FORMAT_RGB16 = 0x04, + UTERM_FORMAT_RGB24 = 0x08, }; struct uterm_video_buffer {