diff --git a/commands/i386/pc/vbetest.c b/commands/i386/pc/vbetest.c index 3cbc301..314320d 100644 --- a/commands/i386/pc/vbetest.c +++ b/commands/i386/pc/vbetest.c @@ -155,6 +155,8 @@ grub_cmd_vbetest (grub_command_t cmd __attribute__ ((unused)), grub_getkey (); + grub_video_restore (); + /* Restore old video mode. */ grub_vbe_set_video_mode (old_mode, 0); diff --git a/commands/videotest.c b/commands/videotest.c index 6fe4b9b..c4b315a 100644 --- a/commands/videotest.c +++ b/commands/videotest.c @@ -160,6 +160,8 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), 0, 0, width, height); } + grub_video_swap_buffers (); + grub_getkey (); grub_video_delete_render_target (text_layer); diff --git a/conf/common.rmk b/conf/common.rmk index 032517f..5e93302 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -535,13 +535,18 @@ lua_mod_LDFLAGS = $(COMMON_LDFLAGS) # Common Video Subsystem specific modules. pkglib_MODULES += video.mod videotest.mod bitmap.mod tga.mod jpeg.mod \ - png.mod font.mod gfxterm.mod + png.mod font.mod gfxterm.mod video_fb.mod # For video.mod. video_mod_SOURCES = video/video.c video_mod_CFLAGS = $(COMMON_CFLAGS) video_mod_LDFLAGS = $(COMMON_LDFLAGS) +video_fb_mod_SOURCES = video/fb/video_fb.c video/fb/fbblit.c \ + video/fb/fbfill.c video/fb/fbutil.c +video_fb_mod_CFLAGS = $(COMMON_CFLAGS) +video_fb_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For videotest.mod. videotest_mod_SOURCES = commands/videotest.c videotest_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 798aee2..5b7b8c0 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -273,8 +273,7 @@ multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) multiboot_mod_ASFLAGS = $(COMMON_ASFLAGS) # For vbe.mod. -vbe_mod_SOURCES = video/i386/pc/vbe.c video/i386/pc/vbeblit.c \ - video/i386/pc/vbefill.c video/i386/pc/vbeutil.c +vbe_mod_SOURCES = video/i386/pc/vbe.c vbe_mod_CFLAGS = $(COMMON_CFLAGS) vbe_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/i386/pc/vbe.h b/include/grub/i386/pc/vbe.h index bd6ecd7..fb11678 100644 --- a/include/grub/i386/pc/vbe.h +++ b/include/grub/i386/pc/vbe.h @@ -19,10 +19,7 @@ #ifndef GRUB_VBE_MACHINE_HEADER #define GRUB_VBE_MACHINE_HEADER 1 -#include -#include -#include -#include +#include /* Default video mode to be used. */ #define GRUB_VBE_DEFAULT_VIDEO_MODE 0x101 @@ -224,54 +221,5 @@ grub_err_t grub_vbe_get_video_mode (grub_uint32_t *mode); grub_err_t grub_vbe_get_video_mode_info (grub_uint32_t mode, struct grub_vbe_mode_info_block *mode_info); -/* VBE module internal prototypes (should not be used from elsewhere). */ -struct grub_video_i386_vbeblit_info; - -struct grub_video_render_target -{ - /* Copy of the screen's mode info structure, except that width, height and - mode_type has been re-adjusted to requested render target settings. */ - struct grub_video_mode_info mode_info; - - struct - { - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; - } viewport; - - /* Indicates whether the data has been allocated by us and must be freed - when render target is destroyed. */ - int is_allocated; - - /* Pointer to data. Can either be in video card memory or in local host's - memory. */ - void *data; -}; - -grub_uint8_t * grub_video_vbe_get_video_ptr (struct grub_video_i386_vbeblit_info *source, - grub_uint32_t x, grub_uint32_t y); - -grub_video_color_t grub_video_vbe_map_rgb (grub_uint8_t red, grub_uint8_t green, - grub_uint8_t blue); - -grub_video_color_t grub_video_vbe_map_rgba (grub_uint8_t red, - grub_uint8_t green, - grub_uint8_t blue, - grub_uint8_t alpha); - -grub_err_t grub_video_vbe_unmap_color (grub_video_color_t color, - grub_uint8_t *red, - grub_uint8_t *green, - grub_uint8_t *blue, - grub_uint8_t *alpha); - -void grub_video_vbe_unmap_color_int (struct grub_video_i386_vbeblit_info *source, - grub_video_color_t color, - grub_uint8_t *red, - grub_uint8_t *green, - grub_uint8_t *blue, - grub_uint8_t *alpha); #endif /* ! GRUB_VBE_MACHINE_HEADER */ diff --git a/include/grub/video.h b/include/grub/video.h index c98731d..ad6dc6d 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -48,10 +48,10 @@ struct grub_video_bitmap; #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK 0x0000ff00 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS 8 -/* Defined predefined render targets. */ -#define GRUB_VIDEO_RENDER_TARGET_DISPLAY ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER ((struct grub_video_render_target *) 1) +/* The basic render target representing the whole display. This always + renders to the back buffer when double-buffering is in use. */ +#define GRUB_VIDEO_RENDER_TARGET_DISPLAY \ + ((struct grub_video_render_target *) 0) /* Defined blitting formats. */ enum grub_video_blit_format @@ -161,6 +161,16 @@ struct grub_video_palette_data grub_uint8_t a; /* Reserved bits value (0-255). */ }; +/* A 2D rectangle type. */ +struct grub_video_rect +{ + int x; + int y; + int width; + int height; +}; +typedef struct grub_video_rect grub_video_rect_t; + struct grub_video_adapter { /* The video adapter name. */ @@ -177,6 +187,9 @@ struct grub_video_adapter grub_err_t (*get_info) (struct grub_video_mode_info *mode_info); + grub_err_t (*get_info_and_fini) (struct grub_video_mode_info *mode_info, + void **framebuffer); + grub_err_t (*set_palette) (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); @@ -218,6 +231,8 @@ struct grub_video_adapter grub_err_t (*swap_buffers) (void); + grub_err_t (*enable_double_buffering) (int enable); + grub_err_t (*create_render_target) (struct grub_video_render_target **result, unsigned int width, unsigned int height, unsigned int mode_type); @@ -241,6 +256,14 @@ grub_err_t grub_video_restore (void); grub_err_t grub_video_get_info (struct grub_video_mode_info *mode_info); +/* Framebuffer address may change as a part of normal operation + (e.g. double buffering). That's why you need to stop video subsystem to be + sure that framebuffer address doesn't change. To ensure this abstraction + grub_video_get_info_and_fini is the only function supplying framebuffer + address. */ +grub_err_t grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuffer); + enum grub_video_blit_format grub_video_get_blit_format (struct grub_video_mode_info *mode_info); grub_err_t grub_video_set_palette (unsigned int start, unsigned int count, @@ -286,6 +309,8 @@ grub_err_t grub_video_scroll (grub_video_color_t color, int dx, int dy); grub_err_t grub_video_swap_buffers (void); +grub_err_t grub_video_enable_double_buffering (int enable); + grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, unsigned int width, unsigned int height, @@ -297,7 +322,7 @@ grub_err_t grub_video_set_active_render_target (struct grub_video_render_target grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target); -grub_err_t grub_video_set_mode (char *modestring, +grub_err_t grub_video_set_mode (const char *modestring, int NESTED_FUNC_ATTR (*hook) (grub_video_adapter_t p, struct grub_video_mode_info *mode_info)); diff --git a/include/grub/video_fb.h b/include/grub/video_fb.h new file mode 100644 index 0000000..9ae4101 --- /dev/null +++ b/include/grub/video_fb.h @@ -0,0 +1,137 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_VIDEO_FB_HEADER +#define GRUB_VIDEO_FB_HEADER 1 + +#include +#include +#include +#include + +/* FB module internal prototype (should not be used from elsewhere). */ + +struct grub_video_fbblit_info; + +struct grub_video_fbrender_target +{ + /* Copy of the screen's mode info structure, except that width, height and + mode_type has been re-adjusted to requested render target settings. */ + struct grub_video_mode_info mode_info; + + struct + { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + } viewport; + + /* Indicates whether the data has been allocated by us and must be freed + when render target is destroyed. */ + int is_allocated; + + /* Pointer to data. Can either be in video card memory or in local host's + memory. */ + void *data; +}; + +#define GRUB_VIDEO_FBSTD_NUMCOLORS 16 +extern struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_NUMCOLORS]; + +grub_uint8_t * grub_video_fb_get_video_ptr (struct grub_video_fbblit_info *source, + grub_uint32_t x, grub_uint32_t y); + +grub_err_t +grub_video_fb_init (void); + +grub_err_t +grub_video_fb_fini (void); + +grub_err_t +grub_video_fb_get_info (struct grub_video_mode_info *mode_info); + +grub_err_t +grub_video_fb_get_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); +grub_err_t +grub_video_fb_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); +grub_err_t +grub_video_fb_set_viewport (unsigned int x, unsigned int y, + unsigned int width, unsigned int height); +grub_err_t +grub_video_fb_get_viewport (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height); + +grub_video_color_t +grub_video_fb_map_color (grub_uint32_t color_name); + +grub_video_color_t +grub_video_fb_map_rgb (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue); + +grub_video_color_t +grub_video_fb_map_rgba (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue, grub_uint8_t alpha); + +grub_err_t +grub_video_fb_unmap_color (grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha); + +void +grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, + grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha); + +grub_err_t +grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height); + +grub_err_t +grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, int x, int y, + int offset_x, int offset_y, + unsigned int width, unsigned int height); + +grub_err_t +grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height); + +grub_err_t +grub_video_fb_scroll (grub_video_color_t color, int dx, int dy); + +grub_err_t +grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, + unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused))); + +grub_err_t +grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target); + +grub_err_t +grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **target); + +grub_err_t +grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target); + +#endif /* ! GRUB_VIDEO_FB_HEADER */ diff --git a/loader/i386/linux.c b/loader/i386/linux.c index 78fa848..9a7a8f7 100644 --- a/loader/i386/linux.c +++ b/loader/i386/linux.c @@ -31,9 +31,7 @@ #include #include #include -/* FIXME: the definition of `struct grub_video_render_target' is - VBE-specific. */ -#include +#include #include #define GRUB_LINUX_CL_OFFSET 0x1000 @@ -403,14 +401,11 @@ static int grub_linux_setup_video (struct linux_kernel_params *params) { struct grub_video_mode_info mode_info; - struct grub_video_render_target *render_target; + void *framebuffer; int ret; - ret = grub_video_get_info (&mode_info); - if (ret) - return 1; + ret = grub_video_get_info_and_fini (&mode_info, &framebuffer); - ret = grub_video_get_active_render_target (&render_target); if (ret) return 1; @@ -419,7 +414,7 @@ grub_linux_setup_video (struct linux_kernel_params *params) params->lfb_depth = mode_info.bpp; params->lfb_line_len = mode_info.pitch; - params->lfb_base = (grub_size_t) render_target->data; + params->lfb_base = (grub_size_t) framebuffer; params->lfb_size = (params->lfb_line_len * params->lfb_height + 65535) >> 16; params->red_mask_size = mode_info.red_mask_size; @@ -449,41 +444,6 @@ grub_linux_boot (void) params = real_mode_mem; - modevar = grub_env_get ("gfxpayload"); - - /* Now all graphical modes are acceptable. - May change in future if we have modes without framebuffer. */ - if (modevar && *modevar != 0) - { - tmp = grub_malloc (grub_strlen (modevar) - + sizeof (DEFAULT_VIDEO_MODE) + 1); - if (! tmp) - return grub_errno; - grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar); - err = grub_video_set_mode (tmp, 0); - grub_free (tmp); - } -#ifndef GRUB_ASSUME_LINUX_HAS_FB_SUPPORT - else - err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0); -#endif - - if (err) - { - grub_print_error (); - grub_printf ("Booting however\n"); - grub_errno = GRUB_ERR_NONE; - } - - if (! grub_linux_setup_video (params)) - params->have_vga = GRUB_VIDEO_TYPE_VLFB; - else - { - params->have_vga = GRUB_VIDEO_TYPE_TEXT; - params->video_width = 80; - params->video_height = 25; - } - grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n", (unsigned) params->code32_start, (unsigned long) &(idt_desc.limit), @@ -534,6 +494,41 @@ grub_linux_boot (void) grub_mmap_iterate (hook); params->mmap_size = e820_num; + modevar = grub_env_get ("gfxpayload"); + + /* Now all graphical modes are acceptable. + May change in future if we have modes without framebuffer. */ + if (modevar && *modevar != 0) + { + tmp = grub_malloc (grub_strlen (modevar) + + sizeof (DEFAULT_VIDEO_MODE) + 1); + if (! tmp) + return grub_errno; + grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar); + err = grub_video_set_mode (tmp, 0); + grub_free (tmp); + } +#ifndef GRUB_ASSUME_LINUX_HAS_FB_SUPPORT + else + err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0); +#endif + + if (err) + { + grub_print_error (); + grub_printf ("Booting however\n"); + grub_errno = GRUB_ERR_NONE; + } + + if (! grub_linux_setup_video (params)) + params->have_vga = GRUB_VIDEO_TYPE_VLFB; + else + { + params->have_vga = GRUB_VIDEO_TYPE_TEXT; + params->video_width = 80; + params->video_height = 25; + } + /* Initialize these last, because terminal position could be affected by printfs above. */ if (params->have_vga == GRUB_VIDEO_TYPE_TEXT) { diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c index 037a713..69a9405 100644 --- a/loader/i386/pc/xnu.c +++ b/loader/i386/pc/xnu.c @@ -21,8 +21,7 @@ #include #include #include -#include -#include +#include #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) @@ -43,10 +42,10 @@ grub_err_t grub_xnu_set_video (struct grub_xnu_boot_params *params) { struct grub_video_mode_info mode_info; - struct grub_video_render_target *render_target; int ret; int x,y; char *tmp, *modevar; + void *framebuffer; grub_err_t err; modevar = grub_env_get ("gfxpayload"); @@ -67,11 +66,7 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) if (err) return err; - ret = grub_video_get_info (&mode_info); - if (ret) - return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters"); - - ret = grub_video_get_active_render_target (&render_target); + ret = grub_video_get_info_and_fini (&mode_info, &framebuffer); if (ret) return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters"); @@ -102,7 +97,7 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) params->lfb_depth = mode_info.bpp; params->lfb_line_len = mode_info.pitch; - params->lfb_base = PTR_TO_UINT32 (render_target->data); + params->lfb_base = PTR_TO_UINT32 (framebuffer); params->lfb_mode = grub_xnu_bitmap ? GRUB_XNU_VIDEO_SPLASH : GRUB_XNU_VIDEO_TEXT_IN_VIDEO; diff --git a/term/gfxterm.c b/term/gfxterm.c index 61cf0e7..775bc60 100644 --- a/term/gfxterm.c +++ b/term/gfxterm.c @@ -397,6 +397,8 @@ redraw_screen_rect (unsigned int x, unsigned int y, y - virtual_screen.offset_y, width, height); } + + grub_video_swap_buffers (); } static void @@ -474,6 +476,7 @@ dirty_region_redraw (void) redraw_screen_rect (x, y, width, height); dirty_region_reset (); + grub_video_swap_buffers (); } static void @@ -614,6 +617,8 @@ scroll_up (void) if (virtual_screen.cursor_state) draw_cursor (1); } + + grub_video_swap_buffers (); } static void @@ -709,5 +714,7 @@ grub_gfxterm_putchar (grub_uint32_t c) above call to write_char is redundant when the cursor is showing. */ if (virtual_screen.cursor_state) draw_cursor (1); + + grub_video_swap_buffers (); } diff --git a/video/fb/video_fb.c b/video/fb/video_fb.c new file mode 100644 index 0000000..c80f6ea --- /dev/null +++ b/video/fb/video_fb.c @@ -0,0 +1,1100 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct grub_video_fbrender_target *render_target; +struct grub_video_palette_data *palette; +static unsigned int palette_size; + +/* Specify "standard" VGA palette, some video cards may + need this and this will also be used when using RGB modes. */ +struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_NUMCOLORS] = + { + // {R, G, B, A} + {0x00, 0x00, 0x00, 0xFF}, // 0 = black + {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue + {0x00, 0xA8, 0x00, 0xFF}, // 2 = green + {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan + {0xA8, 0x00, 0x00, 0xFF}, // 4 = red + {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta + {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown + {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray + + {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray + {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue + {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green + {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan + {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red + {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta + {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow + {0xFE, 0xFE, 0xFE, 0xFF} // 15 = white + }; + +grub_err_t +grub_video_fb_init (void) +{ + grub_free (palette); + render_target = 0; + palette = 0; + palette_size = 0; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_fini (void) +{ + grub_free (palette); + render_target = 0; + palette = 0; + palette_size = 0; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_info (struct grub_video_mode_info *mode_info) +{ + /* Copy mode info from active render target. */ + grub_memcpy (mode_info, &render_target->mode_info, + sizeof (struct grub_video_mode_info)); + + return GRUB_ERR_NONE; +} + + +grub_uint8_t * +grub_video_fb_get_video_ptr (struct grub_video_fbblit_info *source, + grub_uint32_t x, grub_uint32_t y) +{ + grub_uint8_t *ptr = 0; + + switch (source->mode_info->bpp) + { + case 32: + ptr = (grub_uint8_t *)source->data + + y * source->mode_info->pitch + + x * 4; + break; + + case 24: + ptr = (grub_uint8_t *)source->data + + y * source->mode_info->pitch + + x * 3; + break; + + case 16: + case 15: + ptr = (grub_uint8_t *)source->data + + y * source->mode_info->pitch + + x * 2; + break; + + case 8: + ptr = (grub_uint8_t *)source->data + + y * source->mode_info->pitch + + x; + break; + } + + return ptr; +} + +grub_err_t +grub_video_fb_get_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned int i; + + /* Assume that we know everything from index color palette. */ + for (i = 0; (i < count) && ((i + start) < palette_size); i++) + palette_data[i] = palette[start + i]; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned i; + if (start + count > palette_size) + { + palette_size = start + count; + palette = grub_realloc (palette, sizeof (palette[0]) * palette_size); + if (!palette) + { + grub_video_fb_fini (); + return grub_errno; + } + } + for (i = 0; (i < count) && ((i + start) < palette_size); i++) + palette[start + i] = palette_data[i]; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_viewport (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + render_target->viewport.x = x; + render_target->viewport.y = y; + render_target->viewport.width = width; + render_target->viewport.height = height; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_viewport (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height) +{ + if (x) *x = render_target->viewport.x; + if (y) *y = render_target->viewport.y; + if (width) *width = render_target->viewport.width; + if (height) *height = render_target->viewport.height; + + return GRUB_ERR_NONE; +} + +/* Maps color name to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_color (grub_uint32_t color_name) +{ + /* TODO: implement color theme mapping code. */ + + if (color_name < palette_size) + { + if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + return color_name; + else + { + grub_video_color_t color; + + color = grub_video_fb_map_rgb (palette[color_name].r, + palette[color_name].g, + palette[color_name].b); + + return color; + } + } + + return 0; +} + +/* Maps RGB to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_rgb (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue) +{ + if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + { + int minindex = 0; + int delta = 0; + int tmp; + int val; + unsigned i; + + /* Find best matching color. */ + for (i = 0; i < palette_size; i++) + { + val = palette[i].r - red; + tmp = val * val; + val = palette[i].g - green; + tmp += val * val; + val = palette[i].b - blue; + tmp += val * val; + + if (i == 0) + delta = tmp; + + if (tmp < delta) + { + delta = tmp; + minindex = i; + if (tmp == 0) + break; + } + } + + return minindex; + } + else if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == render_target->mode_info.fg_red + && green == render_target->mode_info.fg_green + && blue == render_target->mode_info.fg_blue) + return 1; + else + return 0; + } + else + { + grub_uint32_t value; + grub_uint8_t alpha = 255; /* Opaque color. */ + + red >>= 8 - render_target->mode_info.red_mask_size; + green >>= 8 - render_target->mode_info.green_mask_size; + blue >>= 8 - render_target->mode_info.blue_mask_size; + alpha >>= 8 - render_target->mode_info.reserved_mask_size; + + value = red << render_target->mode_info.red_field_pos; + value |= green << render_target->mode_info.green_field_pos; + value |= blue << render_target->mode_info.blue_field_pos; + value |= alpha << render_target->mode_info.reserved_field_pos; + + return value; + } + +} + +/* Maps RGBA to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_rgba (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue, grub_uint8_t alpha) +{ + if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + /* No alpha available in index color modes, just use + same value as in only RGB modes. */ + return grub_video_fb_map_rgb (red, green, blue); + else if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == render_target->mode_info.fg_red + && green == render_target->mode_info.fg_green + && blue == render_target->mode_info.fg_blue + && alpha == render_target->mode_info.fg_alpha) + return 1; + else + return 0; + } + else + { + grub_uint32_t value; + + red >>= 8 - render_target->mode_info.red_mask_size; + green >>= 8 - render_target->mode_info.green_mask_size; + blue >>= 8 - render_target->mode_info.blue_mask_size; + alpha >>= 8 - render_target->mode_info.reserved_mask_size; + + value = red << render_target->mode_info.red_field_pos; + value |= green << render_target->mode_info.green_field_pos; + value |= blue << render_target->mode_info.blue_field_pos; + value |= alpha << render_target->mode_info.reserved_field_pos; + + return value; + } +} + +/* Splits target optimized format to components. */ +grub_err_t +grub_video_fb_unmap_color (grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha) +{ + struct grub_video_fbblit_info target_info; + + target_info.mode_info = &render_target->mode_info; + target_info.data = render_target->data; + + grub_video_fb_unmap_color_int (&target_info, color, red, green, blue, alpha); + + return GRUB_ERR_NONE; +} + +/* Splits color in source format to components. */ +void +grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, + grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha) +{ + struct grub_video_mode_info *mode_info; + mode_info = source->mode_info; + + if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + { + /* If we have an out-of-bounds color, return transparent black. */ + if (color > 255) + { + *red = 0; + *green = 0; + *blue = 0; + *alpha = 0; + return; + } + + *red = palette[color].r; + *green = palette[color].g; + *blue = palette[color].b; + *alpha = palette[color].a; + return; + } + else if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (color & 1) + { + *red = mode_info->fg_red; + *green = mode_info->fg_green; + *blue = mode_info->fg_blue; + *alpha = mode_info->fg_alpha; + } + else + { + *red = mode_info->bg_red; + *green = mode_info->bg_green; + *blue = mode_info->bg_blue; + *alpha = mode_info->bg_alpha; + } + } + else + { + grub_uint32_t tmp; + + /* Get red component. */ + tmp = color >> mode_info->red_field_pos; + tmp &= (1 << mode_info->red_mask_size) - 1; + tmp <<= 8 - mode_info->red_mask_size; + tmp |= (1 << (8 - mode_info->red_mask_size)) - 1; + *red = tmp & 0xFF; + + /* Get green component. */ + tmp = color >> mode_info->green_field_pos; + tmp &= (1 << mode_info->green_mask_size) - 1; + tmp <<= 8 - mode_info->green_mask_size; + tmp |= (1 << (8 - mode_info->green_mask_size)) - 1; + *green = tmp & 0xFF; + + /* Get blue component. */ + tmp = color >> mode_info->blue_field_pos; + tmp &= (1 << mode_info->blue_mask_size) - 1; + tmp <<= 8 - mode_info->blue_mask_size; + tmp |= (1 << (8 - mode_info->blue_mask_size)) - 1; + *blue = tmp & 0xFF; + + /* Get alpha component. */ + if (source->mode_info->reserved_mask_size > 0) + { + tmp = color >> mode_info->reserved_field_pos; + tmp &= (1 << mode_info->reserved_mask_size) - 1; + tmp <<= 8 - mode_info->reserved_mask_size; + tmp |= (1 << (8 - mode_info->reserved_mask_size)) - 1; + } + else + /* If there is no alpha component, assume it opaque. */ + tmp = 255; + + *alpha = tmp & 0xFF; + } +} + +grub_err_t +grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info target; + + /* Make sure there is something to do. */ + if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) + return GRUB_ERR_NONE; + + /* Do not allow drawing out of viewport. */ + if (x < 0) + { + width += x; + x = 0; + } + if (y < 0) + { + height += y; + y = 0; + } + + if ((x + width) > render_target->viewport.width) + width = render_target->viewport.width - x; + if ((y + height) > render_target->viewport.height) + height = render_target->viewport.height - y; + + /* Add viewport offset. */ + x += render_target->viewport.x; + y += render_target->viewport.y; + + /* Use fbblit_info to encapsulate rendering. */ + target.mode_info = &render_target->mode_info; + target.data = render_target->data; + + /* Try to figure out more optimized version. Note that color is already + mapped to target format so we can make assumptions based on that. */ + if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbfill_direct32 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_fbfill_direct32 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_fbfill_direct24 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_565) + { + grub_video_fbfill_direct16 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_565) + { + grub_video_fbfill_direct16 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbfill_direct8 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + + /* No optimized version found, use default (slow) filler. */ + grub_video_fbfill (&target, color, x, y, width, height); + + return GRUB_ERR_NONE; +} + +/* NOTE: This function assumes that given coordinates are within bounds of + handled data. */ +static void +common_blitter (struct grub_video_fbblit_info *target, + struct grub_video_fbblit_info *source, + enum grub_video_blit_operators oper, int x, int y, + unsigned int width, unsigned int height, + int offset_x, int offset_y) +{ + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* Try to figure out more optimized version for replace operator. */ + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbblit_replace_BGRX8888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_fbblit_replace_BGR888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_fbblit_replace_RGB888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbblit_replace_index_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbblit_replace_BGRX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_fbblit_replace_RGBX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_fbblit_replace_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbblit_replace_index_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace (target, source, x, y, width, height, + offset_x, offset_y); + } + else + { + /* Try to figure out more optimized blend operator. */ + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbblit_blend_BGRA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_fbblit_blend_RGBA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_fbblit_blend_BGR888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_fbblit_blend_RGB888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbblit_blend_index_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + /* Note: There is really no alpha information here, so blend is + changed to replace. */ + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_fbblit_replace_BGRX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_fbblit_replace_RGBX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_fbblit_replace_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) + { + grub_video_fbblit_replace_index_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + + /* No optimized blend operation found, use default (slow) blitter. */ + grub_video_fbblit_blend (target, source, x, y, width, height, + offset_x, offset_y); + } +} + +grub_err_t +grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, int x, int y, + int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info source; + struct grub_video_fbblit_info target; + + /* Make sure there is something to do. */ + if ((width == 0) || (height == 0)) + return GRUB_ERR_NONE; + if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) + return GRUB_ERR_NONE; + if ((x + (int)bitmap->mode_info.width) < 0) + return GRUB_ERR_NONE; + if ((y + (int)bitmap->mode_info.height) < 0) + return GRUB_ERR_NONE; + if ((offset_x >= (int)bitmap->mode_info.width) + || (offset_x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((offset_y >= (int)bitmap->mode_info.height) + || (offset_y + (int)height < 0)) + return GRUB_ERR_NONE; + + /* If we have negative coordinates, optimize drawing to minimum. */ + if (offset_x < 0) + { + width += offset_x; + x -= offset_x; + offset_x = 0; + } + + if (offset_y < 0) + { + height += offset_y; + y -= offset_y; + offset_y = 0; + } + + if (x < 0) + { + width += x; + offset_x -= x; + x = 0; + } + + if (y < 0) + { + height += y; + offset_y -= y; + y = 0; + } + + /* Do not allow drawing out of viewport. */ + if ((x + width) > render_target->viewport.width) + width = render_target->viewport.width - x; + if ((y + height) > render_target->viewport.height) + height = render_target->viewport.height - y; + + if ((offset_x + width) > bitmap->mode_info.width) + width = bitmap->mode_info.width - offset_x; + if ((offset_y + height) > bitmap->mode_info.height) + height = bitmap->mode_info.height - offset_y; + + /* Limit drawing to source render target dimensions. */ + if (width > bitmap->mode_info.width) + width = bitmap->mode_info.width; + + if (height > bitmap->mode_info.height) + height = bitmap->mode_info.height; + + /* Add viewport offset. */ + x += render_target->viewport.x; + y += render_target->viewport.y; + + /* Use fbblit_info to encapsulate rendering. */ + source.mode_info = &bitmap->mode_info; + source.data = bitmap->data; + target.mode_info = &render_target->mode_info; + target.data = render_target->data; + + /* Do actual blitting. */ + common_blitter (&target, &source, oper, x, y, width, height, + offset_x, offset_y); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info source_info; + struct grub_video_fbblit_info target_info; + + /* Make sure there is something to do. */ + if ((width == 0) || (height == 0)) + return GRUB_ERR_NONE; + if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) + return GRUB_ERR_NONE; + if ((x + (int)source->mode_info.width) < 0) + return GRUB_ERR_NONE; + if ((y + (int)source->mode_info.height) < 0) + return GRUB_ERR_NONE; + if ((offset_x >= (int)source->mode_info.width) + || (offset_x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((offset_y >= (int)source->mode_info.height) + || (offset_y + (int)height < 0)) + return GRUB_ERR_NONE; + + /* If we have negative coordinates, optimize drawing to minimum. */ + if (offset_x < 0) + { + width += offset_x; + x -= offset_x; + offset_x = 0; + } + + if (offset_y < 0) + { + height += offset_y; + y -= offset_y; + offset_y = 0; + } + + if (x < 0) + { + width += x; + offset_x -= x; + x = 0; + } + + if (y < 0) + { + height += y; + offset_y -= y; + y = 0; + } + + /* Do not allow drawing out of viewport. */ + if ((x + width) > render_target->viewport.width) + width = render_target->viewport.width - x; + if ((y + height) > render_target->viewport.height) + height = render_target->viewport.height - y; + + if ((offset_x + width) > source->mode_info.width) + width = source->mode_info.width - offset_x; + if ((offset_y + height) > source->mode_info.height) + height = source->mode_info.height - offset_y; + + /* Limit drawing to source render target dimensions. */ + if (width > source->mode_info.width) + width = source->mode_info.width; + + if (height > source->mode_info.height) + height = source->mode_info.height; + + /* Add viewport offset. */ + x += render_target->viewport.x; + y += render_target->viewport.y; + + /* Use fbblit_info to encapsulate rendering. */ + source_info.mode_info = &source->mode_info; + source_info.data = source->data; + target_info.mode_info = &render_target->mode_info; + target_info.data = render_target->data; + + /* Do actual blitting. */ + common_blitter (&target_info, &source_info, oper, x, y, width, height, + offset_x, offset_y); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) +{ + int width; + int height; + int src_x; + int src_y; + int dst_x; + int dst_y; + + /* 1. Check if we have something to do. */ + if ((dx == 0) && (dy == 0)) + return GRUB_ERR_NONE; + + width = render_target->viewport.width - grub_abs (dx); + height = render_target->viewport.height - grub_abs (dy); + + if (dx < 0) + { + src_x = render_target->viewport.x - dx; + dst_x = render_target->viewport.x; + } + else + { + src_x = render_target->viewport.x; + dst_x = render_target->viewport.x + dx; + } + + if (dy < 0) + { + src_y = render_target->viewport.y - dy; + dst_y = render_target->viewport.y; + } + else + { + src_y = render_target->viewport.y; + dst_y = render_target->viewport.y + dy; + } + + /* 2. Check if there is need to copy data. */ + if ((grub_abs (dx) < render_target->viewport.width) + && (grub_abs (dy) < render_target->viewport.height)) + { + /* 3. Move data in render target. */ + struct grub_video_fbblit_info target; + grub_uint8_t *src; + grub_uint8_t *dst; + int j; + + target.mode_info = &render_target->mode_info; + target.data = render_target->data; + + /* Check vertical direction of the move. */ + if (dy <= 0) + /* 3a. Move data upwards. */ + for (j = 0; j < height; j++) + { + dst = grub_video_fb_get_video_ptr (&target, dst_x, dst_y + j); + src = grub_video_fb_get_video_ptr (&target, src_x, src_y + j); + grub_memmove (dst, src, + width * target.mode_info->bytes_per_pixel); + } + else + /* 3b. Move data downwards. */ + for (j = (height - 1); j >= 0; j--) + { + dst = grub_video_fb_get_video_ptr (&target, dst_x, dst_y + j); + src = grub_video_fb_get_video_ptr (&target, src_x, src_y + j); + grub_memmove (dst, src, + width * target.mode_info->bytes_per_pixel); + } + } + + /* 4. Fill empty space with specified color. In this implementation + there might be colliding areas but at the moment there is no need + to optimize this. */ + + /* 4a. Fill top & bottom parts. */ + if (dy > 0) + grub_video_fb_fill_rect (color, 0, 0, render_target->viewport.width, dy); + else if (dy < 0) + { + if (render_target->viewport.height < grub_abs (dy)) + dy = -render_target->viewport.height; + + grub_video_fb_fill_rect (color, 0, render_target->viewport.height + dy, + render_target->viewport.width, -dy); + } + + /* 4b. Fill left & right parts. */ + if (dx > 0) + grub_video_fb_fill_rect (color, 0, 0, + dx, render_target->viewport.height); + else if (dx < 0) + { + if (render_target->viewport.width < grub_abs (dx)) + dx = -render_target->viewport.width; + + grub_video_fb_fill_rect (color, render_target->viewport.width + dx, 0, + -dx, render_target->viewport.height); + } + + return GRUB_ERR_NONE; +} + + +grub_err_t +grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, + unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused))) +{ + struct grub_video_fbrender_target *target; + unsigned int size; + + /* Validate arguments. */ + if ((! result) + || (width == 0) + || (height == 0)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid argument given."); + + /* Allocate memory for render target. */ + target = grub_malloc (sizeof (struct grub_video_fbrender_target)); + if (! target) + return grub_errno; + + /* TODO: Implement other types too. + Currently only 32bit render targets are supported. */ + + /* Mark render target as allocated. */ + target->is_allocated = 1; + + /* Maximize viewport. */ + target->viewport.x = 0; + target->viewport.y = 0; + target->viewport.width = width; + target->viewport.height = height; + + /* Setup render target format. */ + target->mode_info.width = width; + target->mode_info.height = height; + target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA; + target->mode_info.bpp = 32; + target->mode_info.bytes_per_pixel = 4; + target->mode_info.pitch = target->mode_info.bytes_per_pixel * width; + target->mode_info.number_of_colors = palette_size; /* Emulated palette. */ + target->mode_info.red_mask_size = 8; + target->mode_info.red_field_pos = 0; + target->mode_info.green_mask_size = 8; + target->mode_info.green_field_pos = 8; + target->mode_info.blue_mask_size = 8; + target->mode_info.blue_field_pos = 16; + target->mode_info.reserved_mask_size = 8; + target->mode_info.reserved_field_pos = 24; + + target->mode_info.blit_format = grub_video_get_blit_format (&target->mode_info); + + /* Calculate size needed for the data. */ + size = (width * target->mode_info.bytes_per_pixel) * height; + + target->data = grub_malloc (size); + if (! target->data) + { + grub_free (target); + return grub_errno; + } + + /* Clear render target with black and maximum transparency. */ + grub_memset (target->data, 0, size); + + /* TODO: Add render target to render target list. */ + + /* Save result to caller. */ + *result = target; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target) +{ + /* If there is no target, then just return without error. */ + if (! target) + return GRUB_ERR_NONE; + + /* TODO: Delist render target from render target list. */ + + /* If this is software render target, free it's memory. */ + if (target->is_allocated) + grub_free (target->data); + + /* Free render target. */ + grub_free (target); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target) +{ + if (! target->data) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid render target given."); + + render_target = target; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **target) +{ + *target = render_target; + + return GRUB_ERR_NONE; +} diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c index ae08402..5b6e2b8 100644 --- a/video/i386/pc/vbe.c +++ b/video/i386/pc/vbe.c @@ -16,43 +16,17 @@ * along with GRUB. If not, see . */ +#define grub_video_render_target grub_video_fbrender_target + #include #include #include #include -#include -#include -#include #include #include #include #include #include -#include - -/* Specify "standard" VGA palette, some video cards may - need this and this will also be used when using RGB modes. */ -static struct grub_vbe_palette_data vga_colors[16] = - { - // {B, G, R, A} - {0x00, 0x00, 0x00, 0x00}, // 0 = black - {0xA8, 0x00, 0x00, 0x00}, // 1 = blue - {0x00, 0xA8, 0x00, 0x00}, // 2 = green - {0xA8, 0xA8, 0x00, 0x00}, // 3 = cyan - {0x00, 0x00, 0xA8, 0x00}, // 4 = red - {0xA8, 0x00, 0xA8, 0x00}, // 5 = magenta - {0x00, 0x54, 0xA8, 0x00}, // 6 = brown - {0xA8, 0xA8, 0xA8, 0x00}, // 7 = light gray - - {0x54, 0x54, 0x54, 0x00}, // 8 = dark gray - {0xFE, 0x54, 0x54, 0x00}, // 9 = bright blue - {0x54, 0xFE, 0x54, 0x00}, // 10 = bright green - {0xFE, 0xFE, 0x54, 0x00}, // 11 = bright cyan - {0x54, 0x54, 0xFE, 0x00}, // 12 = bright red - {0xFE, 0x54, 0xFE, 0x00}, // 13 = bright magenta - {0x54, 0xFE, 0xFE, 0x00}, // 14 = yellow - {0xFE, 0xFE, 0xFE, 0x00} // 15 = white - }; static int vbe_detected = -1; @@ -68,14 +42,30 @@ static struct grub_uint32_t active_mode; grub_uint8_t *ptr; int index_color_mode; - struct grub_video_palette_data palette[256]; } framebuffer; -static struct grub_video_render_target *render_target; static grub_uint32_t initial_mode; static grub_uint32_t mode_in_use = 0x55aa; static grub_uint16_t *mode_list; +static struct +{ + grub_size_t page_size; /* The size of a page in bytes. */ + + /* For page flipping strategy. */ + int displayed_page; /* The page # that is the front buffer. */ + int render_page; /* The page # that is the back buffer. */ + + /* For blit strategy. */ + grub_uint8_t *offscreen_buffer; + + /* Virtual functions. */ + int (*update_screen) (void); + int (*destroy) (void); +} doublebuf_state; + +static void double_buffering_init (int enable); + static void * real2pm (grub_vbe_farptr_t ptr) { @@ -142,6 +132,7 @@ grub_vbe_set_video_mode (grub_uint32_t mode, { grub_vbe_status_t status; grub_uint32_t old_mode; + grub_err_t err; /* Make sure that VBE is supported. */ grub_vbe_probe (0); @@ -239,15 +230,29 @@ grub_vbe_set_video_mode (grub_uint32_t mode, { struct grub_vbe_palette_data *palette = (struct grub_vbe_palette_data *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + unsigned i; /* Make sure that the BIOS can reach the palette. */ - grub_memcpy (palette, vga_colors, sizeof (vga_colors)); - status = grub_vbe_bios_set_palette_data (sizeof (vga_colors) - / sizeof (struct grub_vbe_palette_data), + for (i = 0; i < sizeof (grub_video_fbstd_colors) + / sizeof (grub_video_fbstd_colors[0]); i++) + { + palette[i].red = grub_video_fbstd_colors[i].r; + palette[i].green = grub_video_fbstd_colors[i].g; + palette[i].blue = grub_video_fbstd_colors[i].b; + palette[i].alignment = 0; + } + + status = grub_vbe_bios_set_palette_data (sizeof (grub_video_fbstd_colors) + / sizeof (grub_video_fbstd_colors[0]), 0, palette); /* Just ignore the status. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + if (err) + return err; + } } @@ -308,43 +313,6 @@ grub_vbe_get_video_mode_info (grub_uint32_t mode, return GRUB_ERR_NONE; } -grub_uint8_t * -grub_video_vbe_get_video_ptr (struct grub_video_i386_vbeblit_info *source, - grub_uint32_t x, grub_uint32_t y) -{ - grub_uint8_t *ptr = 0; - - switch (source->mode_info->bpp) - { - case 32: - ptr = (grub_uint8_t *)source->data - + y * source->mode_info->pitch - + x * 4; - break; - - case 24: - ptr = (grub_uint8_t *)source->data - + y * source->mode_info->pitch - + x * 3; - break; - - case 16: - case 15: - ptr = (grub_uint8_t *)source->data - + y * source->mode_info->pitch - + x * 2; - break; - - case 8: - ptr = (grub_uint8_t *)source->data - + y * source->mode_info->pitch - + x; - break; - } - - return ptr; -} - static grub_err_t grub_video_vbe_init (void) { @@ -387,9 +355,9 @@ grub_video_vbe_init (void) /* Reset frame buffer and render target variables. */ grub_memset (&framebuffer, 0, sizeof(framebuffer)); - render_target = &framebuffer.render_target; + grub_memset (&doublebuf_state, 0, sizeof(doublebuf_state)); - return GRUB_ERR_NONE; + return grub_video_fb_init (); } static grub_err_t @@ -403,14 +371,16 @@ grub_video_vbe_fini (void) /* TODO: Decide, is this something we want to do. */ return grub_errno; + if (doublebuf_state.destroy) + doublebuf_state.destroy(); + /* TODO: Free any resources allocated by driver. */ grub_free (mode_list); mode_list = 0; /* TODO: destroy render targets. */ - /* Return success to caller. */ - return GRUB_ERR_NONE; + return grub_video_fb_fini (); } static grub_err_t @@ -422,7 +392,6 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, struct grub_vbe_mode_info_block best_mode_info; grub_uint32_t best_mode = 0; int depth; - unsigned int i; /* Decode depth from mode_type. If it is zero, then autodetect. */ depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) @@ -502,6 +471,7 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, /* Try to initialize best mode found. */ if (best_mode != 0) { + grub_err_t err; /* If this fails, then we have mode selection heuristics problem, or adapter failure. */ grub_vbe_set_video_mode (best_mode, &active_mode_info); @@ -512,1089 +482,302 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, in order to fasten later operations. */ mode_in_use = best_mode; - /* Reset render target to framebuffer one. */ - render_target = &framebuffer.render_target; - /* Fill mode info details in framebuffer's render target. */ - render_target->mode_info.width = active_mode_info.x_resolution; - render_target->mode_info.height = active_mode_info.y_resolution; + framebuffer.render_target.mode_info.width = active_mode_info.x_resolution; + framebuffer.render_target.mode_info.height = active_mode_info.y_resolution; if (framebuffer.index_color_mode) - render_target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + framebuffer.render_target.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; else - render_target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; - - render_target->mode_info.bpp = active_mode_info.bits_per_pixel; - render_target->mode_info.bytes_per_pixel = framebuffer.bytes_per_pixel; - render_target->mode_info.pitch = framebuffer.bytes_per_scan_line; - render_target->mode_info.number_of_colors = 256; /* TODO: fix me. */ - render_target->mode_info.red_mask_size = active_mode_info.red_mask_size; - render_target->mode_info.red_field_pos = active_mode_info.red_field_position; - render_target->mode_info.green_mask_size = active_mode_info.green_mask_size; - render_target->mode_info.green_field_pos = active_mode_info.green_field_position; - render_target->mode_info.blue_mask_size = active_mode_info.blue_mask_size; - render_target->mode_info.blue_field_pos = active_mode_info.blue_field_position; - render_target->mode_info.reserved_mask_size = active_mode_info.rsvd_mask_size; - render_target->mode_info.reserved_field_pos = active_mode_info.rsvd_field_position; - - render_target->mode_info.blit_format = grub_video_get_blit_format (&render_target->mode_info); + framebuffer.render_target.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + + framebuffer.render_target.mode_info.bpp = active_mode_info.bits_per_pixel; + framebuffer.render_target.mode_info.bytes_per_pixel = framebuffer.bytes_per_pixel; + framebuffer.render_target.mode_info.pitch = framebuffer.bytes_per_scan_line; + framebuffer.render_target.mode_info.number_of_colors = 256; /* TODO: fix me. */ + framebuffer.render_target.mode_info.red_mask_size = active_mode_info.red_mask_size; + framebuffer.render_target.mode_info.red_field_pos = active_mode_info.red_field_position; + framebuffer.render_target.mode_info.green_mask_size = active_mode_info.green_mask_size; + framebuffer.render_target.mode_info.green_field_pos = active_mode_info.green_field_position; + framebuffer.render_target.mode_info.blue_mask_size = active_mode_info.blue_mask_size; + framebuffer.render_target.mode_info.blue_field_pos = active_mode_info.blue_field_position; + framebuffer.render_target.mode_info.reserved_mask_size = active_mode_info.rsvd_mask_size; + framebuffer.render_target.mode_info.reserved_field_pos = active_mode_info.rsvd_field_position; + + framebuffer.render_target.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.render_target.mode_info); /* Reset viewport to match new mode. */ - render_target->viewport.x = 0; - render_target->viewport.y = 0; - render_target->viewport.width = active_mode_info.x_resolution; - render_target->viewport.height = active_mode_info.y_resolution; + framebuffer.render_target.viewport.x = 0; + framebuffer.render_target.viewport.y = 0; + framebuffer.render_target.viewport.width = active_mode_info.x_resolution; + framebuffer.render_target.viewport.height = active_mode_info.y_resolution; + + /* Mark framebuffer memory as non allocated. */ + framebuffer.render_target.is_allocated = 0; - /* Set framebuffer pointer and mark it as non allocated. */ - render_target->is_allocated = 0; - render_target->data = framebuffer.ptr; + /* Set up double buffering, initially disabled. */ + double_buffering_init (0); /* Copy default palette to initialize emulated palette. */ - for (i = 0; - i < (sizeof (vga_colors) - / sizeof (struct grub_vbe_palette_data)); - i++) - { - framebuffer.palette[i].r = vga_colors[i].red; - framebuffer.palette[i].g = vga_colors[i].green; - framebuffer.palette[i].b = vga_colors[i].blue; - framebuffer.palette[i].a = 0xFF; - } + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + if (err) + return err; - return GRUB_ERR_NONE; + /* Reset render target to framebuffer one. */ + return grub_video_fb_set_active_render_target (&framebuffer.render_target); } /* Couldn't found matching mode. */ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found."); } -static grub_err_t -grub_video_vbe_get_info (struct grub_video_mode_info *mode_info) -{ - /* Copy mode info from active render target. */ - grub_memcpy (mode_info, &render_target->mode_info, - sizeof (struct grub_video_mode_info)); - - return GRUB_ERR_NONE; -} +/* + Set framebuffer render target page and display the proper page, based on + `doublebuf_state.render_page' and `doublebuf_state.displayed_page', + respectively. -static grub_err_t -grub_video_vbe_set_palette (unsigned int start, unsigned int count, - struct grub_video_palette_data *palette_data) + Returns 0 upon success, nonzero upon failure. + */ +static int +doublebuf_pageflipping_commit (void) { - unsigned int i; + /* Set the render target's data pointer to the start of the render_page. */ + framebuffer.render_target.data = + ((char *) framebuffer.ptr) + + doublebuf_state.page_size * doublebuf_state.render_page; - if (framebuffer.index_color_mode) - { - /* TODO: Implement setting indexed color mode palette to hardware. */ - //status = grub_vbe_bios_set_palette_data (sizeof (vga_colors) - // / sizeof (struct grub_vbe_palette_data), - // 0, - // palette); + /* Tell the video adapter to display the new front page. */ + int display_start_line = + framebuffer.render_target.mode_info.height + * doublebuf_state.displayed_page; - } + grub_vbe_status_t vbe_err = + grub_vbe_bios_set_display_start (0, display_start_line); + if (vbe_err != GRUB_VBE_STATUS_OK) + return 1; - /* Then set color to emulated palette. */ - for (i = 0; (i < count) && ((i + start) < 256); i++) - framebuffer.palette[start + i] = palette_data[i]; - - return GRUB_ERR_NONE; + return 0; } -static grub_err_t -grub_video_vbe_get_palette (unsigned int start, unsigned int count, - struct grub_video_palette_data *palette_data) +static int +doublebuf_pageflipping_update_screen (void) { - unsigned int i; - - /* Assume that we know everything from index color palette. */ - for (i = 0; (i < count) && ((i + start) < 256); i++) - palette_data[i] = framebuffer.palette[start + i]; + /* Swap the page numbers in the framebuffer struct. */ + int new_displayed_page = doublebuf_state.render_page; + doublebuf_state.render_page = doublebuf_state.displayed_page; + doublebuf_state.displayed_page = new_displayed_page; - return GRUB_ERR_NONE; + return doublebuf_pageflipping_commit (); } -static grub_err_t -grub_video_vbe_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height) +static int +doublebuf_pageflipping_destroy (void) { - /* Make sure viewport is withing screen dimensions. If viewport was set - to be out of the region, mark its size as zero. */ - if (x > active_mode_info.x_resolution) - { - x = 0; - width = 0; - } - - if (y > active_mode_info.y_resolution) - { - y = 0; - height = 0; - } - - if (x + width > active_mode_info.x_resolution) - width = active_mode_info.x_resolution - x; - - if (y + height > active_mode_info.y_resolution) - height = active_mode_info.y_resolution - y; - - render_target->viewport.x = x; - render_target->viewport.y = y; - render_target->viewport.width = width; - render_target->viewport.height = height; - - return GRUB_ERR_NONE; + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; } -static grub_err_t -grub_video_vbe_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height) +static int +doublebuf_pageflipping_init (void) { - if (x) *x = render_target->viewport.x; - if (y) *y = render_target->viewport.y; - if (width) *width = render_target->viewport.width; - if (height) *height = render_target->viewport.height; + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * framebuffer.render_target.mode_info.height; - return GRUB_ERR_NONE; -} + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; -/* Maps color name to target optimized color format. */ -static grub_video_color_t -grub_video_vbe_map_color (grub_uint32_t color_name) -{ - /* TODO: implement color theme mapping code. */ + if (2 * doublebuf_state.page_size > vram_size) + return 1; /* Not enough video memory for 2 pages. */ - if (color_name < 256) - { - if ((render_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) - return color_name; - else - { - grub_video_color_t color; + doublebuf_state.displayed_page = 0; + doublebuf_state.render_page = 1; - color = grub_video_vbe_map_rgb (framebuffer.palette[color_name].r, - framebuffer.palette[color_name].g, - framebuffer.palette[color_name].b); + doublebuf_state.update_screen = doublebuf_pageflipping_update_screen; + doublebuf_state.destroy = doublebuf_pageflipping_destroy; - return color; - } - } + /* Set the framebuffer memory data pointer and display the right page. */ + if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE) + return 1; /* Unable to set the display start. */ + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; return 0; } -/* Maps RGB to target optimized color format. */ -grub_video_color_t -grub_video_vbe_map_rgb (grub_uint8_t red, grub_uint8_t green, - grub_uint8_t blue) +static int +doublebuf_blit_update_screen (void) { - if ((render_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) - { - int minindex = 0; - int delta = 0; - int tmp; - int val; - int i; - - /* Find best matching color. */ - for (i = 0; i < 256; i++) - { - val = framebuffer.palette[i].r - red; - tmp = val * val; - val = framebuffer.palette[i].g - green; - tmp += val * val; - val = framebuffer.palette[i].b - blue; - tmp += val * val; - - if (i == 0) - delta = tmp; - - if (tmp < delta) - { - delta = tmp; - minindex = i; - if (tmp == 0) - break; - } - } - - return minindex; - } - else if ((render_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) - { - if (red == render_target->mode_info.fg_red - && green == render_target->mode_info.fg_green - && blue == render_target->mode_info.fg_blue) - return 1; - else - return 0; - } - else - { - grub_uint32_t value; - grub_uint8_t alpha = 255; /* Opaque color. */ - - red >>= 8 - render_target->mode_info.red_mask_size; - green >>= 8 - render_target->mode_info.green_mask_size; - blue >>= 8 - render_target->mode_info.blue_mask_size; - alpha >>= 8 - render_target->mode_info.reserved_mask_size; - - value = red << render_target->mode_info.red_field_pos; - value |= green << render_target->mode_info.green_field_pos; - value |= blue << render_target->mode_info.blue_field_pos; - value |= alpha << render_target->mode_info.reserved_field_pos; - - return value; - } - + grub_memcpy (framebuffer.ptr, + doublebuf_state.offscreen_buffer, + doublebuf_state.page_size); + return 0; } -/* Maps RGBA to target optimized color format. */ -grub_video_color_t -grub_video_vbe_map_rgba (grub_uint8_t red, grub_uint8_t green, - grub_uint8_t blue, grub_uint8_t alpha) +static int +doublebuf_blit_destroy (void) { - if ((render_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) - /* No alpha available in index color modes, just use - same value as in only RGB modes. */ - return grub_video_vbe_map_rgb (red, green, blue); - else if ((render_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) - { - if (red == render_target->mode_info.fg_red - && green == render_target->mode_info.fg_green - && blue == render_target->mode_info.fg_blue - && alpha == render_target->mode_info.fg_alpha) - return 1; - else - return 0; - } - else - { - grub_uint32_t value; - - red >>= 8 - render_target->mode_info.red_mask_size; - green >>= 8 - render_target->mode_info.green_mask_size; - blue >>= 8 - render_target->mode_info.blue_mask_size; - alpha >>= 8 - render_target->mode_info.reserved_mask_size; + grub_free (doublebuf_state.offscreen_buffer); + doublebuf_state.offscreen_buffer = 0; - value = red << render_target->mode_info.red_field_pos; - value |= green << render_target->mode_info.green_field_pos; - value |= blue << render_target->mode_info.blue_field_pos; - value |= alpha << render_target->mode_info.reserved_field_pos; - - return value; - } + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; } -/* Splits target optimized format to components. */ -grub_err_t grub_video_vbe_unmap_color (grub_video_color_t color, - grub_uint8_t *red, grub_uint8_t *green, - grub_uint8_t *blue, grub_uint8_t *alpha) +static int +doublebuf_blit_init (void) { - struct grub_video_i386_vbeblit_info target_info; + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * framebuffer.render_target.mode_info.height; - target_info.mode_info = &render_target->mode_info; - target_info.data = render_target->data; + doublebuf_state.offscreen_buffer = (grub_uint8_t *) + grub_malloc (doublebuf_state.page_size); + if (doublebuf_state.offscreen_buffer == 0) + return 1; /* Error. */ - grub_video_vbe_unmap_color_int (&target_info, color, red, green, blue, alpha); + framebuffer.render_target.data = doublebuf_state.offscreen_buffer; + doublebuf_state.update_screen = doublebuf_blit_update_screen; + doublebuf_state.destroy = doublebuf_blit_destroy; - return GRUB_ERR_NONE; + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; } -/* Splits color in source format to components. */ -void -grub_video_vbe_unmap_color_int (struct grub_video_i386_vbeblit_info * source, - grub_video_color_t color, - grub_uint8_t *red, grub_uint8_t *green, - grub_uint8_t *blue, grub_uint8_t *alpha) +static int +doublebuf_null_update_screen (void) { - struct grub_video_mode_info *mode_info; - mode_info = source->mode_info; - - if ((mode_info->mode_type - & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) - { - /* If we have an out-of-bounds color, return transparent black. */ - if (color > 255) - { - *red = 0; - *green = 0; - *blue = 0; - *alpha = 0; - return; - } - - *red = framebuffer.palette[color].r; - *green = framebuffer.palette[color].g; - *blue = framebuffer.palette[color].b; - *alpha = framebuffer.palette[color].a; - return; - } - else if ((mode_info->mode_type - & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) - { - if (color & 1) - { - *red = mode_info->fg_red; - *green = mode_info->fg_green; - *blue = mode_info->fg_blue; - *alpha = mode_info->fg_alpha; - } - else - { - *red = mode_info->bg_red; - *green = mode_info->bg_green; - *blue = mode_info->bg_blue; - *alpha = mode_info->bg_alpha; - } - } - else - { - grub_uint32_t tmp; - - /* Get red component. */ - tmp = color >> mode_info->red_field_pos; - tmp &= (1 << mode_info->red_mask_size) - 1; - tmp <<= 8 - mode_info->red_mask_size; - tmp |= (1 << (8 - mode_info->red_mask_size)) - 1; - *red = tmp & 0xFF; - - /* Get green component. */ - tmp = color >> mode_info->green_field_pos; - tmp &= (1 << mode_info->green_mask_size) - 1; - tmp <<= 8 - mode_info->green_mask_size; - tmp |= (1 << (8 - mode_info->green_mask_size)) - 1; - *green = tmp & 0xFF; - - /* Get blue component. */ - tmp = color >> mode_info->blue_field_pos; - tmp &= (1 << mode_info->blue_mask_size) - 1; - tmp <<= 8 - mode_info->blue_mask_size; - tmp |= (1 << (8 - mode_info->blue_mask_size)) - 1; - *blue = tmp & 0xFF; - - /* Get alpha component. */ - if (source->mode_info->reserved_mask_size > 0) - { - tmp = color >> mode_info->reserved_field_pos; - tmp &= (1 << mode_info->reserved_mask_size) - 1; - tmp <<= 8 - mode_info->reserved_mask_size; - tmp |= (1 << (8 - mode_info->reserved_mask_size)) - 1; - } - else - /* If there is no alpha component, assume it opaque. */ - tmp = 255; - - *alpha = tmp & 0xFF; - } + return 0; } -static grub_err_t -grub_video_vbe_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height) +static int +doublebuf_null_destroy (void) { - struct grub_video_i386_vbeblit_info target; - - /* Make sure there is something to do. */ - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) - return GRUB_ERR_NONE; - - /* Do not allow drawing out of viewport. */ - if (x < 0) - { - width += x; - x = 0; - } - if (y < 0) - { - height += y; - y = 0; - } - - if ((x + width) > render_target->viewport.width) - width = render_target->viewport.width - x; - if ((y + height) > render_target->viewport.height) - height = render_target->viewport.height - y; - - /* Add viewport offset. */ - x += render_target->viewport.x; - y += render_target->viewport.y; - - /* Use vbeblit_info to encapsulate rendering. */ - target.mode_info = &render_target->mode_info; - target.data = render_target->data; - - /* Try to figure out more optimized version. Note that color is already - mapped to target format so we can make assumptions based on that. */ - if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbefill_direct32 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - grub_video_i386_vbefill_direct32 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - grub_video_i386_vbefill_direct24 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_565) - { - grub_video_i386_vbefill_direct16 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_565) - { - grub_video_i386_vbefill_direct16 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbefill_direct8 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - - /* No optimized version found, use default (slow) filler. */ - grub_video_i386_vbefill (&target, color, x, y, width, height); - - return GRUB_ERR_NONE; + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; } -/* NOTE: This function assumes that given coordinates are within bounds of - handled data. */ -static void -common_blitter (struct grub_video_i386_vbeblit_info *target, - struct grub_video_i386_vbeblit_info *source, - enum grub_video_blit_operators oper, int x, int y, - unsigned int width, unsigned int height, - int offset_x, int offset_y) +static int +doublebuf_null_init (void) { - if (oper == GRUB_VIDEO_BLIT_REPLACE) - { - /* Try to figure out more optimized version for replace operator. */ - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - grub_video_i386_vbeblit_replace_directN (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbeblit_replace_BGRX8888_RGBX8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) - { - grub_video_i386_vbeblit_replace_BGR888_RGBX8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - grub_video_i386_vbeblit_replace_RGB888_RGBX8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbeblit_replace_index_RGBX8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbeblit_replace_BGRX8888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - grub_video_i386_vbeblit_replace_RGBX8888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) - { - grub_video_i386_vbeblit_replace_BGR888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - grub_video_i386_vbeblit_replace_directN (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbeblit_replace_index_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbeblit_replace_directN (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbeblit_replace_directN (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - - /* No optimized replace operator found, use default (slow) blitter. */ - grub_video_i386_vbeblit_replace (target, source, x, y, width, height, - offset_x, offset_y); - } - else - { - /* Try to figure out more optimized blend operator. */ - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbeblit_blend_BGRA8888_RGBA8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - grub_video_i386_vbeblit_blend_RGBA8888_RGBA8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) - { - grub_video_i386_vbeblit_blend_BGR888_RGBA8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - grub_video_i386_vbeblit_blend_RGB888_RGBA8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbeblit_blend_index_RGBA8888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - /* Note: There is really no alpha information here, so blend is - changed to replace. */ + framebuffer.render_target.data = framebuffer.ptr; + doublebuf_state.update_screen = doublebuf_null_update_screen; + doublebuf_state.destroy = doublebuf_null_destroy; - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) - { - grub_video_i386_vbeblit_replace_BGRX8888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) - { - grub_video_i386_vbeblit_replace_RGBX8888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) - { - grub_video_i386_vbeblit_replace_BGR888_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) - { - grub_video_i386_vbeblit_replace_directN (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) - { - grub_video_i386_vbeblit_replace_index_RGB888 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - } - - /* No optimized blend operation found, use default (slow) blitter. */ - grub_video_i386_vbeblit_blend (target, source, x, y, width, height, - offset_x, offset_y); - } + framebuffer.render_target.mode_info.mode_type + &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; } -static grub_err_t -grub_video_vbe_blit_bitmap (struct grub_video_bitmap *bitmap, - enum grub_video_blit_operators oper, int x, int y, - int offset_x, int offset_y, - unsigned int width, unsigned int height) +/* Select the best double buffering mode available. */ +static void +double_buffering_init (int enable) { - struct grub_video_i386_vbeblit_info source; - struct grub_video_i386_vbeblit_info target; - - /* Make sure there is something to do. */ - if ((width == 0) || (height == 0)) - return GRUB_ERR_NONE; - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) - return GRUB_ERR_NONE; - if ((x + (int)bitmap->mode_info.width) < 0) - return GRUB_ERR_NONE; - if ((y + (int)bitmap->mode_info.height) < 0) - return GRUB_ERR_NONE; - if ((offset_x >= (int)bitmap->mode_info.width) - || (offset_x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((offset_y >= (int)bitmap->mode_info.height) - || (offset_y + (int)height < 0)) - return GRUB_ERR_NONE; - - /* If we have negative coordinates, optimize drawing to minimum. */ - if (offset_x < 0) - { - width += offset_x; - x -= offset_x; - offset_x = 0; - } + if (doublebuf_state.destroy) + doublebuf_state.destroy (); - if (offset_y < 0) + if (enable) { - height += offset_y; - y -= offset_y; - offset_y = 0; - } + if (doublebuf_pageflipping_init () == 0) + return; - if (x < 0) - { - width += x; - offset_x -= x; - x = 0; - } - - if (y < 0) - { - height += y; - offset_y -= y; - y = 0; + if (doublebuf_blit_init () == 0) + return; } - /* Do not allow drawing out of viewport. */ - if ((x + width) > render_target->viewport.width) - width = render_target->viewport.width - x; - if ((y + height) > render_target->viewport.height) - height = render_target->viewport.height - y; - - if ((offset_x + width) > bitmap->mode_info.width) - width = bitmap->mode_info.width - offset_x; - if ((offset_y + height) > bitmap->mode_info.height) - height = bitmap->mode_info.height - offset_y; - - /* Limit drawing to source render target dimensions. */ - if (width > bitmap->mode_info.width) - width = bitmap->mode_info.width; - - if (height > bitmap->mode_info.height) - height = bitmap->mode_info.height; - - /* Add viewport offset. */ - x += render_target->viewport.x; - y += render_target->viewport.y; - - /* Use vbeblit_info to encapsulate rendering. */ - source.mode_info = &bitmap->mode_info; - source.data = bitmap->data; - target.mode_info = &render_target->mode_info; - target.data = render_target->data; - - /* Do actual blitting. */ - common_blitter (&target, &source, oper, x, y, width, height, - offset_x, offset_y); - - return GRUB_ERR_NONE; + /* Fall back to no double buffering. */ + doublebuf_null_init (); } static grub_err_t -grub_video_vbe_blit_render_target (struct grub_video_render_target *source, - enum grub_video_blit_operators oper, - int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height) +grub_video_vbe_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) { - struct grub_video_i386_vbeblit_info source_info; - struct grub_video_i386_vbeblit_info target_info; - - /* Make sure there is something to do. */ - if ((width == 0) || (height == 0)) - return GRUB_ERR_NONE; - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) - return GRUB_ERR_NONE; - if ((x + (int)source->mode_info.width) < 0) - return GRUB_ERR_NONE; - if ((y + (int)source->mode_info.height) < 0) - return GRUB_ERR_NONE; - if ((offset_x >= (int)source->mode_info.width) - || (offset_x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((offset_y >= (int)source->mode_info.height) - || (offset_y + (int)height < 0)) - return GRUB_ERR_NONE; - - /* If we have negative coordinates, optimize drawing to minimum. */ - if (offset_x < 0) - { - width += offset_x; - x -= offset_x; - offset_x = 0; - } - - if (offset_y < 0) - { - height += offset_y; - y -= offset_y; - offset_y = 0; - } - - if (x < 0) + if (framebuffer.index_color_mode) { - width += x; - offset_x -= x; - x = 0; - } + /* TODO: Implement setting indexed color mode palette to hardware. */ + //status = grub_vbe_bios_set_palette_data (sizeof (vga_colors) + // / sizeof (struct grub_vbe_palette_data), + // 0, + // palette); - if (y < 0) - { - height += y; - offset_y -= y; - y = 0; } - /* Do not allow drawing out of viewport. */ - if ((x + width) > render_target->viewport.width) - width = render_target->viewport.width - x; - if ((y + height) > render_target->viewport.height) - height = render_target->viewport.height - y; - - if ((offset_x + width) > source->mode_info.width) - width = source->mode_info.width - offset_x; - if ((offset_y + height) > source->mode_info.height) - height = source->mode_info.height - offset_y; - - /* Limit drawing to source render target dimensions. */ - if (width > source->mode_info.width) - width = source->mode_info.width; - - if (height > source->mode_info.height) - height = source->mode_info.height; - - /* Add viewport offset. */ - x += render_target->viewport.x; - y += render_target->viewport.y; - - /* Use vbeblit_info to encapsulate rendering. */ - source_info.mode_info = &source->mode_info; - source_info.data = source->data; - target_info.mode_info = &render_target->mode_info; - target_info.data = render_target->data; - - /* Do actual blitting. */ - common_blitter (&target_info, &source_info, oper, x, y, width, height, - offset_x, offset_y); + /* Then set color to emulated palette. */ - return GRUB_ERR_NONE; + return grub_video_fb_set_palette (start, count, palette_data); } static grub_err_t -grub_video_vbe_scroll (grub_video_color_t color, int dx, int dy) +grub_video_vbe_set_viewport (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) { - int width; - int height; - int src_x; - int src_y; - int dst_x; - int dst_y; - - /* 1. Check if we have something to do. */ - if ((dx == 0) && (dy == 0)) - return GRUB_ERR_NONE; - - width = render_target->viewport.width - grub_abs (dx); - height = render_target->viewport.height - grub_abs (dy); - - if (dx < 0) - { - src_x = render_target->viewport.x - dx; - dst_x = render_target->viewport.x; - } - else - { - src_x = render_target->viewport.x; - dst_x = render_target->viewport.x + dx; - } - - if (dy < 0) - { - src_y = render_target->viewport.y - dy; - dst_y = render_target->viewport.y; - } - else - { - src_y = render_target->viewport.y; - dst_y = render_target->viewport.y + dy; - } - - /* 2. Check if there is need to copy data. */ - if ((grub_abs (dx) < render_target->viewport.width) - && (grub_abs (dy) < render_target->viewport.height)) + /* Make sure viewport is withing screen dimensions. If viewport was set + to be out of the region, mark its size as zero. */ + if (x > active_mode_info.x_resolution) { - /* 3. Move data in render target. */ - struct grub_video_i386_vbeblit_info target; - grub_uint8_t *src; - grub_uint8_t *dst; - int j; - - target.mode_info = &render_target->mode_info; - target.data = render_target->data; - - /* Check vertical direction of the move. */ - if (dy <= 0) - /* 3a. Move data upwards. */ - for (j = 0; j < height; j++) - { - dst = grub_video_vbe_get_video_ptr (&target, dst_x, dst_y + j); - src = grub_video_vbe_get_video_ptr (&target, src_x, src_y + j); - grub_memmove (dst, src, - width * target.mode_info->bytes_per_pixel); - } - else - /* 3b. Move data downwards. */ - for (j = (height - 1); j >= 0; j--) - { - dst = grub_video_vbe_get_video_ptr (&target, dst_x, dst_y + j); - src = grub_video_vbe_get_video_ptr (&target, src_x, src_y + j); - grub_memmove (dst, src, - width * target.mode_info->bytes_per_pixel); - } + x = 0; + width = 0; } - /* 4. Fill empty space with specified color. In this implementation - there might be colliding areas but at the moment there is no need - to optimize this. */ - - /* 4a. Fill top & bottom parts. */ - if (dy > 0) - grub_video_vbe_fill_rect (color, 0, 0, render_target->viewport.width, dy); - else if (dy < 0) + if (y > active_mode_info.y_resolution) { - if (render_target->viewport.height < grub_abs (dy)) - dy = -render_target->viewport.height; - - grub_video_vbe_fill_rect (color, 0, render_target->viewport.height + dy, - render_target->viewport.width, -dy); + y = 0; + height = 0; } - /* 4b. Fill left & right parts. */ - if (dx > 0) - grub_video_vbe_fill_rect (color, 0, 0, - dx, render_target->viewport.height); - else if (dx < 0) - { - if (render_target->viewport.width < grub_abs (dx)) - dx = -render_target->viewport.width; - - grub_video_vbe_fill_rect (color, render_target->viewport.width + dx, 0, - -dx, render_target->viewport.height); - } + if (x + width > active_mode_info.x_resolution) + width = active_mode_info.x_resolution - x; - return GRUB_ERR_NONE; + if (y + height > active_mode_info.y_resolution) + height = active_mode_info.y_resolution - y; + return grub_video_fb_set_viewport (x, y, width, height); } static grub_err_t grub_video_vbe_swap_buffers (void) { - /* TODO: Implement buffer swapping. */ - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_video_vbe_create_render_target (struct grub_video_render_target **result, - unsigned int width, unsigned int height, - unsigned int mode_type __attribute__ ((unused))) -{ - struct grub_video_render_target *target; - unsigned int size; - - /* Validate arguments. */ - if ((! result) - || (width == 0) - || (height == 0)) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "invalid argument given."); - - /* Allocate memory for render target. */ - target = grub_malloc (sizeof (struct grub_video_render_target)); - if (! target) - return grub_errno; - - /* TODO: Implement other types too. - Currently only 32bit render targets are supported. */ - - /* Mark render target as allocated. */ - target->is_allocated = 1; - - /* Maximize viewport. */ - target->viewport.x = 0; - target->viewport.y = 0; - target->viewport.width = width; - target->viewport.height = height; - - /* Setup render target format. */ - target->mode_info.width = width; - target->mode_info.height = height; - target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB - | GRUB_VIDEO_MODE_TYPE_ALPHA; - target->mode_info.bpp = 32; - target->mode_info.bytes_per_pixel = 4; - target->mode_info.pitch = target->mode_info.bytes_per_pixel * width; - target->mode_info.number_of_colors = 256; /* Emulated palette. */ - target->mode_info.red_mask_size = 8; - target->mode_info.red_field_pos = 0; - target->mode_info.green_mask_size = 8; - target->mode_info.green_field_pos = 8; - target->mode_info.blue_mask_size = 8; - target->mode_info.blue_field_pos = 16; - target->mode_info.reserved_mask_size = 8; - target->mode_info.reserved_field_pos = 24; - - target->mode_info.blit_format = grub_video_get_blit_format (&target->mode_info); - - /* Calculate size needed for the data. */ - size = (width * target->mode_info.bytes_per_pixel) * height; - - target->data = grub_malloc (size); - if (! target->data) - { - grub_free (target); - return grub_errno; - } - - /* Clear render target with black and maximum transparency. */ - grub_memset (target->data, 0, size); - - /* TODO: Add render target to render target list. */ - - /* Save result to caller. */ - *result = target; + if (doublebuf_state.update_screen () != 0) + return grub_error (GRUB_ERR_INVALID_COMMAND, + "Double buffer update failed"); return GRUB_ERR_NONE; } static grub_err_t -grub_video_vbe_delete_render_target (struct grub_video_render_target *target) +grub_video_vbe_enable_double_buffering (int enable) { - /* If there is no target, then just return without error. */ - if (! target) - return GRUB_ERR_NONE; - - /* TODO: Delist render target from render target list. */ - - /* If this is software render target, free it's memory. */ - if (target->is_allocated) - grub_free (target->data); - - /* Free render target. */ - grub_free (target); - + double_buffering_init (enable); return GRUB_ERR_NONE; } static grub_err_t grub_video_vbe_set_active_render_target (struct grub_video_render_target *target) { - if (target == GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER) - { - render_target = &framebuffer.render_target; - - return GRUB_ERR_NONE; - } + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = &framebuffer.render_target; - if (target == GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "double buffering not implemented yet."); - - if (! target->data) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "invalid render target given."); - - render_target = target; - - return GRUB_ERR_NONE; + return grub_video_fb_set_active_render_target (target); } static grub_err_t -grub_video_vbe_get_active_render_target (struct grub_video_render_target **target) +grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) { - *target = render_target; + grub_err_t err; + err = grub_video_fb_get_info (mode_info); + if (err) + return err; + *framebuf = ((char *) framebuffer.ptr) + + doublebuf_state.page_size * doublebuf_state.displayed_page; return GRUB_ERR_NONE; } + static struct grub_video_adapter grub_video_vbe_adapter = { .name = "VESA BIOS Extension Video Driver", @@ -1602,24 +785,26 @@ static struct grub_video_adapter grub_video_vbe_adapter = .init = grub_video_vbe_init, .fini = grub_video_vbe_fini, .setup = grub_video_vbe_setup, - .get_info = grub_video_vbe_get_info, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_vbe_get_info_and_fini, .set_palette = grub_video_vbe_set_palette, - .get_palette = grub_video_vbe_get_palette, + .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_vbe_set_viewport, - .get_viewport = grub_video_vbe_get_viewport, - .map_color = grub_video_vbe_map_color, - .map_rgb = grub_video_vbe_map_rgb, - .map_rgba = grub_video_vbe_map_rgba, - .unmap_color = grub_video_vbe_unmap_color, - .fill_rect = grub_video_vbe_fill_rect, - .blit_bitmap = grub_video_vbe_blit_bitmap, - .blit_render_target = grub_video_vbe_blit_render_target, - .scroll = grub_video_vbe_scroll, + .get_viewport = grub_video_fb_get_viewport, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, .swap_buffers = grub_video_vbe_swap_buffers, - .create_render_target = grub_video_vbe_create_render_target, - .delete_render_target = grub_video_vbe_delete_render_target, + .enable_double_buffering = grub_video_vbe_enable_double_buffering, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, .set_active_render_target = grub_video_vbe_set_active_render_target, - .get_active_render_target = grub_video_vbe_get_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, .next = 0 }; diff --git a/video/video.c b/video/video.c index c22947b..b7df580 100644 --- a/video/video.c +++ b/video/video.c @@ -93,6 +93,24 @@ grub_video_get_info (struct grub_video_mode_info *mode_info) return grub_video_adapter_active->get_info (mode_info); } +/* Get information about active video mode. */ +grub_err_t +grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuffer) +{ + grub_err_t err; + + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "No video mode activated"); + + err = grub_video_adapter_active->get_info_and_fini (mode_info, framebuffer); + if (err) + return err; + + grub_video_adapter_active = 0; + return GRUB_ERR_NONE; +} + /* Determine optimized blitting formation for specified video mode info. */ enum grub_video_blit_format grub_video_get_blit_format (struct grub_video_mode_info *mode_info) @@ -335,6 +353,16 @@ grub_video_swap_buffers (void) return grub_video_adapter_active->swap_buffers (); } +/* Enable or disable double buffering. */ +grub_err_t +grub_video_enable_double_buffering (int enable) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "No video mode activated"); + + return grub_video_adapter_active->enable_double_buffering (enable); +} + /* Create new render target. */ grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, @@ -380,7 +408,7 @@ grub_video_get_active_render_target (struct grub_video_render_target **target) } grub_err_t -grub_video_set_mode (char *modestring, +grub_video_set_mode (const char *modestring, int NESTED_FUNC_ATTR (*hook) (grub_video_adapter_t p, struct grub_video_mode_info *mode_info)) { @@ -630,6 +658,8 @@ grub_video_set_mode (char *modestring, flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + flags |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + /* Try to initialize requested mode. Ignore any errors. */ grub_video_adapter_t p; @@ -654,6 +684,8 @@ grub_video_set_mode (char *modestring, if (err != GRUB_ERR_NONE) { p->fini (); + grub_dprintf ("video", "Failed to setup mode %dx%d (%x) on %s\n", + width, height, flags, p->name); grub_errno = GRUB_ERR_NONE; continue; } @@ -662,6 +694,8 @@ grub_video_set_mode (char *modestring, if (err != GRUB_ERR_NONE) { p->fini (); + grub_dprintf ("video", "Failed to get info: %dx%d (%x) on %s\n", + width, height, flags, p->name); grub_errno = GRUB_ERR_NONE; continue; } @@ -695,8 +729,6 @@ grub_video_set_mode (char *modestring, /* Initialize Video API module. */ GRUB_MOD_INIT(video_video) { - grub_video_adapter_active = 0; - grub_video_adapter_list = 0; } /* Finalize Video API module. */