grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] PRELIMINARY framebuffer rotation patch


From: Kyle Bader
Subject: [PATCH] PRELIMINARY framebuffer rotation patch
Date: Mon, 27 May 2024 23:16:54 -0600

From 79d5934af355f360a78438e0132b788a0f626ec9 Mon Sep 17 00:00:00 2001
From: kbader94 <kyle.bader94@gmail.com>
Date: Sun, 26 May 2024 13:17:45 -0600
Subject: [PATCH] PRELIMINARY framebuffer rotation patch

This patch contains the minimum changes required to implement framebuffer orientation rotation.

More and more frequently GNU/linux users are using nonstandard display orientations, such as portrait orientations. While linux supports rotating the framebuffer on startup via the fbcon=rotate:<val> paramater, this parameter has no effect on GRUB, and GRUB has no alternative parameters to set the framebuffer rotation. This feature has previously been proposed, with an attempted patch here: https://lists.gnu.org/archive/html/grub-devel/2010-02/msg00158.html.

This patch implements a basic framebuffer rotation by:
   *  adding a rotation env_var
   *  modifying the dirty function to support 2d dirty regions, with accompanying changes to doublebuf_pageflipping_update_screen and doublebuf_blit_update_screen, and associated init functions.
   *  modifying mode_info to contain orignal_width and origina_height
   *  modifying the blitting function to transform coordinates to account for rotation
   *  modifying the fbfill function as above
   *  modifying the scroll function as above
   *  modifying individual drivers(bochs.c) to use the original_sizes instead of the width and height

grub-core/video/fb/video_fb.c - dirty changes:

   The current implementation of the dirty function only tracks changes to the y coordinates. This means updating even a small 2d region such as a single 16*16 character requires updating the entire <screen_width>*16 pixels. This patch revises the dirty methods to operate on 2d regions. This means the doublebuf functions responsible for updating the screen, doublebuf_blit_update_screen and doublebuf_pageflipping_update_screen, also have to be changed so they make multiple copies, one for each row of pixels to be updated, instead of a single copy for the entire contigous block of pixels contained in the framebuffer.

include/grub/video.h mode_info - changes

   We need to differentiate the physical framebuffer size from the render area size, which is done by adding original_height and orignal_width members to mode_info. Additionally, the rotation for the framebuffer will be spefied by setting the new rotation member contained within mode_info

grub-core/video/fb/fbutil.c - add trans_x and trans_y helper functions

   These functions are used by the fbblit and fbfill functions to transform coordinates prior to drawing.

grub-core/video/fb/fbblit.c - check target rotation and transform appropriately

   As an initial proof of concept only the slow blitting operation is implemented, however the same logic can easily be applied to other blitting operations and color formats.

grub-core/video/bochs.c - video driver changes requires

   Some video drivers may need changes to account for the fact that mode_info.width and height may not reflect the actual width and height of the screen and/or framebuffer. The only driver I've implemented and tested these changes on is the bochs.c driver.

Further work required

   This patch is not final and is presented as a proof of concept, for your consideration and feedback. It works in qemu, however other drivers besides bochs.c have yet to be implemented. Additionally other blitting formats can be made to support rotations with optimized blitters, currently only the generic blitter is supported.

Setting the rotation

   The rotation can be set by setting the rotation env var to 0, 90, or 270 by either entering the command set rotation=<rotation>, or placing the command in the grub.cfg file.

 Changes to be committed:
modified:   grub-core/video/bochs.c
modified:   grub-core/video/fb/fbblit.c
modified:   grub-core/video/fb/fbfill.c
modified:   grub-core/video/fb/fbutil.c
modified:   grub-core/video/fb/video_fb.c
modified:   include/grub/fbutil.h
modified:   include/grub/video.h
---
 grub-core/video/bochs.c       |   4 +-
 grub-core/video/fb/fbblit.c   |  63 +++++--
 grub-core/video/fb/fbfill.c   |  29 +++-
 grub-core/video/fb/fbutil.c   |  56 +++++++
 grub-core/video/fb/video_fb.c | 307 +++++++++++++++++++++-------------
 include/grub/fbutil.h         |   9 +
 include/grub/video.h          |  20 ++-
 7 files changed, 350 insertions(+), 138 deletions(-)

