Add optional framebuffer rotation, which allows the user to specify the display output rotation
MODIFIED: fbutil.h include/grub - Add function declarations. see below. video.h include/grub struct grub_video_mode_info - Add int original_width, int original_height, enum grub_video_rotation rotation. fbblit.c grub-core/video/fb Modify grub_video_fb_dispatch_blit() to rotate blit coordinates. TODO: currently only the generic(slow) blitting operation is used when rotation is specified. This should be implementd for every blitting mode and operation. fbfill.c grub-core/video/fb Modify grub_video_fb_fill_dispatch() to rotate fill coordinates. fbutil.c grub-core/video/fb Add functions trans_x and trans_y to translate coordinates depending on mode_info.rotation Add function grub_video_transform_rectangle to transform video_rect depending on mode_info video_fb.c grub-core/video/fb Modify functions: dirty() to rotate dirty video_rect grub_video_fb_scroll() to rotate scroll grub_video_fb_create_render_target - use no rotation via mode_info by default. Render target's(such as text_layers) get rotated on the final blit to the actual framebuffer, thus we want a mode_info with NO rotation set to prevent "double rotation's". grub_video_fb_create_render_target_from_pointer - Check for rotation grub_env_var and set mode_info rotation, viewport, area, width, and height for render_target depending on the rotation specified. Other functions - update to use original_width or original_height where appropriate. Signed-off-by: kbader94 <kyle.bade...@gmail.com> --- grub-core/video/fb/fbblit.c | 71 ++++++++++++++++++++-- grub-core/video/fb/fbfill.c | 28 ++++++--- grub-core/video/fb/fbutil.c | 61 +++++++++++++++++++ grub-core/video/fb/video_fb.c | 107 +++++++++++++++++++++++----------- include/grub/fbutil.h | 9 +++ include/grub/video.h | 20 ++++++- 6 files changed, 247 insertions(+), 49 deletions(-) diff --git a/grub-core/video/fb/fbblit.c b/grub-core/video/fb/fbblit.c index 1010ef393..5dda0ffdb 100644 --- a/grub-core/video/fb/fbblit.c +++ b/grub-core/video/fb/fbblit.c @@ -62,7 +62,8 @@ grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, dst_color = grub_video_fb_map_rgba (src_red, src_green, src_blue, src_alpha); - set_pixel (dst, x + i, y + j, dst_color); + set_pixel (dst, x + trans_x(i, j, dst->mode_info), + y + trans_y(i,j, dst->mode_info), dst_color); } } } @@ -1195,11 +1196,11 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, { dst_color = grub_video_fb_map_rgba (src_red, src_green, src_blue, src_alpha); - set_pixel (dst, x + i, y + j, dst_color); + set_pixel (dst, x + trans_x(i, j, dst->mode_info), y + trans_y(i,j, dst->mode_info), dst_color); continue; } - dst_color = get_pixel (dst, x + i, y + j); + dst_color = get_pixel (dst, x + trans_x(i, j, dst->mode_info), y + trans_y(i,j, dst->mode_info)); grub_video_fb_unmap_color_int (dst, dst_color, &dst_red, &dst_green, &dst_blue, &dst_alpha); @@ -1212,7 +1213,7 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue, dst_alpha); - set_pixel (dst, x + i, y + j, dst_color); + set_pixel (dst, x + trans_x(i, j, dst->mode_info), y + trans_y(i,j, dst->mode_info), dst_color); } } } @@ -1936,6 +1937,68 @@ 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 - 1; + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace (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 (target, source, nx, ny, width, height, + offset_x, offset_y); + return; + } + } + + if (target->mode_info->rotation == GRUB_VIDEO_ROTATE_180) + { + int nx = target->mode_info->width - x - 1; + int ny = target->mode_info->height - y - 1; + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace (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 (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 - 1; + int ny = x; + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace (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 (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/fbfill.c b/grub-core/video/fb/fbfill.c index 49fa16b92..cab4d8acc 100644 --- a/grub-core/video/fb/fbfill.c +++ b/grub-core/video/fb/fbfill.c @@ -185,28 +185,38 @@ grub_video_fb_fill_dispatch (struct grub_video_fbblit_info *target, grub_video_color_t color, int x, int y, unsigned int width, unsigned int height) { + grub_video_rect_t orig = { + .x = x, + .y = y, + .width = width, + .height = height + }; + grub_video_rect_t tran = grub_video_transform_rectangle (orig, target->mode_info); + /* Try to figure out more optimized version. Note that color is already mapped to target format so we can make assumptions based on that. */ switch (target->mode_info->bytes_per_pixel) { case 4: - grub_video_fbfill_direct32 (target, color, x, y, - width, height); + grub_video_fbfill_direct32 (target, color, tran.x, tran.y, + tran.width, tran.height); return; case 3: - grub_video_fbfill_direct24 (target, color, x, y, - width, height); + grub_video_fbfill_direct24 (target, color, tran.x, tran.y, + tran.width, tran.height); + return; return; case 2: - grub_video_fbfill_direct16 (target, color, x, y, - width, height); + grub_video_fbfill_direct16 (target, color, tran.x, tran.y, + tran.width, tran.height); return; case 1: - grub_video_fbfill_direct8 (target, color, x, y, - width, height); + grub_video_fbfill_direct8 (target, color, tran.x, tran.y, + tran.width, tran.height); return; } /* No optimized version found, use default (slow) filler. */ - grub_video_fbfill (target, color, x, y, width, height); + grub_video_fbfill (target, color, tran.x, tran.y, + tran.width, tran.height); } diff --git a/grub-core/video/fb/fbutil.c b/grub-core/video/fb/fbutil.c index 25ef39f47..368356fd7 100644 --- a/grub-core/video/fb/fbutil.c +++ b/grub-core/video/fb/fbutil.c @@ -149,3 +149,64 @@ set_pixel (struct grub_video_fbblit_info *source, break; } } + +int +trans_x(int x,int y, struct grub_video_mode_info *mode_info) +{ + switch (mode_info->rotation) { + case GRUB_VIDEO_ROTATE_90: + return y; + case GRUB_VIDEO_ROTATE_180: + return -x; + case GRUB_VIDEO_ROTATE_270: + return -y; + case GRUB_VIDEO_ROTATE_NONE: + default: + return x; + } +} + +int +trans_y(int x, int y, struct grub_video_mode_info *mode_info) +{ + switch (mode_info->rotation) { + case GRUB_VIDEO_ROTATE_90: + return -x; + case GRUB_VIDEO_ROTATE_180: + return -y; + case GRUB_VIDEO_ROTATE_270: + return x; + case GRUB_VIDEO_ROTATE_NONE: + default: + return y; + } +} + +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_180: + n.width = r.width; + n.height = r.height; + n.x = mode_info->width - r.x - r.width; + n.y = mode_info->height - r.y - r.height; + 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; +} \ No newline at end of file diff --git a/grub-core/video/fb/video_fb.c b/grub-core/video/fb/video_fb.c index d17c47b11..377a9179d 100644 --- a/grub-core/video/fb/video_fb.c +++ b/grub-core/video/fb/video_fb.c @@ -26,6 +26,7 @@ #include <grub/bitmap.h> #include <grub/dl.h> #include <grub/safemath.h> +#include <grub/env.h> GRUB_MOD_LICENSE ("GPLv3+"); @@ -826,15 +827,17 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, static void dirty (int x, int y, int width, int height) { - + grub_video_rect_t *current_dirty = &framebuffer.current_dirty; - grub_video_rect_t dirty_rect = { + grub_video_rect_t additive_rect = { .x = x, .y = y, .width = width, .height = height }; + grub_video_rect_t dirty_rect = grub_video_transform_rectangle (additive_rect, &framebuffer.render_target->mode_info); + if (framebuffer.render_target != framebuffer.back_target) return; @@ -1063,6 +1066,8 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) { int width; int height; + int tx; /* transformed scroll coords */ + int ty; /* transformed scroll coords */ int src_x; int src_y; int dst_x; @@ -1072,39 +1077,41 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) 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); + tx = trans_x(dx,dy, &framebuffer.render_target->mode_info); + ty = trans_y(dx,dy, &framebuffer.render_target->mode_info); + width = framebuffer.render_target->mode_info.original_width - grub_abs (tx); + height = framebuffer.render_target->mode_info.original_height - grub_abs (ty); dirty (framebuffer.render_target->viewport.x, framebuffer.render_target->viewport.y, - framebuffer.render_target->viewport.width, + framebuffer.render_target->viewport.width, framebuffer.render_target->viewport.height); - if (dx < 0) + if (tx < 0) { - src_x = framebuffer.render_target->viewport.x - dx; + src_x = framebuffer.render_target->viewport.x - tx; dst_x = framebuffer.render_target->viewport.x; } else { src_x = framebuffer.render_target->viewport.x; - dst_x = framebuffer.render_target->viewport.x + dx; + dst_x = framebuffer.render_target->viewport.x + tx; } - if (dy < 0) + if (ty < 0) { - src_y = framebuffer.render_target->viewport.y - dy; + src_y = framebuffer.render_target->viewport.y - ty; dst_y = framebuffer.render_target->viewport.y; } else { src_y = framebuffer.render_target->viewport.y; - dst_y = framebuffer.render_target->viewport.y + dy; + dst_y = framebuffer.render_target->viewport.y + ty; } /* 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 (tx) < framebuffer.render_target->viewport.width) + && (grub_abs (ty) < framebuffer.render_target->viewport.height)) { /* 3. Move data in render target. */ struct grub_video_fbblit_info target; @@ -1119,7 +1126,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) linelen = width * target.mode_info->bytes_per_pixel; #define DO_SCROLL \ /* Check vertical direction of the move. */ \ - if (dy < 0 || (dy == 0 && dx < 0)) \ + if (ty < 0 || (ty == 0 && tx < 0)) \ { \ dst = (void *) grub_video_fb_get_video_ptr (&target, \ dst_x, dst_y); \ @@ -1245,6 +1252,9 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, /* TODO: Implement other types too. Currently only 32bit render targets are supported. */ +/* only used by text_layers, so do not rotate */ + target->mode_info.rotation = GRUB_VIDEO_ROTATE_NONE; + /* Mark render target as allocated. */ target->is_allocated = 1; @@ -1270,6 +1280,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 @@ -1332,6 +1344,7 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ { struct grub_video_fbrender_target *target; unsigned y; + const char *rot_env; #ifndef GRUB_HAVE_UNALIGNED_ACCESS if (!(mode_info->bytes_per_pixel & (mode_info->bytes_per_pixel - 1)) @@ -1352,30 +1365,53 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ target->data = ptr; grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info)); + rot_env = grub_env_get("rotation"); + + if (!rot_env) { + target->mode_info.rotation = GRUB_VIDEO_ROTATE_NONE; + } else if (grub_strcmp(rot_env, "90") == 0) { + target->mode_info.rotation = GRUB_VIDEO_ROTATE_90; + } else if (grub_strcmp(rot_env, "180") == 0) { + target->mode_info.rotation = GRUB_VIDEO_ROTATE_180; + } else if (grub_strcmp(rot_env, "270") == 0) { + target->mode_info.rotation = GRUB_VIDEO_ROTATE_270; + } else { + target->mode_info.rotation = GRUB_VIDEO_ROTATE_NONE; + } + + target->mode_info.original_width = mode_info->width; + target->mode_info.original_height = 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; - target->viewport.width = mode_info->width; - target->viewport.height = mode_info->height; + target->viewport.width = target->mode_info.width; + target->viewport.height = target->mode_info.height; target->region.x = 0; target->region.y = 0; - target->region.width = mode_info->width; - target->region.height = mode_info->height; + target->region.width = target->mode_info.width; + target->region.height = target->mode_info.height; target->area_enabled = 0; target->area.x = 0; target->area.y = 0; - target->area.width = mode_info->width; - target->area.height = mode_info->height; + target->area.width = target->mode_info.width; + target->area.height = target->mode_info.height; target->area_offset_x = 0; 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; @@ -1455,9 +1491,9 @@ doublebuf_blit_update_screen (void) } /* reset current_dirty rect */ - framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.height = 0; - framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.current_dirty.width = 0; return GRUB_ERR_NONE; @@ -1491,9 +1527,9 @@ grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **back, framebuffer.pages[0] = framebuf; framebuffer.displayed_page = 0; framebuffer.render_page = 0; - framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.height = 0; - framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.current_dirty.width = 0; return GRUB_ERR_NONE; @@ -1546,9 +1582,9 @@ doublebuf_pageflipping_update_screen (void) /* reset current_dirty rect */ framebuffer.previous_dirty = framebuffer.current_dirty; - framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.height = 0; - framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.current_dirty.width = 0; /* Swap the page numbers in the framebuffer struct. */ @@ -1609,15 +1645,15 @@ doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info, framebuffer.pages[1] = page1_ptr; /* reset current_dirty rect */ - framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.height = 0; - framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.current_dirty.width = 0; /* reset previous_dirty rect */ - framebuffer.previous_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.previous_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.previous_dirty.height = 0; - framebuffer.previous_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.previous_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.previous_dirty.width = 0; /* Set the framebuffer memory data pointer and display the right page. */ @@ -1705,9 +1741,9 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, framebuffer.render_page = 0; framebuffer.set_page = 0; /* reset transformed_add_rect */ - framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.original_height; framebuffer.current_dirty.height = 0; - framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.original_width; framebuffer.current_dirty.width = 0; mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; @@ -1739,6 +1775,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/fbutil.h b/include/grub/fbutil.h index 78a1ab3b4..ad625bff9 100644 --- a/include/grub/fbutil.h +++ b/include/grub/fbutil.h @@ -61,4 +61,13 @@ grub_video_color_t get_pixel (struct grub_video_fbblit_info *source, void set_pixel (struct grub_video_fbblit_info *source, unsigned int x, unsigned int y, grub_video_color_t color); +int +trans_x(int x,int y, struct grub_video_mode_info *mode_info); + +int +trans_y(int x, int y, struct grub_video_mode_info *mode_info); + +grub_video_rect_t grub_video_transform_rectangle (grub_video_rect_t r, + const struct grub_video_mode_info *mode_info); + #endif /* ! GRUB_VBEUTIL_MACHINE_HEADER */ diff --git a/include/grub/video.h b/include/grub/video.h index 9dac0f379..f05e70b26 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -75,6 +75,14 @@ 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_180, + 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 +130,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.34.1 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel