71,8 → 71,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; |
|
99,10 → 104,22 |
unsigned int cols; |
unsigned int rows; |
|
/* Style and glyphs for text printing */ |
/* |
* Style and glyphs for text printing |
*/ |
|
/** Current style. */ |
style_t style; |
|
/** 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; |
139,6 → 156,15 |
|
static bool client_connected = false; /**< Allow only 1 connection */ |
|
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); |
|
|
#define RED(x, bits) ((x >> (8 + 8 + 8 - bits)) & ((1 << bits) - 1)) |
#define GREEN(x, bits) ((x >> (8 + 8 - bits)) & ((1 << bits) - 1)) |
#define BLUE(x, bits) ((x >> (8 - bits)) & ((1 << bits) - 1)) |
219,8 → 245,42 |
= ~((RED(rgb, 3) << 5) | (GREEN(rgb, 2) << 3) | BLUE(rgb, 3)); |
} |
|
/** Draw a filled rectangle. |
* |
* @note Need real implementation that does not access VRAM twice. |
*/ |
static void draw_filled_rect(unsigned int x0, unsigned int y0, unsigned int x1, |
unsigned int y1, uint32_t color) |
{ |
unsigned int x, y; |
unsigned int copy_bytes; |
|
/** Redraw viewport |
uint8_t *sp, *dp; |
uint8_t cbuf[4]; |
|
if (y0 >= y1 || x0 >= x1) return; |
screen.rgb_conv(cbuf, color); |
|
sp = &screen.fb_addr[FB_POS(x0, y0)]; |
dp = sp; |
|
/* Draw the first line. */ |
for (x = x0; x < x1; x++) { |
memcpy(dp, cbuf, screen.pixelbytes); |
dp += screen.pixelbytes; |
} |
|
dp = sp + screen.scanline; |
copy_bytes = (x1 - x0) * screen.pixelbytes; |
|
/* Draw the remaining lines by copying. */ |
for (y = y0 + 1; y < y1; y++) { |
memcpy(dp, sp, copy_bytes); |
dp += screen.scanline; |
} |
} |
|
/** Redraw viewport. |
* |
* @param vport Viewport to redraw |
* |
227,48 → 287,31 |
*/ |
static void vport_redraw(viewport_t *vport) |
{ |
unsigned int row; |
|
unsigned int row, col; |
|
for (row = 0; row < vport->rows; row++) { |
unsigned int y = vport->y + ROW2Y(row); |
unsigned int yd; |
|
for (yd = 0; yd < FONT_SCANLINES; yd++) { |
unsigned int x; |
unsigned int col; |
|
for (col = 0, x = vport->x; col < vport->cols; col++, x += FONT_WIDTH) |
memcpy(&screen.fb_addr[FB_POS(x, y + yd)], |
&vport->glyphs[GLYPH_POS(vport->backbuf[BB_POS(vport, col, row)], yd, false)], |
screen.glyphscanline); |
for (col = 0; col < vport->cols; col++) { |
draw_vp_glyph(vport, false, col, row); |
} |
} |
|
|
if (COL2X(vport->cols) < vport->width) { |
unsigned int y; |
|
for (y = 0; y < vport->height; y++) { |
unsigned int x; |
|
for (x = COL2X(vport->cols); x < vport->width; x++) |
memcpy(&screen.fb_addr[FB_POS(x, y)], vport->bgpixel, screen.pixelbytes); |
} |
draw_filled_rect( |
vport->x + COL2X(vport->cols), vport->y, |
vport->x + vport->width, vport->y + vport->height, |
vport->style.bg_color); |
} |
|
|
if (ROW2Y(vport->rows) < vport->height) { |
unsigned int y; |
|
for (y = ROW2Y(vport->rows); y < vport->height; y++) { |
unsigned int x; |
|
for (x = 0; x < vport->width; x++) |
memcpy(&screen.fb_addr[FB_POS(x, y)], vport->bgpixel, screen.pixelbytes); |
} |
draw_filled_rect( |
vport->x, vport->y + ROW2Y(vport->rows), |
vport->x + vport->width, vport->y + vport->height, |
vport->style.bg_color); |
} |
} |
|
|
/** Clear viewport |
/** Clear viewport. |
* |
* @param vport Viewport to clear |
* |
279,8 → 322,7 |
vport_redraw(vport); |
} |
|
|
/** Scroll viewport by given number of lines |
/** Scroll viewport by the specified number of lines. |
* |
* @param vport Viewport to scroll |
* @param lines Number of lines to scroll |
288,43 → 330,52 |
*/ |
static void vport_scroll(viewport_t *vport, int lines) |
{ |
unsigned int row; |
|
unsigned int row, col; |
unsigned int x, y; |
uint8_t glyph; |
|
/* |
* Redraw. |
*/ |
|
y = vport->y; |
for (row = 0; row < vport->rows; row++) { |
unsigned int y = vport->y + ROW2Y(row); |
unsigned int yd; |
|
for (yd = 0; yd < FONT_SCANLINES; yd++) { |
unsigned int x; |
unsigned int col; |
|
for (col = 0, x = vport->x; col < vport->cols; col++, x += FONT_WIDTH) { |
uint8_t glyph; |
|
if ((row + lines >= 0) && (row + lines < vport->rows)) { |
if (vport->backbuf[BB_POS(vport, col, row)] == vport->backbuf[BB_POS(vport, col, row + lines)]) |
continue; |
|
glyph = vport->backbuf[BB_POS(vport, col, row + lines)]; |
} else |
glyph = 0; |
|
memcpy(&screen.fb_addr[FB_POS(x, y + yd)], |
&vport->glyphs[GLYPH_POS(glyph, yd, false)], screen.glyphscanline); |
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)]; |
|
if (vport->backbuf[BB_POS(vport, col, row)] == glyph) { |
x += FONT_WIDTH; |
continue; |
} |
} else { |
glyph = 0; |
} |
|
(*vport->dglyph)(x, y, false, vport->glyphs, glyph, |
vport->style.fg_color, vport->style.bg_color); |
x += FONT_WIDTH; |
} |
y += FONT_SCANLINES; |
} |
|
|
/* |
* Scroll backbuffer. |
*/ |
|
if (lines > 0) { |
memcpy(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); |
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); |
} else { |
memcpy(vport->backbuf - vport->cols * lines, vport->backbuf, vport->cols * (vport->rows + lines)); |
memmove(vport->backbuf - vport->cols * lines, vport->backbuf, |
vport->cols * (vport->rows + lines)); |
memset(vport->backbuf, 0, - vport->cols * lines); |
} |
} |
|
|
/** Render glyphs |
* |
* Convert glyphs from device independent font |
346,18 → 397,11 |
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); |
} |
} |
} |
392,6 → 436,7 |
unsigned int rows = height / FONT_SCANLINES; |
unsigned int bbsize = cols * rows; |
unsigned int glyphsize = 2 * FONT_GLYPHS * screen.glyphbytes; |
unsigned int word_size = sizeof(unsigned long); |
|
uint8_t *backbuf = (uint8_t *) malloc(bbsize); |
if (!backbuf) |
427,7 → 472,24 |
|
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; |
504,8 → 566,136 |
} |
|
|
/** Draw glyph at given position relative to viewport |
/** Draw a glyph, takes advantage of alignment. |
* |
* 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_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 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 |
* @param cursor Draw glyph with cursor |
* @param col Screen position relative to viewport |
512,20 → 702,19 |
* @param row Screen position relative to viewport |
* |
*/ |
static void draw_glyph(viewport_t *vport, bool cursor, unsigned int col, unsigned int row) |
static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col, |
unsigned int row) |
{ |
unsigned int x = vport->x + COL2X(col); |
unsigned int y = vport->y + ROW2Y(row); |
unsigned int yd; |
uint8_t glyph; |
|
uint8_t glyph = vport->backbuf[BB_POS(vport, col, row)]; |
|
for (yd = 0; yd < FONT_SCANLINES; yd++) |
memcpy(&screen.fb_addr[FB_POS(x, y + yd)], |
&vport->glyphs[GLYPH_POS(glyph, yd, cursor)], screen.glyphscanline); |
glyph = vport->backbuf[BB_POS(vport, col, row)]; |
|
(*vport->dglyph)(x, y, cursor, vport->glyphs, glyph, |
vport->style.fg_color, vport->style.bg_color); |
} |
|
|
/** Hide cursor if it is shown |
* |
*/ |
532,7 → 721,7 |
static void cursor_hide(viewport_t *vport) |
{ |
if ((vport->cursor_active) && (vport->cursor_shown)) { |
draw_glyph(vport, false, vport->cur_col, vport->cur_row); |
draw_vp_glyph(vport, false, vport->cur_col, vport->cur_row); |
vport->cursor_shown = false; |
} |
} |
545,7 → 734,7 |
{ |
/* Do not check for cursor_shown */ |
if (vport->cursor_active) { |
draw_glyph(vport, true, vport->cur_col, vport->cur_row); |
draw_vp_glyph(vport, true, vport->cur_col, vport->cur_row); |
vport->cursor_shown = true; |
} |
} |
579,7 → 768,7 |
cursor_hide(vport); |
|
vport->backbuf[BB_POS(vport, col, row)] = c; |
draw_glyph(vport, false, col, row); |
draw_vp_glyph(vport, false, col, row); |
|
vport->cur_col = col; |
vport->cur_row = row; |
616,7 → 805,7 |
|
if (glyph != data[i].character) { |
vport->backbuf[BB_POS(vport, col, row)] = data[i].character; |
draw_glyph(vport, false, col, row); |
draw_vp_glyph(vport, false, col, row); |
} |
} |
cursor_show(vport); |
930,7 → 1119,7 |
if ((pointer_shown) || (!pointer_enabled)) |
return; |
|
/* Save image under the cursor */ |
/* Save image under the pointer. */ |
if (pointer_vport == -1) { |
pointer_vport = vport_create(pointer_x, pointer_y, pointer_width, pointer_height); |
if (pointer_vport < 0) |
945,7 → 1134,7 |
else |
copy_vp_to_pixmap(&viewports[pointer_vport], &pixmaps[pointer_pixmap]); |
|
/* Draw cursor */ |
/* Draw mouse pointer. */ |
for (i = 0; i < pointer_height; i++) |
for (j = 0; j < pointer_width; j++) { |
bytepos = i * ((pointer_width - 1) / 8 + 1) + j / 8; |
966,7 → 1155,7 |
|
static void mouse_hide(void) |
{ |
/* Restore image under the cursor */ |
/* Restore image under the pointer. */ |
if (pointer_shown) { |
draw_pixmap(pointer_vport, pointer_pixmap); |
pointer_shown = 0; |
1291,7 → 1480,6 |
case FB_SET_STYLE: |
vport->style.fg_color = IPC_GET_ARG1(call); |
vport->style.bg_color = IPC_GET_ARG2(call); |
render_glyphs(vport); |
retval = EOK; |
break; |
case FB_GET_RESOLUTION: |