Index: stand/common/gfx_fb.h =================================================================== --- stand/common/gfx_fb.h +++ stand/common/gfx_fb.h @@ -210,14 +210,13 @@ teken_t tg_teken; /* Teken core */ teken_pos_t tg_cursor; /* Where cursor was drawn */ bool tg_cursor_visible; - uint8_t *tg_cursor_image; /* Memory for cursor */ - size_t tg_cursor_size; teken_pos_t tg_tp; /* Terminal dimensions */ teken_pos_t tg_origin; /* Point of origin in pixels */ uint8_t *tg_glyph; /* Memory for glyph */ size_t tg_glyph_size; struct vt_font tg_font; struct gen_fb tg_fb; + uint32_t *tg_shadow_fb; /* units of 4 bytes */ teken_funcs_t *tg_functions; void *tg_private; bool tg_kernel_supported; /* Loaded kernel is supported */ Index: stand/common/gfx_fb.c =================================================================== --- stand/common/gfx_fb.c +++ stand/common/gfx_fb.c @@ -741,6 +741,38 @@ return (0); } +static void +gfxfb_shadow_fill(uint32_t *BltBuffer, + uint32_t DestinationX, uint32_t DestinationY, + uint32_t Width, uint32_t Height) +{ + uint32_t fbX, fbY; + + if (gfx_state.tg_shadow_fb == NULL) + return; + + fbX = gfx_state.tg_fb.fb_width; + fbY = gfx_state.tg_fb.fb_height; + + if (BltBuffer == NULL) + return; + + if (DestinationX + Width > fbX) + Width = fbX - DestinationX; + + if (DestinationY + Height > fbY) + Height = fbY - DestinationY; + + uint32_t y2 = Height + DestinationY; + for (uint32_t y1 = DestinationY; y1 < y2; y1++) { + uint32_t off = y1 * fbX + DestinationX; + + for (uint32_t x = 0; x < Width; x++) { + gfx_state.tg_shadow_fb[off + x] = *BltBuffer; + } + } +} + int gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation, uint32_t SourceX, uint32_t SourceY, @@ -764,6 +796,8 @@ tpl = BS->RaiseTPL(TPL_NOTIFY); switch (BltOperation) { case GfxFbBltVideoFill: + gfxfb_shadow_fill(BltBuffer, DestinationX, + DestinationY, Width, Height); status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); @@ -815,6 +849,8 @@ switch (BltOperation) { case GfxFbBltVideoFill: + gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY, + Width, Height); rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY, Width, Height); break; @@ -984,7 +1020,6 @@ static void gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on) { - unsigned x, y, width, height; const uint8_t *glyph; teken_pos_t p; int idx; @@ -998,42 +1033,6 @@ if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; - width = state->tg_font.vf_width; - height = state->tg_font.vf_height; - x = state->tg_origin.tp_col + p.tp_col * width; - y = state->tg_origin.tp_row + p.tp_row * height; - - /* - * Save original display content to preserve image data. - */ - if (on) { - if (state->tg_cursor_image == NULL || - state->tg_cursor_size != width * height * 4) { - free(state->tg_cursor_image); - state->tg_cursor_size = width * height * 4; - state->tg_cursor_image = malloc(state->tg_cursor_size); - } - if (state->tg_cursor_image != NULL) { - if (gfxfb_blt(state->tg_cursor_image, - GfxFbBltVideoToBltBuffer, x, y, 0, 0, - width, height, 0) != 0) { - free(state->tg_cursor_image); - state->tg_cursor_image = NULL; - } - } - } else { - /* - * Restore display from tg_cursor_image. - * If there is no image, restore char from screen_buffer. - */ - if (state->tg_cursor_image != NULL && - gfxfb_blt(state->tg_cursor_image, GfxFbBltBufferToVideo, - 0, 0, x, y, width, height, 0) == 0) { - state->tg_cursor = p; - return; - } - } - glyph = font_lookup(&state->tg_font, screen_buffer[idx].c, &screen_buffer[idx].a); gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on); @@ -1110,19 +1109,62 @@ const teken_pos_t *d) { uint32_t sx, sy, dx, dy, width, height; + uint32_t pitch, bytes; + int step; width = state->tg_font.vf_width; height = state->tg_font.vf_height; - sx = state->tg_origin.tp_col + s->tr_begin.tp_col * width; - sy = state->tg_origin.tp_row + s->tr_begin.tp_row * height; - dx = state->tg_origin.tp_col + d->tp_col * width; - dy = state->tg_origin.tp_row + d->tp_row * height; + sx = s->tr_begin.tp_col * width; + sy = s->tr_begin.tp_row * height; + dx = d->tp_col * width; + dy = d->tp_row * height; width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1); - (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, sx, sy, dx, dy, + /* + * With no shadow fb, use video to video copy. + */ + if (state->tg_shadow_fb == NULL) { + (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, + sx + state->tg_origin.tp_col, + sy + state->tg_origin.tp_row, + dx + state->tg_origin.tp_col, + dy + state->tg_origin.tp_row, width, height, 0); + return; + } + + /* + * With shadow fb, we need to copy data on both shadow and video, + * to preserve the consistency. We only read data from shadow fb. + */ + + step = 1; + pitch = state->tg_fb.fb_width; + bytes = width * sizeof (*state->tg_shadow_fb); + + /* + * To handle overlapping areas, set up reverse copy here. + */ + if (dy * pitch + dx > sy * pitch + sx) { + sy += height; + dy += height; + step = -step; + } + + while (height-- > 0) { + uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx]; + uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx]; + + bcopy(source, destination, bytes); + (void) gfxfb_blt(destination, GfxFbBltBufferToVideo, + 0, 0, dx + state->tg_origin.tp_col, + dy + state->tg_origin.tp_row, width, 1, 0); + + sy += step; + dy += step; + } } static void @@ -1303,13 +1345,32 @@ void *data) { #if defined(EFI) - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p; #else - struct paletteentry *buf; + struct paletteentry *buf, *p; #endif size_t size; - size = width * height * sizeof(*buf); + /* + * If we do have shadow fb, we will use shadow to render data, + * and copy shadow to video. + */ + if (gfx_state.tg_shadow_fb != NULL) { + uint32_t pitch = gfx_state.tg_fb.fb_width; + + /* Copy rectangle line by line. */ + p = data; + for (uint32_t sy = 0; sy < height; sy++) { + buf = (void *)(gfx_state.tg_shadow_fb + + (y - gfx_state.tg_origin.tp_row) * pitch + + x - gfx_state.tg_origin.tp_col); + bitmap_cpy(buf, &p[sy * width], width); + (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, + 0, 0, x, y, width, 1, 0); + y++; + } + return; + } /* * Common data to display is glyph, use preallocated @@ -1318,6 +1379,7 @@ if (gfx_state.tg_glyph_size != GlyphBufferSize) (void) allocate_glyphbuffer(width, height); + size = width * height * sizeof(*buf); if (size == GlyphBufferSize) buf = GlyphBuffer; else Index: stand/efi/loader/framebuffer.c =================================================================== --- stand/efi/loader/framebuffer.c +++ stand/efi/loader/framebuffer.c @@ -622,6 +622,9 @@ gfx_state->tg_fb.fb_bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green | efifb.fb_mask_blue | efifb.fb_mask_reserved); + free(gfx_state->tg_shadow_fb); + gfx_state->tg_shadow_fb = malloc(efifb.fb_height * efifb.fb_width * + sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); return (0); } Index: stand/i386/libi386/vbe.c =================================================================== --- stand/i386/libi386/vbe.c +++ stand/i386/libi386/vbe.c @@ -709,6 +709,10 @@ gfx_state.tg_fb.fb_width = mi.XResolution; gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel; + free(gfx_state.tg_shadow_fb); + gfx_state.tg_shadow_fb = malloc(mi.YResolution * mi.XResolution * + sizeof(struct paletteentry)); + /* Bytes per pixel */ bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;