diff --git a/grub-core/video/bochs.c b/grub-core/video/bochs.c
index edc651697..5937d7486 100644
--- a/grub-core/video/bochs.c
+++ b/grub-core/video/bochs.c
@@ -172,7 +172,7 @@ grub_video_bochs_video_fini (void)
 static grub_err_t
 doublebuf_pageflipping_set_page (int page)
 {
-  int start = framebuffer.mode_info.height * page;
+  int start = framebuffer.mode_info.original_height * page;
 
   vbe_write (start, BOCHS_VBE_Y_OFFSET);
   return GRUB_ERR_NONE;
@@ -323,6 +323,8 @@ grub_video_bochs_setup (unsigned int width, unsigned int height,
   /* Fill mode info details.  */
   framebuffer.mode_info.width = width;
   framebuffer.mode_info.height = height;
+  framebuffer.mode_info.original_width = width;
+  framebuffer.mode_info.original_height = height;
   framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
   framebuffer.mode_info.bpp = depth;
   framebuffer.mode_info.bytes_per_pixel = bytes_per_pixel;
diff --git a/grub-core/video/fb/fbblit.c b/grub-core/video/fb/fbblit.c
index 1010ef393..046b6f1b2 100644
--- a/grub-core/video/fb/fbblit.c
+++ b/grub-core/video/fb/fbblit.c
@@ -37,9 +37,9 @@
 /* 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)
+   struct grub_video_fbblit_info *src,
+   int x, int y, int width, int height,
+   int offset_x, int offset_y)
 {
   int i;
   int j;
@@ -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);
  }
     }
 }
@@ -1162,9 +1163,9 @@ alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha)
 /* 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)
+                 struct grub_video_fbblit_info *src,
+                 int x, int y, int width, int height,
+                 int offset_x, int offset_y)
 {
   int i;
   int j;
@@ -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,48 @@ 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;
+ }
+    }
+
+  /* TODO: dispatch 180 */
+
+  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..051dcded0 100644
--- a/grub-core/video/fb/fbfill.c
+++ b/grub-core/video/fb/fbfill.c
@@ -33,6 +33,7 @@
 #include <grub/types.h>
 #include <grub/safemath.h>
 #include <grub/video.h>
+#include <grub/misc.h>
 
 /* Generic filler that works for every supported mode.  */
 static void
@@ -185,28 +186,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..4936a82ba 100644
--- a/grub-core/video/fb/fbutil.c
+++ b/grub-core/video/fb/fbutil.c
@@ -149,3 +149,59 @@ 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:
+    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 fa4ebde26..438bfde00 100644
--- a/grub-core/video/fb/video_fb.c
+++ b/grub-core/video/fb/video_fb.c
@@ -26,18 +26,12 @@
 #include <grub/bitmap.h>
 #include <grub/dl.h>
 #include <grub/safemath.h>
+#include <grub/env.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
 typedef grub_err_t (*grub_video_fb_doublebuf_update_screen_t) (void);
 typedef volatile void *framebuf_t;
-
-struct dirty
-{
-  int first_line;
-  int last_line;
-};
-
 static struct
 {
   struct grub_video_fbrender_target *render_target;
@@ -47,8 +41,8 @@ static struct
 
   unsigned int palette_size;
 
-  struct dirty current_dirty;
-  struct dirty previous_dirty;
+  struct grub_video_rect current_dirty;
+  struct grub_video_rect previous_dirty;
 
   /* For page flipping strategy.  */
   int displayed_page;           /* The page # that is the front buffer.  */
@@ -831,14 +825,38 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source,
 }
 
 static void
-dirty (int y, int height)
+dirty (int x, int y, int width, int height)
 {
+  grub_video_rect_t *current_dirty = &framebuffer.current_dirty;
+  /* TODO: convert this and other functions to use rect type for cleaner code */
+  grub_video_rect_t additive_rect = {
+    .x = x,
+    .y = y,
+    .width = width,
+    .height = height
+  };
+
+  grub_video_rect_t transformed_add_rect = grub_video_transform_rectangle (additive_rect, &framebuffer.render_target->mode_info);
+
   if (framebuffer.render_target != framebuffer.back_target)
     return;
-  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;
+
+  /* extend transformed_add_rect bounds if dirty x min is less than current x */
+  if (transformed_add_rect.x < current_dirty->x)
+    current_dirty->x = transformed_add_rect.x;
+
+  /* extend transformed_add_rect bounds if dirty x max is greater than current width */
+  if (transformed_add_rect.x + transformed_add_rect.width > current_dirty->x + current_dirty->width)
+    current_dirty->width = (transformed_add_rect.x + transformed_add_rect.width) - current_dirty->x;
+
+  /* extend transformed_add_rect bounds if dirty y min is less than current y */
+  if (transformed_add_rect.y < current_dirty->y)
+    current_dirty->y = transformed_add_rect.y;
+
+  /* extend transformed_add_rect bounds if dirty y max is greater than current height */
+  if (transformed_add_rect.y + transformed_add_rect.height > current_dirty->y + current_dirty->height)
+    current_dirty->height = (transformed_add_rect.y + transformed_add_rect.height) - current_dirty->y;
+
 }
 
 grub_err_t
@@ -896,14 +914,13 @@ grub_video_fb_fill_rect (grub_video_color_t color, int x, int y,
   x += area_x;
   y += area_y;
 
-  dirty (y, height);
-
   /* Use fbblit_info to encapsulate rendering.  */
   target.mode_info = &framebuffer.render_target->mode_info;
   target.data = ""> 
+  dirty (x, y, width, height);
   grub_video_fb_fill_dispatch (&target, color, x, y,
-       width, height);
+                        width, height);
   return GRUB_ERR_NONE;
 }
 
@@ -1008,8 +1025,7 @@ grub_video_fb_blit_source (struct grub_video_fbblit_info *source,
   target.mode_info = &framebuffer.render_target->mode_info;
   target.data = ""> 
-  /* Do actual blitting.  */
-  dirty (y, height);
+  dirty (x, y, width, height);
   grub_video_fb_dispatch_blit (&target, source, oper, x, y, width, height,
                                offset_x, offset_y);
 
@@ -1049,6 +1065,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;
@@ -1058,37 +1076,42 @@ 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);
-
-  dirty (framebuffer.render_target->viewport.y,
+  dirty (framebuffer.render_target->viewport.x,
+ framebuffer.render_target->viewport.y,
+ framebuffer.render_target->viewport.width,
  framebuffer.render_target->viewport.height);
 
-  if (dx < 0)
+  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);
+
+  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;
@@ -1103,14 +1126,14 @@ 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); \
   src = "" *) 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++);                             \
