uterm: split fbdev module into different files

This is the first step towards a uterm-fbdev module. It splits the huge
video file into a video backend and a render backend. A monitor backend
will follow later when a full uterm module infrastructure is available.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2013-01-11 16:24:37 +01:00
parent a272e506f7
commit cad0d410d7
4 changed files with 623 additions and 526 deletions

View File

@ -266,7 +266,10 @@ libuterm_la_LIBADD += $(UDEV_LIBS)
endif
if BUILD_ENABLE_VIDEO_FBDEV
libuterm_la_SOURCES += src/uterm_video_fbdev.c
libuterm_la_SOURCES += \
src/uterm_fbdev_internal.h \
src/uterm_fbdev_video.c \
src/uterm_fbdev_render.c
endif
if BUILD_ENABLE_VIDEO_DUMB

View File

@ -0,0 +1,88 @@
/*
* uterm - Linux User-Space Terminal fbdev module
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Internal definitions */
#ifndef UTERM_FBDEV_INTERNAL_H
#define UTERM_FBDEV_INTERNAL_H
#include <inttypes.h>
#include <limits.h>
#include <linux/fb.h>
#include <stdbool.h>
#include <stdlib.h>
#include "uterm_video.h"
struct fbdev_mode {
unsigned int width;
unsigned int height;
};
struct fbdev_display {
char *node;
int fd;
bool pending_intro;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
unsigned int rate;
unsigned int bufid;
size_t xres;
size_t yres;
size_t len;
uint8_t *map;
unsigned int stride;
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;
};
int uterm_fbdev_display_blit(struct uterm_display *disp,
const struct uterm_video_buffer *buf,
unsigned int x, unsigned int y);
int uterm_fbdev_display_blend(struct uterm_display *disp,
const struct uterm_video_buffer *buf,
unsigned int x, unsigned int y,
uint8_t fr, uint8_t fg, uint8_t fb,
uint8_t br, uint8_t bg, uint8_t bb);
int uterm_fbdev_display_fake_blendv(struct uterm_display *disp,
const struct uterm_video_blend_req *req,
size_t num);
int uterm_fbdev_display_fill(struct uterm_display *disp,
uint8_t r, uint8_t g, uint8_t b,
unsigned int x, unsigned int y,
unsigned int width, unsigned int height);
#endif /* UTERM_FBDEV_INTERNAL_H */

524
src/uterm_fbdev_render.c Normal file
View File

