From e29466b0b5835e6a9c8dc172114c34cbeab0925a Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 9 Feb 2017 02:25:12 +0100 Subject: [PATCH] WIP: rotate. --- grub-core/video/fb/fbblit.c | 165 +++++++++++----------------- grub-core/video/fb/fbblit_rot.c | 96 +++++++++++++++++ grub-core/video/fb/fbfill.c | 1 + grub-core/video/fb/video_fb.c | 186 +++++++++++++++++++++++++------- include/grub/video.h | 19 +++- 5 files changed, 329 insertions(+), 138 deletions(-) create mode 100644 grub-core/video/fb/fbblit_rot.c diff --git a/grub-core/video/fb/fbblit.c b/grub-core/video/fb/fbblit.c index d55924837..cc7a60c83 100644 --- a/grub-core/video/fb/fbblit.c +++ b/grub-core/video/fb/fbblit.c @@ -34,38 +34,36 @@ #include #include -/* Generic replacing blitter (slow). Works for every supported format. */ -static void -grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, - struct grub_video_fbblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y) +static inline grub_uint8_t +alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha) { - int i; - int j; - grub_uint8_t src_red; - grub_uint8_t src_green; - grub_uint8_t src_blue; - grub_uint8_t src_alpha; - grub_video_color_t src_color; - grub_video_color_t dst_color; - - for (j = 0; j < height; j++) - { - for (i = 0; i < width; i++) - { - src_color = get_pixel (src, i + offset_x, j + offset_y); + grub_uint16_t s; + grub_uint16_t h, l; + s = (fg * alpha) + (bg * (255 ^ alpha)); + /* Optimised division by 255. */ + h = s >> 8; + l = s & 0xff; + if (h + l >= 255) + h++; + return h; +} - grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, - &src_blue, &src_alpha); +#define SUFFIX(x) x +#define ADD_X 0 +#define ADD_Y 0 +#define TRANS_X(x, y) x +#define TRANS_Y(x, y) y +#include "fbblit_rot.c" - dst_color = grub_video_fb_map_rgba (src_red, src_green, - src_blue, src_alpha); +#define SUFFIX(x) x ## _90 +#define TRANS_X(x, y) (y) +#define TRANS_Y(x, y) (-(x)) +#include "fbblit_rot.c" - set_pixel (dst, x + i, y + j, dst_color); - } - } -} +#define SUFFIX(x) x ## _270 +#define TRANS_X(x, y) (-(y)) +#define TRANS_Y(x, y) (x) +#include "fbblit_rot.c" /* Block copy replacing blitter. Works with modes multiple of 8 bits. */ static void @@ -1145,78 +1143,6 @@ grub_video_fbblit_replace_index_RGB888 (struct grub_video_fbblit_info *dst, } } -static inline grub_uint8_t -alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha) -{ - grub_uint16_t s; - grub_uint16_t h, l; - s = (fg * alpha) + (bg * (255 ^ alpha)); - /* Optimised division by 255. */ - h = s >> 8; - l = s & 0xff; - if (h + l >= 255) - h++; - return h; -} - -/* Generic blending blitter. Works for every supported format. */ -static void -grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, - struct grub_video_fbblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y) -{ - int i; - int j; - - for (j = 0; j < height; j++) - { - for (i = 0; i < width; i++) - { - grub_uint8_t src_red; - grub_uint8_t src_green; - grub_uint8_t src_blue; - grub_uint8_t src_alpha; - grub_uint8_t dst_red; - grub_uint8_t dst_green; - grub_uint8_t dst_blue; - grub_uint8_t dst_alpha; - grub_video_color_t src_color; - grub_video_color_t dst_color; - - src_color = get_pixel (src, i + offset_x, j + offset_y); - grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, - &src_blue, &src_alpha); - - if (src_alpha == 0) - continue; - - if (src_alpha == 255) - { - dst_color = grub_video_fb_map_rgba (src_red, src_green, - src_blue, src_alpha); - set_pixel (dst, x + i, y + j, dst_color); - continue; - } - - dst_color = get_pixel (dst, x + i, y + j); - - grub_video_fb_unmap_color_int (dst, dst_color, &dst_red, - &dst_green, &dst_blue, &dst_alpha); - - dst_red = alpha_dilute (dst_red, src_red, src_alpha); - dst_green = alpha_dilute (dst_green, src_green, src_alpha); - dst_blue = alpha_dilute (dst_blue, src_blue, src_alpha); - - dst_alpha = src_alpha; - dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue, - dst_alpha); - - set_pixel (dst, x + i, y + j, dst_color); - } - } -} - /* Optimized blending blitter for RGBA8888 to BGRA8888. */ static void grub_video_fbblit_blend_BGRA8888_RGBA8888 (struct grub_video_fbblit_info *dst, @@ -1936,6 +1862,45 @@ grub_video_fb_dispatch_blit (struct grub_video_fbblit_info *target, unsigned int width, unsigned int height, int offset_x, int offset_y) { + if (target->mode_info->rotation == GRUB_VIDEO_ROTATE_90) + { + int nx = y; + int ny = target->mode_info->width - x; + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace_90 (target, source, nx, ny, width, height, + offset_x, offset_y); + return; + } + else + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_blend_90 (target, source, nx, ny, width, height, + offset_x, offset_y); + return; + } + } + if (target->mode_info->rotation == GRUB_VIDEO_ROTATE_270) + { + int nx = target->mode_info->height - y; + int ny = x; + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace_270 (target, source, nx, ny, width, height, + offset_x, offset_y); + return; + } + else + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_blend_270 (target, source, nx, ny, width, height, + offset_x, offset_y); + return; + } + } + if (oper == GRUB_VIDEO_BLIT_REPLACE) { /* Try to figure out more optimized version for replace operator. */ diff --git a/grub-core/video/fb/fbblit_rot.c b/grub-core/video/fb/fbblit_rot.c new file mode 100644 index 000000000..49ccffbce --- /dev/null +++ b/grub-core/video/fb/fbblit_rot.c @@ -0,0 +1,96 @@ +/* Generic replacing blitter (slow). Works for every supported format. */ +static void +SUFFIX(grub_video_fbblit_replace) (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t src_red; + grub_uint8_t src_green; + grub_uint8_t src_blue; + grub_uint8_t src_alpha; + grub_video_color_t src_color; + grub_video_color_t dst_color; + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + src_color = get_pixel (src, i + offset_x, j + offset_y); + + grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, + &src_blue, &src_alpha); + + dst_color = grub_video_fb_map_rgba (src_red, src_green, + src_blue, src_alpha); + + set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color); + } + } +} + +/* Generic blending blitter. Works for every supported format. */ +static void +SUFFIX(grub_video_fbblit_blend) (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t src_red; + grub_uint8_t src_green; + grub_uint8_t src_blue; + grub_uint8_t src_alpha; + grub_uint8_t dst_red; + grub_uint8_t dst_green; + grub_uint8_t dst_blue; + grub_uint8_t dst_alpha; + grub_video_color_t src_color; + grub_video_color_t dst_color; + + src_color = get_pixel (src, i + offset_x, j + offset_y); + grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, + &src_blue, &src_alpha); + + if (src_alpha == 0) + continue; + + if (src_alpha == 255) + { + dst_color = grub_video_fb_map_rgba (src_red, src_green, + src_blue, src_alpha); + set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color); + continue; + } + + dst_color = get_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j)); + + grub_video_fb_unmap_color_int (dst, dst_color, &dst_red, + &dst_green, &dst_blue, &dst_alpha); + + dst_red = alpha_dilute (dst_red, src_red, src_alpha); + dst_green = alpha_dilute (dst_green, src_green, src_alpha); + dst_blue = alpha_dilute (dst_blue, src_blue, src_alpha); + + dst_alpha = src_alpha; + dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue, + dst_alpha); + + set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color); + } + } +} + +#undef SUFFIX +#undef ADD_X +#undef ADD_Y +#undef TRANS_X +#undef TRANS_Y diff --git a/grub-core/video/fb/fbfill.c b/grub-core/video/fb/fbfill.c index 11816d07a..8c7fad9cd 100644 --- a/grub-core/video/fb/fbfill.c +++ b/grub-core/video/fb/fbfill.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Generic filler that works for every supported mode. */ static void diff --git a/grub-core/video/fb/video_fb.c b/grub-core/video/fb/video_fb.c index 1a602c8b2..439a198d0 100644 --- a/grub-core/video/fb/video_fb.c +++ b/grub-core/video/fb/video_fb.c @@ -830,7 +830,7 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, } static void -dirty (int y, int height) +dirty_untrans (int y, int height) { if (framebuffer.render_target != framebuffer.back_target) return; @@ -840,6 +840,66 @@ dirty (int y, int height) framebuffer.current_dirty.last_line = y + height; } +static void +dirty (int x, int width, int y, int height) +{ + if (framebuffer.render_target != framebuffer.back_target) + return; + if (framebuffer.render_target->mode_info.rotation == GRUB_VIDEO_ROTATE_90 + || framebuffer.render_target->mode_info.rotation == GRUB_VIDEO_ROTATE_270) + { + if (framebuffer.current_dirty.first_line > x) + framebuffer.current_dirty.first_line = x; + if (framebuffer.current_dirty.last_line < x + width) + framebuffer.current_dirty.last_line = x + width; + } + else + { + if (framebuffer.current_dirty.first_line > y) + framebuffer.current_dirty.first_line = y; + if (framebuffer.current_dirty.last_line < y + height) + framebuffer.current_dirty.last_line = y + height; + } +} + +static void +grub_video_fb_fill_rect_untrans (grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info target; + + target.mode_info = &framebuffer.render_target->mode_info; + target.data = framebuffer.render_target->data; + + grub_video_fb_fill_dispatch (&target, color, x, y, + width, height); + dirty_untrans (y, height); +} + +static grub_video_rect_t +grub_video_transform_rectangle (grub_video_rect_t r, const struct grub_video_mode_info *mode_info) +{ + grub_video_rect_t n; + switch (mode_info->rotation) + { + case GRUB_VIDEO_ROTATE_NONE: + return r; + case GRUB_VIDEO_ROTATE_90: + n.width = r.height; + n.height = r.width; + n.x = r.y; + n.y = mode_info->width - r.x - r.width; + return n; + case GRUB_VIDEO_ROTATE_270: + n.width = r.height; + n.height = r.width; + n.x = mode_info->height - r.y - r.height; + n.y = r.x; + return n; + } + return r; +} + grub_err_t grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, unsigned int width, unsigned int height) @@ -895,14 +955,22 @@ grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, x += area_x; y += area_y; - dirty (y, height); + dirty (x, width, y, height); /* Use fbblit_info to encapsulate rendering. */ target.mode_info = &framebuffer.render_target->mode_info; target.data = framebuffer.render_target->data; - grub_video_fb_fill_dispatch (&target, color, x, y, - width, height); + grub_video_rect_t orig = { + .x = x, + .y = y, + .width = width, + .height = height + }; + grub_video_rect_t tran = grub_video_transform_rectangle (orig, &framebuffer.render_target->mode_info); + + grub_video_fb_fill_dispatch (&target, color, tran.x, tran.y, + tran.width, tran.height); return GRUB_ERR_NONE; } @@ -1008,7 +1076,7 @@ grub_video_fb_blit_source (struct grub_video_fbblit_info *source, target.data = framebuffer.render_target->data; /* Do actual blitting. */ - dirty (y, height); + dirty (x, width, y, height); grub_video_fb_dispatch_blit (&target, source, oper, x, y, width, height, offset_x, offset_y); @@ -1052,42 +1120,70 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) int src_y; int dst_x; int dst_y; + grub_video_rect_t transformed_viewport; /* 1. Check if we have something to do. */ if ((dx == 0) && (dy == 0)) return GRUB_ERR_NONE; - width = framebuffer.render_target->viewport.width - grub_abs (dx); - height = framebuffer.render_target->viewport.height - grub_abs (dy); + transformed_viewport = grub_video_transform_rectangle (framebuffer.render_target->viewport, + &framebuffer.render_target->mode_info); - dirty (framebuffer.render_target->viewport.y, - framebuffer.render_target->viewport.height); + dirty (transformed_viewport.x, + transformed_viewport.width, + transformed_viewport.y, + transformed_viewport.height); + + switch (framebuffer.render_target->mode_info.rotation) + { + case GRUB_VIDEO_ROTATE_NONE: + break; + case GRUB_VIDEO_ROTATE_90: + { + int ndx = dy; + int ndy = -dx; + dx = ndx; + dy = ndy; + } + break; + case GRUB_VIDEO_ROTATE_270: + { + int ndx = -dy; + int ndy = dx; + dx = ndx; + dy = ndy; + } + break; + } + + width = transformed_viewport.width - grub_abs (dx); + height = transformed_viewport.height - grub_abs (dy); if (dx < 0) { - src_x = framebuffer.render_target->viewport.x - dx; - dst_x = framebuffer.render_target->viewport.x; + src_x = transformed_viewport.x - dx; + dst_x = transformed_viewport.x; } else { - src_x = framebuffer.render_target->viewport.x; - dst_x = framebuffer.render_target->viewport.x + dx; + src_x = transformed_viewport.x; + dst_x = transformed_viewport.x + dx; } if (dy < 0) { - src_y = framebuffer.render_target->viewport.y - dy; - dst_y = framebuffer.render_target->viewport.y; + src_y = transformed_viewport.y - dy; + dst_y = transformed_viewport.y; } else { - src_y = framebuffer.render_target->viewport.y; - dst_y = framebuffer.render_target->viewport.y + dy; + src_y = transformed_viewport.y; + dst_y = transformed_viewport.y + dy; } /* 2. Check if there is need to copy data. */ - if ((grub_abs (dx) < framebuffer.render_target->viewport.width) - && (grub_abs (dy) < framebuffer.render_target->viewport.height)) + if ((grub_abs (dx) < transformed_viewport.width) + && (grub_abs (dy) < transformed_viewport.height)) { /* 3. Move data in render target. */ struct grub_video_fbblit_info target; @@ -1109,7 +1205,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) src = (void *) grub_video_fb_get_video_ptr (&target, \ src_x, src_y); \ /* 3a. Move data upwards. */ \ - for (j = 0; j < height; j++) \ + for (j = 0; j < height; j++) \ { \ for (i = 0; i < linelen; i++) \ *(dst++) = *(src++); \ @@ -1128,7 +1224,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) src_y + height - 1); \ dst--; \ src--; \ - for (j = 0; j < height; j++) \ + for (j = 0; j < height; j++) \ { \ for (i = 0; i < linelen; i++) \ *(dst--) = *(src--); \ @@ -1178,27 +1274,27 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) /* 4a. Fill top & bottom parts. */ if (dy > 0) - grub_video_fb_fill_rect (color, 0, 0, framebuffer.render_target->viewport.width, dy); + grub_video_fb_fill_rect_untrans (color, 0, 0, transformed_viewport.width, dy); else if (dy < 0) { - if (framebuffer.render_target->viewport.height < grub_abs (dy)) - dy = -framebuffer.render_target->viewport.height; + if (transformed_viewport.height < grub_abs (dy)) + dy = -transformed_viewport.height; - grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy, - framebuffer.render_target->viewport.width, -dy); + grub_video_fb_fill_rect_untrans (color, 0, transformed_viewport.height + dy, + transformed_viewport.width, -dy); } /* 4b. Fill left & right parts. */ if (dx > 0) - grub_video_fb_fill_rect (color, 0, 0, - dx, framebuffer.render_target->viewport.height); + grub_video_fb_fill_rect_untrans (color, 0, 0, + dx, transformed_viewport.height); else if (dx < 0) { - if (framebuffer.render_target->viewport.width < grub_abs (dx)) - dx = -framebuffer.render_target->viewport.width; + if (transformed_viewport.width < grub_abs (dx)) + dx = -transformed_viewport.width; - grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0, - -dx, framebuffer.render_target->viewport.height); + grub_video_fb_fill_rect_untrans (color, transformed_viewport.width + dx, 0, + -dx, transformed_viewport.height); } return GRUB_ERR_NONE; @@ -1228,6 +1324,8 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, /* TODO: Implement other types too. Currently only 32bit render targets are supported. */ + target->mode_info.rotation = GRUB_VIDEO_ROTATE_NONE; + /* Mark render target as allocated. */ target->is_allocated = 1; @@ -1253,6 +1351,8 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, /* Setup render target format. */ target->mode_info.width = width; target->mode_info.height = height; + target->mode_info.original_width = width; + target->mode_info.original_height = height; switch (mode_type) { case GRUB_VIDEO_MODE_TYPE_INDEX_COLOR @@ -1336,6 +1436,17 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info)); + target->mode_info.rotation = GRUB_VIDEO_ROTATE_270; ///!!! + target->mode_info.original_width = target->mode_info.width; + target->mode_info.original_height = target->mode_info.height; + + if (target->mode_info.rotation == GRUB_VIDEO_ROTATE_90 + || target->mode_info.rotation == GRUB_VIDEO_ROTATE_270) + { + target->mode_info.width = target->mode_info.original_height; + target->mode_info.height = target->mode_info.original_width; + } + /* Reset viewport, region and area to match new mode. */ target->viewport.x = 0; target->viewport.y = 0; @@ -1356,9 +1467,9 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ target->area_offset_y = 0; /* Clear render target with black and maximum transparency. */ - for (y = 0; y < mode_info->height; y++) + for (y = 0; y < target->mode_info.original_height; y++) grub_memset (target->data + mode_info->pitch * y, 0, - mode_info->bytes_per_pixel * mode_info->width); + mode_info->bytes_per_pixel * target->mode_info.original_width); /* Save result to caller. */ *result = target; @@ -1427,7 +1538,7 @@ doublebuf_blit_update_screen (void) * (framebuffer.current_dirty.last_line - framebuffer.current_dirty.first_line)); framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; + = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.last_line = 0; return GRUB_ERR_NONE; @@ -1636,7 +1747,7 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, framebuffer.render_page = 0; framebuffer.set_page = 0; framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; + = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.last_line = 0; mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; @@ -1668,6 +1779,9 @@ grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info, grub_memcpy (mode_info, &(framebuffer.back_target->mode_info), sizeof (*mode_info)); + mode_info->width = framebuffer.back_target->mode_info.original_width; + mode_info->height = framebuffer.back_target->mode_info.original_height; + /* We are about to load a kernel. Switch back to page zero, since some kernel drivers expect that. */ if (framebuffer.set_page && framebuffer.displayed_page != 0) diff --git a/include/grub/video.h b/include/grub/video.h index 52c3fd71e..49d23fc17 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -75,6 +75,13 @@ typedef enum grub_video_mode_type GRUB_VIDEO_MODE_TYPE_INFO_MASK = 0x00FF0000, } grub_video_mode_type_t; +enum grub_video_rotation + { + GRUB_VIDEO_ROTATE_NONE, + GRUB_VIDEO_ROTATE_90, + GRUB_VIDEO_ROTATE_270, + }; + /* 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 \ @@ -122,12 +129,20 @@ enum grub_video_blit_operators struct grub_video_mode_info { - /* Width of the screen. */ + /* Width of the screen, before the rotation. */ + unsigned int original_width; + + /* Height of the screen, before the rotation. */ + unsigned int original_height; + + /* Width of the screen, after the rotation. */ unsigned int width; - /* Height of the screen. */ + /* Height of the screen, after the rotation. */ unsigned int height; + enum grub_video_rotation rotation; + /* Mode type bitmask. Contains information like is it Index color or RGB mode. */ grub_video_mode_type_t mode_type; -- 2.47.2