@@ -1129,7 +1152,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--);                                 \
@@ -1186,12 +1209,12 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
         dy = -framebuffer.render_target->viewport.height;
 
       grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy,
-                                framebuffer.render_target->viewport.width, -dy);
+       framebuffer.render_target->viewport.width, -dy);
     }
 
   /* 4b. Fill left & right parts.  */
   if (dx > 0)
-    grub_video_fb_fill_rect (color, 0, 0,
+   grub_video_fb_fill_rect (color, 0, 0,
                               dx, framebuffer.render_target->viewport.height);
   else if (dx < 0)
     {
@@ -1199,7 +1222,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
         dx = -framebuffer.render_target->viewport.width;
 
       grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0,
-                                -dx, framebuffer.render_target->viewport.height);
+       -dx, framebuffer.render_target->viewport.height);
     }
 
   return GRUB_ERR_NONE;
@@ -1229,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;
 
@@ -1254,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
@@ -1337,29 +1365,51 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ
 
   grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info));
 
+  const char *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, "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;
@@ -1413,31 +1463,34 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
-doublebuf_blit_update_screen (void)
-{
-  if (framebuffer.current_dirty.first_line
-      <= framebuffer.current_dirty.last_line)
-    {
-      grub_size_t copy_size;
-
-      if (grub_sub (framebuffer.current_dirty.last_line,
-    framebuffer.current_dirty.first_line, &copy_size) ||
-  grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, &copy_size))
- {
-  /* Shouldn't happen, but if it does we've a bug. */
-  return GRUB_ERR_BUG;
- }
-
-      grub_memcpy ((char *) framebuffer.pages[0] + framebuffer.current_dirty.first_line *
-   framebuffer.back_target->mode_info.pitch,
-   (char *) framebuffer.back_target->data + framebuffer.current_dirty.first_line *
-   framebuffer.back_target->mode_info.pitch,
-   copy_size);
+/* transfer 2D regions, one row of pixels at a time */
+static grub_err_t doublebuf_blit_update_screen(void) {
+  if (framebuffer.current_dirty.height > 0 || framebuffer.current_dirty.width > 0) {
+    /* byte size of transformed_add_rect row */
+    grub_size_t row_size = framebuffer.current_dirty.width * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* address of transformed_add_rect's origin on render target */
+    char *src_base = (char *)framebuffer.back_target->data +
+                     framebuffer.current_dirty.y * framebuffer.back_target->mode_info.pitch +
+                     framebuffer.current_dirty.x * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* address of transformed_add_rect's origin on display target */
+    char *dst_base = (char *)framebuffer.pages[0] +
+                     framebuffer.current_dirty.y * framebuffer.back_target->mode_info.pitch +
+                     framebuffer.current_dirty.x * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* blit each row from render_target to display */
+    grub_size_t pitch = framebuffer.back_target->mode_info.pitch;
+    for (unsigned int y = 0; y < framebuffer.current_dirty.height; y++) {
+      grub_memcpy(dst_base + y * pitch, src_base + y * pitch, row_size);
     }
-  framebuffer.current_dirty.first_line
-    = framebuffer.back_target->mode_info.height;
-  framebuffer.current_dirty.last_line = 0;
+  }
+
+  /* reset transformed_add_rect */
+  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.original_width;
+  framebuffer.current_dirty.width = 0;
 
   return GRUB_ERR_NONE;
 }
@@ -1470,62 +1523,74 @@ 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.first_line = mode_info.height;
-  framebuffer.current_dirty.last_line = 0;
+  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.original_width;
+  framebuffer.current_dirty.width = 0;
 
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
-doublebuf_pageflipping_update_screen (void)
+static grub_err_t
+doublebuf_pageflipping_update_screen(void)
 {
   int new_displayed_page;
   grub_err_t err;
-  int first_line, last_line;
-
-  first_line = framebuffer.current_dirty.first_line;
-  last_line = framebuffer.current_dirty.last_line;
-  if (first_line > framebuffer.previous_dirty.first_line)
-    first_line = framebuffer.previous_dirty.first_line;
-  if (last_line < framebuffer.previous_dirty.last_line)
-    last_line = framebuffer.previous_dirty.last_line;
 
-  if (first_line <= last_line)
-    {
-      grub_size_t copy_size;
-
-      if (grub_sub (last_line, first_line, &copy_size) ||
-  grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, &copy_size))
- {
-  /* Shouldn't happen, but if it does we've a bug. */
-  return GRUB_ERR_BUG;
- }
-
-      grub_memcpy ((char *) framebuffer.pages[framebuffer.render_page] + first_line *
-   framebuffer.back_target->mode_info.pitch,
-   (char *) framebuffer.back_target->data + first_line *
-   framebuffer.back_target->mode_info.pitch,
-   copy_size);
+  /* compare current and previous dirty_rects, and use greatest extents */
+  unsigned int min_x = framebuffer.current_dirty.x;
+  unsigned int min_y = framebuffer.current_dirty.y;
+  unsigned int max_x = framebuffer.current_dirty.x + framebuffer.current_dirty.width;
+  unsigned int max_y = framebuffer.current_dirty.y + framebuffer.current_dirty.height;  
+  if (framebuffer.previous_dirty.x < min_x)
+    min_x = framebuffer.previous_dirty.x;
+  if (framebuffer.previous_dirty.y < min_y)
+    min_y = framebuffer.previous_dirty.y;
+  if (framebuffer.previous_dirty.x + framebuffer.previous_dirty.width > max_x)
+    max_x = framebuffer.previous_dirty.x + framebuffer.previous_dirty.width;
+  if (framebuffer.previous_dirty.y + framebuffer.previous_dirty.height > max_y)
+    max_y = framebuffer.previous_dirty.y + framebuffer.previous_dirty.height;
+  int dirty_width = max_x - min_x;
+  int dirty_height = max_y - min_y;
+
+  /* check if there is anything to do */
+  if (dirty_width > 0 && dirty_height > 0) {
+    /* byte size of dirty row */
+    grub_size_t row_size = dirty_width * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* render target base address */
+    char *src_base = (char *)framebuffer.back_target->data + min_y * framebuffer.back_target->mode_info.pitch + min_x * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* display target base address */
+    char *dst_base = (char *)framebuffer.pages[framebuffer.render_page] + min_y * framebuffer.back_target->mode_info.pitch + min_x * framebuffer.back_target->mode_info.bytes_per_pixel;
+
+    /* blit each row from render_target to display */
+    grub_size_t pitch = framebuffer.back_target->mode_info.pitch;
+    for (int y = 0; y < dirty_height; y++) {
+      grub_memcpy(dst_base + y * pitch, src_base + y * pitch, row_size);
     }
+  }
 
+  /* reset transformed_add_rect */
   framebuffer.previous_dirty = framebuffer.current_dirty;
-  framebuffer.current_dirty.first_line
-    = framebuffer.back_target->mode_info.height;
-  framebuffer.current_dirty.last_line = 0;
+  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.original_width;
+  framebuffer.current_dirty.width = 0;
 
-  /* Swap the page numbers in the framebuffer struct.  */
+  /* Swap the page numbers in the framebuffer struct. */
   new_displayed_page = framebuffer.render_page;
   framebuffer.render_page = framebuffer.displayed_page;
   framebuffer.displayed_page = new_displayed_page;
 
   err = framebuffer.set_page (framebuffer.displayed_page);
-  if (err)
+  if (err)
     {
-      /* Restore previous state.  */
-      framebuffer.render_page = framebuffer.displayed_page;
-      framebuffer.displayed_page = new_displayed_page;
-      return err;
-    }
+      /* Restore previous state. */
+    framebuffer.render_page = framebuffer.displayed_page;
+    framebuffer.displayed_page = new_displayed_page;
+    return err;
+  }
 
   return GRUB_ERR_NONE;
 }