@ -0,0 +1,524 @@
/*
* uterm - Linux User-Space Terminal fbdev module
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* FBDEV module rendering functions
*/
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "log.h"
#include "uterm_fbdev_internal.h"
#include "uterm_video.h"
#include "uterm_video_internal.h"
#define LOG_SUBSYSTEM "fbdev_render"
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;
struct fbdev_display *fbdev = disp->data;
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. */
fbdev->dither_r = r - fbdev->dither_r;
fbdev->dither_g = g - fbdev->dither_g;
fbdev->dither_b = b - fbdev->dither_b;
r = clamp_value(fbdev->dither_r, 0, 255) >> (8 - fbdev->len_r);
g = clamp_value(fbdev->dither_g, 0, 255) >> (8 - fbdev->len_g);
b = clamp_value(fbdev->dither_b, 0, 255) >> (8 - fbdev->len_b);
nr = r << (8 - fbdev->len_r);
ng = g << (8 - fbdev->len_g);
nb = b << (8 - fbdev->len_b);
for (i = fbdev->len_r; i < 8; i <<= 1)
nr |= nr >> i;
for (i = fbdev->len_g; i < 8; i <<= 1)
ng |= ng >> i;
for (i = fbdev->len_b; i < 8; i <<= 1)
nb |= nb >> i;
fbdev->dither_r = nr - fbdev->dither_r;
fbdev->dither_g = ng - fbdev->dither_g;
fbdev->dither_b = nb - fbdev->dither_b;
res = r << fbdev->off_r;
res |= g << fbdev->off_g;
res |= b << fbdev->off_b;
} else {
res = (r >> (8 - fbdev->len_r)) << fbdev->off_r;
res |= (g >> (8 - fbdev->len_g)) << fbdev->off_g;
res |= (b >> (8 - fbdev->len_b)) << fbdev->off_b;
}
return res;
}
int uterm_fbdev_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, i;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!buf || !video_is_awake(disp->video))
return -EINVAL;
if (buf->format != UTERM_FORMAT_XRGB32)
return -EINVAL;
tmp = x + buf->width;
if (tmp < x || x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
else
width = buf->width;
tmp = y + buf->height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
else
height = buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
src = buf->data;
if (fbdev->xrgb32) {
while (height--) {
memcpy(dst, src, 4 * width);
dst += fbdev->stride;
src += buf->stride;
}
} else if (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 += fbdev->stride;
src += buf->stride;
}
} else if (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 += fbdev->stride;
src += buf->stride;
}
} else {
log_debug("invalid Bpp");
}
return 0;
}
int uterm_fbdev_display_blend(struct uterm_display *disp,
const struct uterm_video_buffer *buf,
unsigned int x, unsigned int y,
uint8_t fr, uint8_t fg, uint8_t fb,
uint8_t br, uint8_t bg, uint8_t bb)
{
unsigned int tmp;
uint8_t *dst, *src;
unsigned int width, height, i;
unsigned int r, g, b;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
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 >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
else
width = buf->width;
tmp = y + buf->height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
else
height = buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
src = buf->data;
/* Division by 256 instead of 255 increases
* speed by like 20% on slower machines.
* Downside is, full white is 254/254/254
* instead of 255/255/255. */
if (fbdev->xrgb32) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint32_t*)dst)[i] = val;
}
dst += fbdev->stride;
src += buf->stride;
}
} else if (fbdev->Bpp == 2) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint16_t*)dst)[i] = xrgb32_to_device(disp,
val);
}
dst += fbdev->stride;
src += buf->stride;
}
} else if (fbdev->Bpp == 4) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint32_t*)dst)[i] = xrgb32_to_device(disp,
val);
}
dst += fbdev->stride;
src += buf->stride;
}
} else {
log_warning("invalid Bpp");
}
return 0;
}
int uterm_fbdev_display_fake_blendv(struct uterm_display *disp,
const struct uterm_video_blend_req *req,
size_t num)
{
unsigned int tmp;
uint8_t *dst, *src;
unsigned int width, height, i, j;
unsigned int r, g, b;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!req || !video_is_awake(disp->video))
return -EINVAL;
for (j = 0; j < num; ++j, ++req) {
if (!req->buf)
continue;
if (req->buf->format != UTERM_FORMAT_GREY)
return -EOPNOTSUPP;
tmp = req->x + req->buf->width;
if (tmp < req->x || req->x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - req->x;
else
width = req->buf->width;
tmp = req->y + req->buf->height;
if (tmp < req->y || req->y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - req->y;
else
height = req->buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[req->y * fbdev->stride + req->x * fbdev->Bpp];
src = req->buf->data;
/* Division by 256 instead of 255 increases
* speed by like 20% on slower machines.
* Downside is, full white is 254/254/254
* instead of 255/255/255. */
if (fbdev->xrgb32) {
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;
((uint32_t*)dst)[i] = val;
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else if (fbdev->Bpp == 2) {
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;
((uint16_t*)dst)[i] =
xrgb32_to_device(disp, val);
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else if (fbdev->Bpp == 4) {
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;
((uint32_t*)dst)[i] =
xrgb32_to_device(disp, val);
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else {
log_warning("invalid Bpp");
}
}
return 0;
}
int uterm_fbdev_display_fill(struct uterm_display *disp,
uint8_t r, uint8_t g, uint8_t b,
unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
unsigned int tmp, i;
uint8_t *dst;
uint32_t full_val, rgb32;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!video_is_awake(disp->video))
return -EINVAL;
tmp = x + width;
if (tmp < x || x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
tmp = y + height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
full_val = ((r & 0xff) >> (8 - fbdev->len_r)) << fbdev->off_r;
full_val |= ((g & 0xff) >> (8 - fbdev->len_g)) << fbdev->off_g;
full_val |= ((b & 0xff) >> (8 - fbdev->len_b)) << fbdev->off_b;
if (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 += fbdev->stride;
}
} else {
full_val &= 0xffff;
while (height--) {
for (i = 0; i < width; ++i)
((uint16_t*)dst)[i] = full_val;
dst += fbdev->stride;
}
}
} else if (fbdev->Bpp == 4) {
while (height--) {
for (i = 0; i < width; ++i)
((uint32_t*)dst)[i] = full_val;
dst += fbdev->stride;
}
} else {
log_error("invalid Bpp");
return -EFAULT;
}
return 0;
}

View File

@ -1,5 +1,5 @@
/*
* uterm - Linux User-Space Terminal
* uterm - Linux User-Space Terminal fbdev module
*
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
*
@ -37,45 +37,12 @@
#include <sys/mman.h>
#include <unistd.h>
#include "log.h"
#include "uterm_fbdev_internal.h"
#include "uterm_video.h"
#include "uterm_video_internal.h"
#define LOG_SUBSYSTEM "video_fbdev"
struct fbdev_mode {
unsigned int width;
unsigned int height;
};
struct fbdev_display {
char *node;
int fd;
bool pending_intro;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
unsigned int rate;
unsigned int bufid;
size_t xres;
size_t yres;
size_t len;
uint8_t *map;
unsigned int stride;
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;
};
static int mode_init(struct uterm_mode *mode)
{
struct fbdev_mode *fbdev;
@ -464,491 +431,6 @@ static int display_swap(struct uterm_display *disp)
return display_schedule_vblank_timer(disp);
}
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;
struct fbdev_display *fbdev = disp->data;
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. */
fbdev->dither_r = r - fbdev->dither_r;
fbdev->dither_g = g - fbdev->dither_g;
fbdev->dither_b = b - fbdev->dither_b;
r = clamp_value(fbdev->dither_r, 0, 255) >> (8 - fbdev->len_r);
g = clamp_value(fbdev->dither_g, 0, 255) >> (8 - fbdev->len_g);
b = clamp_value(fbdev->dither_b, 0, 255) >> (8 - fbdev->len_b);
nr = r << (8 - fbdev->len_r);
ng = g << (8 - fbdev->len_g);
nb = b << (8 - fbdev->len_b);
for (i = fbdev->len_r; i < 8; i <<= 1)
nr |= nr >> i;
for (i = fbdev->len_g; i < 8; i <<= 1)
ng |= ng >> i;
for (i = fbdev->len_b; i < 8; i <<= 1)
nb |= nb >> i;
fbdev->dither_r = nr - fbdev->dither_r;
fbdev->dither_g = ng - fbdev->dither_g;
fbdev->dither_b = nb - fbdev->dither_b;
res = r << fbdev->off_r;
res |= g << fbdev->off_g;
res |= b << fbdev->off_b;
} else {
res = (r >> (8 - fbdev->len_r)) << fbdev->off_r;
res |= (g >> (8 - fbdev->len_g)) << fbdev->off_g;
res |= (b >> (8 - fbdev->len_b)) << 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, i;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!buf || !video_is_awake(disp->video))
return -EINVAL;
if (buf->format != UTERM_FORMAT_XRGB32)
return -EINVAL;
tmp = x + buf->width;
if (tmp < x || x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
else
width = buf->width;
tmp = y + buf->height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
else
height = buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
src = buf->data;
if (fbdev->xrgb32) {
while (height--) {
memcpy(dst, src, 4 * width);
dst += fbdev->stride;
src += buf->stride;
}
} else if (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 += fbdev->stride;
src += buf->stride;
}
} else if (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 += fbdev->stride;
src += buf->stride;
}
} else {
log_debug("invalid Bpp");
}
return 0;
}
static int display_blend(struct uterm_display *disp,
const struct uterm_video_buffer *buf,
unsigned int x, unsigned int y,
uint8_t fr, uint8_t fg, uint8_t fb,
uint8_t br, uint8_t bg, uint8_t bb)
{
unsigned int tmp;
uint8_t *dst, *src;
unsigned int width, height, i;
unsigned int r, g, b;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
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 >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
else
width = buf->width;
tmp = y + buf->height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
else
height = buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
src = buf->data;
/* Division by 256 instead of 255 increases
* speed by like 20% on slower machines.
* Downside is, full white is 254/254/254
* instead of 255/255/255. */
if (fbdev->xrgb32) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint32_t*)dst)[i] = val;
}
dst += fbdev->stride;
src += buf->stride;
}
} else if (fbdev->Bpp == 2) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint16_t*)dst)[i] = xrgb32_to_device(disp,
val);
}
dst += fbdev->stride;
src += buf->stride;
}
} else if (fbdev->Bpp == 4) {
while (height--) {
for (i = 0; i < width; ++i) {
if (src[i] == 0) {
r = br;
g = bg;
b = bb;
} else if (src[i] == 255) {
r = fr;
g = fg;
b = fb;
} else {
r = fr * src[i] +
br * (255 - src[i]);
r /= 256;
g = fg * src[i] +
bg * (255 - src[i]);
g /= 256;
b = fb * src[i] +
bb * (255 - src[i]);
b /= 256;
}
val = (r << 16) | (g << 8) | b;
((uint32_t*)dst)[i] = xrgb32_to_device(disp,
val);
}
dst += fbdev->stride;
src += buf->stride;
}
} else {
log_warning("invalid Bpp");
}
return 0;
}
static int display_fake_blendv(struct uterm_display *disp,
const struct uterm_video_blend_req *req,
size_t num)
{
unsigned int tmp;
uint8_t *dst, *src;
unsigned int width, height, i, j;
unsigned int r, g, b;
uint32_t val;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!req || !video_is_awake(disp->video))
return -EINVAL;
for (j = 0; j < num; ++j, ++req) {
if (!req->buf)
continue;
if (req->buf->format != UTERM_FORMAT_GREY)
return -EOPNOTSUPP;
tmp = req->x + req->buf->width;
if (tmp < req->x || req->x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - req->x;
else
width = req->buf->width;
tmp = req->y + req->buf->height;
if (tmp < req->y || req->y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - req->y;
else
height = req->buf->height;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[req->y * fbdev->stride + req->x * fbdev->Bpp];
src = req->buf->data;
/* Division by 256 instead of 255 increases
* speed by like 20% on slower machines.
* Downside is, full white is 254/254/254
* instead of 255/255/255. */
if (fbdev->xrgb32) {
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;
((uint32_t*)dst)[i] = val;
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else if (fbdev->Bpp == 2) {
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;
((uint16_t*)dst)[i] =
xrgb32_to_device(disp, val);
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else if (fbdev->Bpp == 4) {
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;
((uint32_t*)dst)[i] =
xrgb32_to_device(disp, val);
}
dst += fbdev->stride;
src += req->buf->stride;
}
} else {
log_warning("invalid Bpp");
}
}
return 0;
}
static int display_fill(struct uterm_display *disp,
uint8_t r, uint8_t g, uint8_t b,
unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
unsigned int tmp, i;
uint8_t *dst;
uint32_t full_val, rgb32;
struct fbdev_display *fbdev = disp->data;
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
return -EINVAL;
if (!video_is_awake(disp->video))
return -EINVAL;
tmp = x + width;
if (tmp < x || x >= fbdev->xres)
return -EINVAL;
if (tmp > fbdev->xres)
width = fbdev->xres - x;
tmp = y + height;
if (tmp < y || y >= fbdev->yres)
return -EINVAL;
if (tmp > fbdev->yres)
height = fbdev->yres - y;
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
dst = fbdev->map;
else
dst = &fbdev->map[fbdev->yres * fbdev->stride];
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
full_val = ((r & 0xff) >> (8 - fbdev->len_r)) << fbdev->off_r;
full_val |= ((g & 0xff) >> (8 - fbdev->len_g)) << fbdev->off_g;
full_val |= ((b & 0xff) >> (8 - fbdev->len_b)) << fbdev->off_b;
if (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 += fbdev->stride;
}
} else {
full_val &= 0xffff;
while (height--) {
for (i = 0; i < width; ++i)
((uint16_t*)dst)[i] = full_val;
dst += fbdev->stride;
}
}
} else if (fbdev->Bpp == 4) {
while (height--) {
for (i = 0; i < width; ++i)
((uint32_t*)dst)[i] = full_val;
dst += fbdev->stride;
}
} else {
log_error("invalid Bpp");
return -EFAULT;
}
return 0;
}
static const struct display_ops fbdev_display_ops = {
.init = NULL,
.destroy = NULL,
@ -957,11 +439,11 @@ static const struct display_ops fbdev_display_ops = {
.set_dpms = display_set_dpms,
.use = NULL,
.swap = display_swap,
.blit = display_blit,
.blend = display_blend,
.blendv = display_fake_blendv,
.fake_blendv = display_fake_blendv,
.fill = display_fill,
.blit = uterm_fbdev_display_blit,
.blend = uterm_fbdev_display_blend,
.blendv = uterm_fbdev_display_fake_blendv,
.fake_blendv = uterm_fbdev_display_fake_blendv,
.fill = uterm_fbdev_display_fill,
};
static void intro_idle_event(struct ev_eloop *eloop, void *unused, void *data)