Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4337 → Rev 4338

/branches/dynload/uspace/srv/fb/fb.c
51,6 → 51,8
#include <ipc/services.h>
#include <kernel/errno.h>
#include <kernel/genarch/fb/visuals.h>
#include <console/color.h>
#include <console/style.h>
#include <async.h>
#include <bool.h>
 
71,8 → 73,13
#define MAX_PIXMAPS 256 /**< Maximum number of saved pixmaps */
#define MAX_VIEWPORTS 128 /**< Viewport is a rectangular area on the screen */
 
/** Function to render a pixel from a RGB value. */
typedef void (*rgb_conv_t)(void *, uint32_t);
 
/** Function to draw a glyph. */
typedef void (*dg_t)(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph, uint32_t fg_color, uint32_t bg_color);
 
struct {
uint8_t *fb_addr;
88,7 → 95,14
rgb_conv_t rgb_conv;
} screen;
 
/** Backbuffer character cell. */
typedef struct {
uint8_t glyph;
uint32_t fg_color;
uint32_t bg_color;
} bb_cell_t;
 
typedef struct {
bool initialized;
unsigned int x;
unsigned int y;
99,10 → 113,22
unsigned int cols;
unsigned int rows;
/* Style and glyphs for text printing */
style_t style;
/*
* Style and glyphs for text printing
*/
 
/** Current attributes. */
attr_rgb_t attr;
 
/** Pre-rendered mask for rendering glyphs. Different viewports
* might use different drawing functions depending on whether their
* scanlines are aligned on a word boundary.*/
uint8_t *glyphs;
 
uint8_t *bgpixel;
 
/** Glyph drawing function for this viewport. */
dg_t dglyph;
/* Auto-cursor position */
bool cursor_active;
111,8 → 137,8
bool cursor_shown;
/* Back buffer */
bb_cell_t *backbuf;
unsigned int bbsize;
uint8_t *backbuf;
} viewport_t;
 
typedef struct {
139,8 → 165,38
 
static bool client_connected = false; /**< Allow only 1 connection */
 
static void draw_glyph(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph);
static uint32_t color_table[16] = {
[COLOR_BLACK] = 0x000000,
[COLOR_BLUE] = 0x0000f0,
[COLOR_GREEN] = 0x00f000,
[COLOR_CYAN] = 0x00f0f0,
[COLOR_RED] = 0xf00000,
[COLOR_MAGENTA] = 0xf000f0,
[COLOR_YELLOW] = 0xf0f000,
[COLOR_WHITE] = 0xf0f0f0,
 
[8 + COLOR_BLACK] = 0x000000,
[8 + COLOR_BLUE] = 0x0000ff,
[8 + COLOR_GREEN] = 0x00ff00,
[8 + COLOR_CYAN] = 0x00ffff,
[8 + COLOR_RED] = 0xff0000,
[8 + COLOR_MAGENTA] = 0xff00ff,
[8 + COLOR_YELLOW] = 0xffff00,
[8 + COLOR_WHITE] = 0xffffff,
};
 
static int rgb_from_style(attr_rgb_t *rgb, int style);
static int rgb_from_idx(attr_rgb_t *rgb, ipcarg_t fg_color,
ipcarg_t bg_color, ipcarg_t flags);
 
static int fb_set_color(viewport_t *vport, ipcarg_t fg_color,
ipcarg_t bg_color, ipcarg_t attr);
 
static void draw_glyph_aligned(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph, uint32_t fg_color, uint32_t bg_color);
static void draw_glyph_fallback(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph, uint32_t fg_color, uint32_t bg_color);
 
static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col,
unsigned int row);
 
279,7 → 335,7
draw_filled_rect(
vport->x + COL2X(vport->cols), vport->y,
vport->x + vport->width, vport->y + vport->height,
vport->style.bg_color);
vport->attr.bg_color);
}
 
if (ROW2Y(vport->rows) < vport->height) {
286,11 → 342,22
draw_filled_rect(
vport->x, vport->y + ROW2Y(vport->rows),
vport->x + vport->width, vport->y + vport->height,
vport->style.bg_color);
vport->attr.bg_color);
}
}
 
