uterm: video: fbdev: add support for non xrgb32 devices
This adds blitting/blending/filling support for devices which are not the classic xrgb32 device. bpp=24 is not supported as it is still unclear how 3-byte integers look like in mixed/big endian. This uses a very basic dithering technique to check for errors between the real and computed values which is then distributed horizontally only. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
ea62198b6e
commit
bed915dfb6
@ -249,7 +249,18 @@ struct fbdev_display {
|
||||
size_t len;
|
||||
uint8_t *map;
|
||||
unsigned int stride;
|
||||
unsigned int bpp;
|
||||
|
||||
bool xrgb32;
|
||||
unsigned int Bpp;
|
||||
unsigned int off_r;
|
||||
unsigned int off_g;
|
||||
unsigned int off_b;
|
||||
unsigned int len_r;
|
||||
unsigned int len_g;
|
||||
unsigned int len_b;
|
||||
int_fast32_t dither_r;
|
||||
int_fast32_t dither_g;
|
||||
int_fast32_t dither_b;
|
||||
};
|
||||
|
||||
struct fbdev_video {
|
||||
@ -312,6 +323,7 @@ int mode_new(struct uterm_mode **out, const struct mode_ops *ops);
|
||||
#define DISPLAY_AVAILABLE 0x04
|
||||
#define DISPLAY_OPEN 0x08
|
||||
#define DISPLAY_DBUF 0x10
|
||||
#define DISPLAY_DITHERING 0x20
|
||||
|
||||
struct uterm_display {
|
||||
unsigned long ref;
|
||||
|
@ -81,7 +81,9 @@ static int display_activate_force(struct uterm_display *disp,
|
||||
struct uterm_mode *mode,
|
||||
bool force)
|
||||
{
|
||||
static const char depths[] = { 32, 24, 16, 0 };
|
||||
/* 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 };
|
||||
struct fb_var_screeninfo *vinfo;
|
||||
struct fb_fix_screeninfo *finfo;
|
||||
int ret, i;
|
||||
@ -112,10 +114,6 @@ static int display_activate_force(struct uterm_display *disp,
|
||||
vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
|
||||
vinfo->xres_virtual = vinfo->xres;
|
||||
vinfo->yres_virtual = vinfo->yres * 2;
|
||||
vinfo->bits_per_pixel = 32;
|
||||
|
||||
log_info("activating display %s to %ux%u %u bpp", disp->fbdev.node,
|
||||
vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
|
||||
disp->flags |= DISPLAY_DBUF;
|
||||
|
||||
/* udlfb is broken as it reports the sizes of the virtual framebuffer
|
||||
@ -153,7 +151,8 @@ static int display_activate_force(struct uterm_display *disp,
|
||||
* modes like pseudocolor or direct-color do not provide this. As I have
|
||||
* never seen a device that does not support TRUECOLOR, I think we can
|
||||
* ignore them here. */
|
||||
if (finfo->visual != FB_VISUAL_TRUECOLOR) {
|
||||
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;
|
||||
@ -176,37 +175,35 @@ static int display_activate_force(struct uterm_display *disp,
|
||||
(disp->flags & DISPLAY_DBUF &&
|
||||
vinfo->yres_virtual < vinfo->yres * 2) ||
|
||||
vinfo->yres_virtual < vinfo->yres) {
|
||||
log_error("device %s does not support out buffer sizes",
|
||||
log_error("device %s has weird buffer sizes",
|
||||
disp->fbdev.node);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (vinfo->bits_per_pixel % 8) {
|
||||
log_error("device %s uses no power of 8 bpp: %u",
|
||||
if (vinfo->bits_per_pixel != 32 &&
|
||||
vinfo->bits_per_pixel != 16) {
|
||||
log_error("device %s does not support 16/32 bpp but: %u",
|
||||
disp->fbdev.node, vinfo->bits_per_pixel);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (finfo->visual != FB_VISUAL_TRUECOLOR ||
|
||||
vinfo->bits_per_pixel < 16) {
|
||||
log_error("device %s does not support true-color bpp >= 16",
|
||||
if (finfo->visual != FB_VISUAL_TRUECOLOR) {
|
||||
log_error("device %s does not support true-color",
|
||||
disp->fbdev.node);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* TODO: remove this check and correctly provide conversions for the
|
||||
* blitting functions. In fact, the for-loop above is totally useless
|
||||
* while using this restriction here but lets be optimistic and say that
|
||||
* this will be replaced soon. */
|
||||
if (vinfo->red.offset != 16 || vinfo->red.length != 8 ||
|
||||
vinfo->green.offset != 8 || vinfo->green.length != 8 ||
|
||||
vinfo->blue.offset != 0 || vinfo->green.length != 8 ||
|
||||
vinfo->bits_per_pixel != 32) {
|
||||
log_error("device %s does not support xrgb32",
|
||||
if (vinfo->red.length > 8 ||
|
||||
vinfo->green.length > 8 ||
|
||||
vinfo->blue.length > 8) {
|
||||
log_error("device %s uses unusual color-ranges",
|
||||
disp->fbdev.node);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
log_info("activating display %s to %ux%u %u bpp", disp->fbdev.node,
|
||||
vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
|
||||
|
||||
/* calculate monitor rate, default is 60 Hz */
|
||||
quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
|
||||
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
|
||||
@ -232,9 +229,30 @@ static int display_activate_force(struct uterm_display *disp,
|
||||
disp->fbdev.xres = vinfo->xres;
|
||||
disp->fbdev.yres = vinfo->yres;
|
||||
disp->fbdev.len = len;
|
||||
disp->fbdev.bpp = vinfo->bits_per_pixel / 8;
|
||||
disp->fbdev.stride = finfo->line_length;
|
||||
disp->fbdev.bufid = 0;
|
||||
disp->fbdev.Bpp = vinfo->bits_per_pixel / 8;
|
||||
disp->fbdev.off_r = vinfo->red.offset;
|
||||
disp->fbdev.len_r = vinfo->red.length;
|
||||
disp->fbdev.off_g = vinfo->green.offset;
|
||||
disp->fbdev.len_g = vinfo->green.length;
|
||||
disp->fbdev.off_b = vinfo->blue.offset;
|
||||
disp->fbdev.len_b = vinfo->blue.length;
|
||||
disp->fbdev.dither_r = 0;
|
||||
disp->fbdev.dither_g = 0;
|
||||
disp->fbdev.dither_b = 0;
|
||||
disp->fbdev.xrgb32 = false;
|
||||
if (disp->fbdev.len_r == 8 &&
|
||||
disp->fbdev.len_g == 8 &&
|
||||
disp->fbdev.len_b == 8 &&
|
||||
disp->fbdev.off_r == 16 &&
|
||||
disp->fbdev.off_g == 8 &&
|
||||
disp->fbdev.off_b == 0 &&
|
||||
disp->fbdev.Bpp == 4)
|
||||
disp->fbdev.xrgb32 = true;
|
||||
|
||||
/* TODO: make dithering configurable */
|
||||
disp->flags |= DISPLAY_DITHERING;
|
||||
|
||||
ret = mode_new(&disp->modes, &fbdev_mode_ops);
|
||||
if (ret) {
|
||||
@ -344,13 +362,75 @@ static int display_swap(struct uterm_display *disp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clamp_value(int val, int low, int up)
|
||||
{
|
||||
if (val < low)
|
||||
return low;
|
||||
else if (val > up)
|
||||
return up;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint_fast32_t xrgb32_to_device(struct uterm_display *disp,
|
||||
uint32_t pixel)
|
||||
{
|
||||
uint8_t r, g, b, nr, ng, nb;
|
||||
int i;
|
||||
uint_fast32_t res;
|
||||
|
||||
r = (pixel >> 16) & 0xff;
|
||||
g = (pixel >> 8) & 0xff;
|
||||
b = (pixel >> 0) & 0xff;
|
||||
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
/* This is some very basic dithering which simply does small
|
||||
* rotations in the lower pixel bits. TODO: Let's take a look
|
||||
* at Floyd-Steinberg dithering which should give much better
|
||||
* results. It is slightly slower, though.
|
||||
* Or even better would be some Sierra filter like the Sierra
|
||||
* LITE. */
|
||||
disp->fbdev.dither_r = r - disp->fbdev.dither_r;
|
||||
disp->fbdev.dither_g = g - disp->fbdev.dither_g;
|
||||
disp->fbdev.dither_b = b - disp->fbdev.dither_b;
|
||||
r = clamp_value(disp->fbdev.dither_r, 0, 255) >> (8 - disp->fbdev.len_r);
|
||||
g = clamp_value(disp->fbdev.dither_g, 0, 255) >> (8 - disp->fbdev.len_g);
|
||||
b = clamp_value(disp->fbdev.dither_b, 0, 255) >> (8 - disp->fbdev.len_b);
|
||||
nr = r << (8 - disp->fbdev.len_r);
|
||||
ng = g << (8 - disp->fbdev.len_g);
|
||||
nb = b << (8 - disp->fbdev.len_b);
|
||||
|
||||
for (i = disp->fbdev.len_r; i < 8; i <<= 1)
|
||||
nr |= nr >> i;
|
||||
for (i = disp->fbdev.len_g; i < 8; i <<= 1)
|
||||
ng |= ng >> i;
|
||||
for (i = disp->fbdev.len_b; i < 8; i <<= 1)
|
||||
nb |= nb >> i;
|
||||
|
||||
disp->fbdev.dither_r = nr - disp->fbdev.dither_r;
|
||||
disp->fbdev.dither_g = ng - disp->fbdev.dither_g;
|
||||
disp->fbdev.dither_b = nb - disp->fbdev.dither_b;
|
||||
|
||||
res = r << disp->fbdev.off_r;
|
||||
res |= g << disp->fbdev.off_g;
|
||||
res |= b << disp->fbdev.off_b;
|
||||
} else {
|
||||
res = (r >> (8 - disp->fbdev.len_r)) << disp->fbdev.off_r;
|
||||
res |= (g >> (8 - disp->fbdev.len_g)) << disp->fbdev.off_g;
|
||||
res |= (b >> (8 - disp->fbdev.len_b)) << disp->fbdev.off_b;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int display_blit(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height;
|
||||
unsigned int width, height, i;
|
||||
uint32_t val;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
@ -379,13 +459,35 @@ static int display_blit(struct uterm_display *disp,
|
||||
dst = disp->fbdev.map;
|
||||
else
|
||||
dst = &disp->fbdev.map[disp->fbdev.yres * disp->fbdev.stride];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.bpp];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.Bpp];
|
||||
src = buf->data;
|
||||
|
||||
while (height--) {
|
||||
memcpy(dst, src, 4 * width);
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
if (disp->fbdev.xrgb32) {
|
||||
while (height--) {
|
||||
memcpy(dst, src, 4 * width);
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (disp->fbdev.Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (disp->fbdev.Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_debug("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -401,11 +503,14 @@ static int display_blend(struct uterm_display *disp,
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i;
|
||||
unsigned int r, g, b;
|
||||
uint32_t val;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!buf || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
if (buf->format != UTERM_FORMAT_GREY)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + buf->width;
|
||||
if (tmp < x || x >= disp->fbdev.xres)
|
||||
@ -427,10 +532,10 @@ static int display_blend(struct uterm_display *disp,
|
||||
dst = disp->fbdev.map;
|
||||
else
|
||||
dst = &disp->fbdev.map[disp->fbdev.yres * disp->fbdev.stride];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.bpp];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.Bpp];
|
||||
src = buf->data;
|
||||
|
||||
if (buf->format == UTERM_FORMAT_GREY) {
|
||||
if (disp->fbdev.xrgb32) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
r = (fr & 0xff) * src[i] / 255 +
|
||||
@ -439,16 +544,50 @@ static int display_blend(struct uterm_display *disp,
|
||||
(bg & 0xff) * (255 - src[i]) / 255;
|
||||
b = (fb & 0xff) * src[i] / 255 +
|
||||
(bb & 0xff) * (255 - src[i]) / 255;
|
||||
((uint32_t*)dst)[i] =
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
(b & 0xff);
|
||||
val = (r & 0xff) << 16;
|
||||
val |= (g & 0xff) << 8;
|
||||
val |= (b & 0xff) << 0;
|
||||
((uint32_t*)dst)[i] = val;
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (disp->fbdev.Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
r = (fr & 0xff) * src[i] / 255 +
|
||||
(br & 0xff) * (255 - src[i]) / 255;
|
||||
g = (fg & 0xff) * src[i] / 255 +
|
||||
(bg & 0xff) * (255 - src[i]) / 255;
|
||||
b = (fb & 0xff) * src[i] / 255 +
|
||||
(bb & 0xff) * (255 - src[i]) / 255;
|
||||
val = (r & 0xff) << 16;
|
||||
val |= (g & 0xff) << 8;
|
||||
val |= (b & 0xff) << 0;
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (disp->fbdev.Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
r = (fr & 0xff) * src[i] / 255 +
|
||||
(br & 0xff) * (255 - src[i]) / 255;
|
||||
g = (fg & 0xff) * src[i] / 255 +
|
||||
(bg & 0xff) * (255 - src[i]) / 255;
|
||||
b = (fb & 0xff) * src[i] / 255 +
|
||||
(bb & 0xff) * (255 - src[i]) / 255;
|
||||
val = (r & 0xff) << 16;
|
||||
val |= (g & 0xff) << 8;
|
||||
val |= (b & 0xff) << 0;
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_warning("using unsupported buffer format for blending");
|
||||
log_warning("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -461,6 +600,7 @@ static int display_fill(struct uterm_display *disp,
|
||||
{
|
||||
unsigned int tmp, i;
|
||||
uint8_t *dst;
|
||||
uint32_t full_val, rgb32;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
@ -482,15 +622,39 @@ static int display_fill(struct uterm_display *disp,
|
||||
dst = disp->fbdev.map;
|
||||
else
|
||||
dst = &disp->fbdev.map[disp->fbdev.yres * disp->fbdev.stride];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.bpp];
|
||||
dst = &dst[y * disp->fbdev.stride + x * disp->fbdev.Bpp];
|
||||
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
((uint32_t*)dst)[i] = ((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
(b & 0xff);
|
||||
full_val = ((r & 0xff) >> (8 - disp->fbdev.len_r)) << disp->fbdev.off_r;
|
||||
full_val |= ((g & 0xff) >> (8 - disp->fbdev.len_g)) << disp->fbdev.off_g;
|
||||
full_val |= ((b & 0xff) >> (8 - disp->fbdev.len_b)) << disp->fbdev.off_b;
|
||||
|
||||
if (disp->fbdev.Bpp == 2) {
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
rgb32 = (r & 0xff) << 16;
|
||||
rgb32 |= (g & 0xff) << 8;
|
||||
rgb32 |= (b & 0xff) << 0;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, rgb32);
|
||||
dst += disp->fbdev.stride;
|
||||
}
|
||||
} else {
|
||||
full_val &= 0xffff;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = full_val;
|
||||
dst += disp->fbdev.stride;
|
||||
}
|
||||
}
|
||||
dst += disp->fbdev.stride;
|
||||
} else if (disp->fbdev.Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint32_t*)dst)[i] = full_val;
|
||||
dst += disp->fbdev.stride;
|
||||
}
|
||||
} else {
|
||||
log_error("invalid Bpp");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user