@@ -1570,12 +1635,17 @@ doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info,
   framebuffer.pages[0] = page0_ptr;
   framebuffer.pages[1] = page1_ptr;
 
-  framebuffer.current_dirty.first_line
-    = framebuffer.back_target->mode_info.height;
-  framebuffer.current_dirty.last_line = 0;
-  framebuffer.previous_dirty.first_line
-    = framebuffer.back_target->mode_info.height;
-  framebuffer.previous_dirty.last_line = 0;
+  /* reset current_dirty rect */
+  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.original_width;
+  framebuffer.current_dirty.width = 0;
+
+   /* reset previous_dirty rect */
+  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.original_width;
+  framebuffer.previous_dirty.width = 0;
 
   /* Set the framebuffer memory data pointer and display the right page.  */
   err = set_page_in (framebuffer.displayed_page);
@@ -1661,9 +1731,11 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask,
   framebuffer.displayed_page = 0;
   framebuffer.render_page = 0;
   framebuffer.set_page = 0;
-  framebuffer.current_dirty.first_line
-    = framebuffer.back_target->mode_info.height;
-  framebuffer.current_dirty.last_line = 0;
+ /* reset transformed_add_rect */
+  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.original_width;
+  framebuffer.current_dirty.width = 0;
 
   mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
 
@@ -1694,6 +1766,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


reply via email to

[Prev in Thread] Current Thread [Next in Thread]