static void backbuf_clear(bb_cell_t *backbuf, size_t len, uint32_t fg_color,
uint32_t bg_color)
{
unsigned i;
 
for (i = 0; i < len; i++) {
backbuf[i].glyph = 0;
backbuf[i].fg_color = fg_color;
backbuf[i].bg_color = bg_color;
}
}
 
/** Clear viewport.
*
* @param vport Viewport to clear
298,7 → 365,8
*/
static void vport_clear(viewport_t *vport)
{
memset(vport->backbuf, 0, vport->bbsize);
backbuf_clear(vport->backbuf, vport->cols * vport->rows,
vport->attr.fg_color, vport->attr.bg_color);
vport_redraw(vport);
}
 
313,6 → 381,9
unsigned int row, col;
unsigned int x, y;
uint8_t glyph;
uint32_t fg_color;
uint32_t bg_color;
bb_cell_t *bbp, *xbp;
 
/*
* Redraw.
323,17 → 394,27
x = vport->x;
for (col = 0; col < vport->cols; col++) {
if ((row + lines >= 0) && (row + lines < vport->rows)) {
glyph = vport->backbuf[BB_POS(vport, col, row + lines)];
xbp = &vport->backbuf[BB_POS(vport, col, row + lines)];
bbp = &vport->backbuf[BB_POS(vport, col, row)];
 
if (vport->backbuf[BB_POS(vport, col, row)] == glyph) {
glyph = xbp->glyph;
fg_color = xbp->fg_color;
bg_color = xbp->bg_color;
 
if (bbp->glyph == glyph &&
bbp->fg_color == xbp->fg_color &&
bbp->bg_color == xbp->bg_color) {
x += FONT_WIDTH;
continue;
}
} else {
glyph = 0;
fg_color = vport->attr.fg_color;
bg_color = vport->attr.bg_color;
}
 
draw_glyph(x, y, false, vport->glyphs, glyph);
(*vport->dglyph)(x, y, false, vport->glyphs, glyph,
fg_color, bg_color);
x += FONT_WIDTH;
}
y += FONT_SCANLINES;
345,13 → 426,14
 
if (lines > 0) {
memmove(vport->backbuf, vport->backbuf + vport->cols * lines,
vport->cols * (vport->rows - lines));
memset(&vport->backbuf[BB_POS(vport, 0, vport->rows - lines)],
0, vport->cols * lines);
vport->cols * (vport->rows - lines) * sizeof(bb_cell_t));
backbuf_clear(&vport->backbuf[BB_POS(vport, 0, vport->rows - lines)],
vport->cols * lines, vport->attr.fg_color, vport->attr.bg_color);
} else {
memmove(vport->backbuf - vport->cols * lines, vport->backbuf,
vport->cols * (vport->rows + lines));
memset(vport->backbuf, 0, - vport->cols * lines);
vport->cols * (vport->rows + lines) * sizeof(bb_cell_t));
backbuf_clear(vport->backbuf, - vport->cols * lines,
vport->attr.fg_color, vport->attr.bg_color);
}
}
 
376,23 → 458,16
for (x = 0; x < FONT_WIDTH; x++) {
screen.rgb_conv(&vport->glyphs[GLYPH_POS(glyph, y, false) + x * screen.pixelbytes],
(fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x)))
? vport->style.fg_color : vport->style.bg_color);
? 0xffffff : 0x000000);
uint32_t curcolor;
if (y < FONT_SCANLINES - 2)
curcolor =
(fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x)))
? vport->style.fg_color : vport->style.bg_color;
else
curcolor = vport->style.fg_color;
screen.rgb_conv(&vport->glyphs[GLYPH_POS(glyph, y, true) + x * screen.pixelbytes], curcolor);
screen.rgb_conv(&vport->glyphs[GLYPH_POS(glyph, y, true) + x * screen.pixelbytes],
(fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x)))
? 0x000000 : 0xffffff);
}
}
}
screen.rgb_conv(vport->bgpixel, vport->style.bg_color);
screen.rgb_conv(vport->bgpixel, vport->attr.bg_color);
}
 
 
420,10 → 495,11
unsigned int cols = width / FONT_WIDTH;
unsigned int rows = height / FONT_SCANLINES;
unsigned int bbsize = cols * rows;
unsigned int bbsize = cols * rows * sizeof(bb_cell_t);
unsigned int glyphsize = 2 * FONT_GLYPHS * screen.glyphbytes;
unsigned int word_size = sizeof(unsigned long);
uint8_t *backbuf = (uint8_t *) malloc(bbsize);
bb_cell_t *backbuf = (bb_cell_t *) malloc(bbsize);
if (!backbuf)
return ENOMEM;
439,8 → 515,8
free(backbuf);
return ENOMEM;
}
memset(backbuf, 0, bbsize);
 
backbuf_clear(backbuf, cols * rows, DEFAULT_FGCOLOR, DEFAULT_BGCOLOR);
memset(glyphs, 0, glyphsize);
memset(bgpixel, 0, screen.pixelbytes);
452,12 → 528,29
viewports[i].cols = cols;
viewports[i].rows = rows;
viewports[i].style.bg_color = DEFAULT_BGCOLOR;
viewports[i].style.fg_color = DEFAULT_FGCOLOR;
viewports[i].attr.bg_color = DEFAULT_BGCOLOR;
viewports[i].attr.fg_color = DEFAULT_FGCOLOR;
viewports[i].glyphs = glyphs;
viewports[i].bgpixel = bgpixel;
 
/*
* Conditions necessary to select aligned version:
*
* - word size is divisible by pixelbytes
* - cell scanline size is divisible by word size
* - cell scanlines are word-aligned
*/
if ((word_size % screen.pixelbytes) == 0 &&
(FONT_WIDTH * screen.pixelbytes) % word_size == 0 &&
(x * screen.pixelbytes) % word_size == 0 &&
screen.scanline % word_size == 0) {
 
viewports[i].dglyph = draw_glyph_aligned;
} else {
viewports[i].dglyph = draw_glyph_fallback;
}
 
viewports[i].cur_col = 0;
viewports[i].cur_row = 0;
viewports[i].cursor_active = false;
534,25 → 627,134
}
 
 
/** Draw a glyph.
/** Draw a glyph, takes advantage of alignment.
*
* @param x x coordinate of top-left corner on screen.
* @param y y coordinate of top-left corner on screen.
* @param cursor Draw glyph with cursor
* @param glyphs Pointer to font bitmap.
* @param glyph Code of the glyph to draw.
* This version can only be used if the following conditions are met:
*
* - word size is divisible by pixelbytes
* - cell scanline size is divisible by word size
* - cell scanlines are word-aligned
*
* It makes use of the pre-rendered mask to process (possibly) several
* pixels at once (word size / pixelbytes pixels at a time are processed)
* making it very fast. Most notably this version is not applicable at 24 bits
* per pixel.
*
* @param x x coordinate of top-left corner on screen.
* @param y y coordinate of top-left corner on screen.
* @param cursor Draw glyph with cursor
* @param glyphs Pointer to font bitmap.
* @param glyph Code of the glyph to draw.
* @param fg_color Foreground color.
* @param bg_color Backgroudn color.
*/
static void draw_glyph(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph)
static void draw_glyph_aligned(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph, uint32_t fg_color, uint32_t bg_color)
{
unsigned int yd;
for (yd = 0; yd < FONT_SCANLINES; yd++)
memcpy(&screen.fb_addr[FB_POS(x, y + yd)],
&glyphs[GLYPH_POS(glyph, yd, cursor)], screen.glyphscanline);
unsigned int i, yd;
unsigned long fg_buf, bg_buf;
unsigned long *maskp, *dp;
unsigned long mask;
unsigned int ww, d_add;
 
/*
* Prepare a pair of words, one filled with foreground-color
* pattern and the other filled with background-color pattern.
*/
for (i = 0; i < sizeof(unsigned long) / screen.pixelbytes; i++) {
screen.rgb_conv(&((uint8_t *)&fg_buf)[i * screen.pixelbytes],
fg_color);
screen.rgb_conv(&((uint8_t *)&bg_buf)[i * screen.pixelbytes],
bg_color);
}
 
 
/* Pointer to the current position in the mask. */
maskp = (unsigned long *) &glyphs[GLYPH_POS(glyph, 0, cursor)];
 
/* Pointer to the current position on the screen. */
dp = (unsigned long *) &screen.fb_addr[FB_POS(x, y)];
 
/* Width of the character cell in words. */
ww = FONT_WIDTH * screen.pixelbytes / sizeof(unsigned long);
 
/* Offset to add when moving to another screen scanline. */
d_add = screen.scanline - FONT_WIDTH * screen.pixelbytes;
 
for (yd = 0; yd < FONT_SCANLINES; yd++) {
/*
* Now process the cell scanline, combining foreground
* and background color patters using the pre-rendered mask.
*/
for (i = 0; i < ww; i++) {
mask = *maskp++;
*dp++ = (fg_buf & mask) | (bg_buf & ~mask);
}
 
/* Move to the beginning of the next scanline of the cell. */
dp = (unsigned long *) ((uint8_t *) dp + d_add);
}
}
 
/** Draw a glyph, fallback version.
*
* This version does not make use of the pre-rendered mask, it uses
* the font bitmap directly. It works always, but it is slower.
*
* @param x x coordinate of top-left corner on screen.
* @param y y coordinate of top-left corner on screen.
* @param cursor Draw glyph with cursor
* @param glyphs Pointer to font bitmap.
* @param glyph Code of the glyph to draw.
* @param fg_color Foreground color.
* @param bg_color Backgroudn color.
*/
void draw_glyph_fallback(unsigned int x, unsigned int y, bool cursor,
uint8_t *glyphs, uint8_t glyph, uint32_t fg_color, uint32_t bg_color)
{
unsigned int i, j, yd;
uint8_t fg_buf[4], bg_buf[4];
uint8_t *dp, *sp;
unsigned int d_add;
uint8_t b;
 
/* Pre-render 1x the foreground and background color pixels. */
if (cursor) {
screen.rgb_conv(fg_buf, bg_color);
screen.rgb_conv(bg_buf, fg_color);
} else {
screen.rgb_conv(fg_buf, fg_color);
screen.rgb_conv(bg_buf, bg_color);
}
 
/* Pointer to the current position on the screen. */
dp = (uint8_t *) &screen.fb_addr[FB_POS(x, y)];
 
/* Offset to add when moving to another screen scanline. */
d_add = screen.scanline - FONT_WIDTH * screen.pixelbytes;
 
for (yd = 0; yd < FONT_SCANLINES; yd++) {
/* Byte containing bits of the glyph scanline. */
b = fb_font[glyph * FONT_SCANLINES + yd];
 
for (i = 0; i < FONT_WIDTH; i++) {
/* Choose color based on the current bit. */
sp = (b & 0x80) ? fg_buf : bg_buf;
 
/* Copy the pixel. */
for (j = 0; j < screen.pixelbytes; j++) {
*dp++ = *sp++;
}
 
/* Move to the next bit. */
b = b << 1;
}
/* Move to the beginning of the next scanline of the cell. */
dp += d_add;
}
}
 
/** Draw glyph at specified position in viewport.
*
* @param vport Viewport identification
566,13 → 768,19
{
unsigned int x = vport->x + COL2X(col);
unsigned int y = vport->y + ROW2Y(row);
 
uint8_t glyph;
uint32_t fg_color;
uint32_t bg_color;
glyph = vport->backbuf[BB_POS(vport, col, row)];
draw_glyph(x, y, cursor, vport->glyphs, glyph);
glyph = vport->backbuf[BB_POS(vport, col, row)].glyph;
fg_color = vport->backbuf[BB_POS(vport, col, row)].fg_color;
bg_color = vport->backbuf[BB_POS(vport, col, row)].bg_color;
 
(*vport->dglyph)(x, y, cursor, vport->glyphs, glyph,
fg_color, bg_color);
}
 
 
/** Hide cursor if it is shown
*
*/
620,12 → 828,18
*/
static void draw_char(viewport_t *vport, uint8_t c, unsigned int col, unsigned int row)
{
bb_cell_t *bbp;
 
/* Do not hide cursor if we are going to overwrite it */
if ((vport->cursor_active) && (vport->cursor_shown) &&
((vport->cur_col != col) || (vport->cur_row != row)))
cursor_hide(vport);
vport->backbuf[BB_POS(vport, col, row)] = c;
 
bbp = &vport->backbuf[BB_POS(vport, col, row)];
bbp->glyph = c;
bbp->fg_color = vport->attr.fg_color;
bbp->bg_color = vport->attr.bg_color;
 
draw_vp_glyph(vport, false, col, row);
vport->cur_col = col;
652,17 → 866,37
static void draw_text_data(viewport_t *vport, keyfield_t *data)
{
unsigned int i;
bb_cell_t *bbp;
attrs_t *a;
attr_rgb_t rgb;
for (i = 0; i < vport->cols * vport->rows; i++) {
unsigned int col = i % vport->cols;
unsigned int row = i / vport->cols;
uint8_t glyph = vport->backbuf[BB_POS(vport, col, row)];
// TODO: use data[i].style
bbp = &vport->backbuf[BB_POS(vport, col, row)];
uint8_t glyph = bbp->glyph;
 
if (glyph != data[i].character) {
vport->backbuf[BB_POS(vport, col, row)] = data[i].character;
bbp->glyph = data[i].character;
a = &data[i].attrs;
 
switch (a->t) {
case at_style:
rgb_from_style(&rgb, a->a.s.style);
break;
case at_idx:
rgb_from_idx(&rgb, a->a.i.fg_color,
a->a.i.bg_color, a->a.i.flags);
break;
case at_rgb:
rgb = a->a.r;
break;
}
 
bbp->fg_color = rgb.fg_color;
bbp->bg_color = rgb.bg_color;
 
draw_vp_glyph(vport, false, col, row);
}
}
1180,6 → 1414,47
}
 
static int rgb_from_style(attr_rgb_t *rgb, int style)
{
switch (style) {
case STYLE_NORMAL:
rgb->fg_color = color_table[COLOR_BLACK];
rgb->bg_color = color_table[COLOR_WHITE];
break;
case STYLE_EMPHASIS:
rgb->fg_color = color_table[COLOR_RED];
rgb->bg_color = color_table[COLOR_WHITE];
break;
default:
return EINVAL;
}
 
return EOK;
}
 
static int rgb_from_idx(attr_rgb_t *rgb, ipcarg_t fg_color,
ipcarg_t bg_color, ipcarg_t flags)
{
fg_color = (fg_color & 7) | ((flags & CATTR_BRIGHT) ? 8 : 0);
bg_color = (bg_color & 7) | ((flags & CATTR_BRIGHT) ? 8 : 0);
 
rgb->fg_color = color_table[fg_color];
rgb->bg_color = color_table[bg_color];
 
return EOK;
}
 
static int fb_set_style(viewport_t *vport, ipcarg_t style)
{
return rgb_from_style(&vport->attr, (int) style);
}
 
static int fb_set_color(viewport_t *vport, ipcarg_t fg_color,
ipcarg_t bg_color, ipcarg_t flags)
{
return rgb_from_idx(&vport->attr, fg_color, bg_color, flags);
}
 
/** Function for handling connections to FB
*
*/
1336,9 → 1611,15
retval = EOK;
break;
case FB_SET_STYLE:
vport->style.fg_color = IPC_GET_ARG1(call);
vport->style.bg_color = IPC_GET_ARG2(call);
render_glyphs(vport);
retval = fb_set_style(vport, IPC_GET_ARG1(call));
break;
case FB_SET_COLOR:
retval = fb_set_color(vport, IPC_GET_ARG1(call),
IPC_GET_ARG2(call), IPC_GET_ARG3(call));
break;
case FB_SET_RGB_COLOR:
vport->attr.fg_color = IPC_GET_ARG1(call);
vport->attr.bg_color = IPC_GET_ARG2(call);
retval = EOK;
break;
case FB_GET_RESOLUTION: