grub-devel
[Top][All Lists]
Advanced

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

[patch] background_image: image centering and scaling


From: Olaf Mandel
Subject: [patch] background_image: image centering and scaling
Date: Thu, 28 Aug 2008 18:46:40 -0700
User-agent: IceDove 1.5.0.14eol (X11/20080724)

Hello,

on (some) computers with LCD monitors, the image is not automatically
scaled to full screen if the video mode is set to a lower resolution. So
the user has to set the gfxmode variable to the correct value in this
case. But then the background image provided by their distribution needs
to be rescaled by the user to fit the new screen resolution of grub.

It would be much preferable (IMO) to have grub2 rescale an existing
image automatically on loading. This seems to be the cleaner, more
sustainable solution: no extra files in the file system, works nicely
even if the user changes the screen resolution manually, ...

This patch adds two new functions to video/bitmap.c:
grub_video_bitmap_scale() and grub_video_bitmap_scale_keepaspect(). They
are called from term/gfxterm.c after reading in the bitmap in
grub_gfxterm_background_image_cmd(). At the moment, the scaling
algorithm used is very simple, no interpolation is used. But this should
be a good starting point to expand into smooth scaling later.

Additionally, this patch adds the ability to place the bitmap at any
position of the screen, not only in the top/left corner. This is done by
extending redraw_screen_rect() to also clear the areas to the left and
top of the image.

These changes can be controlled at by editing term/gfxterm.c: the three
preprocessor variables BITMAP_CENTERED, BITMAP_SCALED and
BITMAP_KEEPASPECTRATIO default to 1, but setting them to 0 restores the
old behavior.

This patch fixes one minor bug in grub_gfxterm_background_image_cmd():
the stored height of the bitmap was its width. This caused the screen to
not be cleared correctly in some cases.

TODO:
* Use a better scaling algorithm (linear or bi-linear interpolation).
* Change the scaling algorithm to work in-place. At the moment, a new
memory location is acquired, and the old one is freed after copying
(change malloc -> realloc).

Best regards,
Olaf Mandel
-- 
Olaf Mandel   <address@hidden>   <http://www.olaf.mandel.name/>
PGP key:      1024D/33398848 2002-09-19
Fingerprint:  0E33 BEA6 1A71 9C5E 62BD  FC0E 99A7 D2C6 3339 8848
Index: video/bitmap.c
===================================================================
--- video/bitmap.c      (revision 1832)
+++ video/bitmap.c      (working copy)
@@ -245,6 +245,82 @@
   return bitmap->data;
 }
 
+grub_err_t
+grub_video_bitmap_scale (struct grub_video_bitmap **out,
+                         struct grub_video_bitmap *in,
+                         unsigned int width, unsigned int height)
+{
+  grub_err_t res;
+  unsigned int x, y;
+  unsigned int w, h;
+
+  if (!in)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid argument.");
+  w = in->mode_info.width;
+  h = in->mode_info.height;
+
+  res = grub_video_bitmap_create (out, width, height,
+                                  in->mode_info.blit_format);
+  if (res != GRUB_ERR_NONE)
+    return res;
+
+  /* This is a very simple scaling, just copying the pixel value of the
+   * pixel to the top / left of the correct position.  */
+  for (y = 0; y < height; ++y)
+  {
+    unsigned int offsy = y * (*out)->mode_info.pitch;
+    unsigned int oldy;
+    unsigned int oldoffsy;
+
+    oldy = y * h / height;
+    oldoffsy = oldy * in->mode_info.pitch;
+
+    for (x = 0; x < width; ++x)
+    {
+      unsigned int offsx = x * in->mode_info.bytes_per_pixel;
+      unsigned int oldx;
+      unsigned int oldoffsx;
+
+      oldx = x * w / width;
+      oldoffsx = oldx * in->mode_info.bytes_per_pixel;
+
+      grub_memcpy ((char *)(*out)->data + offsy + offsx,
+                   (char *)in->data + oldoffsy + oldoffsx,
+                   in->mode_info.bytes_per_pixel);
+    }
+  }
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_video_bitmap_scale_keepaspect (struct grub_video_bitmap **out,
+                                    struct grub_video_bitmap *in,
+                                    unsigned int maxwidth,
+                                    unsigned int maxheight)
+{
+  unsigned int w;
+  unsigned int h;
+  unsigned int width = maxwidth;
+  unsigned int height = maxheight;
+
+  if (!in)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid argument.");
+  w = in->mode_info.width;
+  h = in->mode_info.height;
+
+  /* Is the new region wider than the old bitmap? Use products instead of
+   * fractions.  */
+  if ( w * maxheight < maxwidth * h)
+    width = w * maxheight / h;
+
+  /* Is the new region higher than the old bitmap?  */
+  if ( w * maxheight > maxwidth * h)
+    height = h * maxwidth / w;
+
+  return grub_video_bitmap_scale (out, in, width, height);
+}
+
 /* Initialize bitmap module.  */
 GRUB_MOD_INIT(video_bitmap)
 {
Index: include/grub/bitmap.h
===================================================================
--- include/grub/bitmap.h       (revision 1832)
+++ include/grub/bitmap.h       (working copy)
@@ -67,4 +67,12 @@
 
 void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap);
 
+grub_err_t grub_video_bitmap_scale (struct grub_video_bitmap **out,
+                                    struct grub_video_bitmap *in,
+                                    unsigned int width, unsigned int height);
+
+grub_err_t grub_video_bitmap_scale_keepaspect (struct grub_video_bitmap **out,
+                                               struct grub_video_bitmap *in,
+                                               unsigned int maxwidth,
+                                               unsigned int maxheight);
 #endif /* ! GRUB_BITMAP_HEADER */
Index: ChangeLog
===================================================================
--- ChangeLog   (revision 1832)
+++ ChangeLog   (working copy)
@@ -1,3 +1,13 @@
+2008-08-28  Olaf Mandel  <address@hidden>
+
+       * term/gfxterm.c: Correctly store height of background image, add
+       support for centering images on viewport (opposed to having them in
+       the upper-left corner). Use new scaling feature below.
+
+       * video/bitmap.c: Add ability to scale pictures to arbitrary
+       dimensions (very simple scaling at the moment, linear or bilinear
+       scaling would be better).
+
 2008-08-28  Robert Millan  <address@hidden>
 
        * util/biosdisk.c (find_grub_drive): Declare missing `i' variable.
Index: term/gfxterm.c
===================================================================
--- term/gfxterm.c      (revision 1832)
+++ term/gfxterm.c      (working copy)
@@ -43,6 +43,10 @@
 #define DEFAULT_NORMAL_COLOR    0x07
 #define DEFAULT_HIGHLIGHT_COLOR 0x70
 
+#define BITMAP_CENTERED         1
+#define BITMAP_SCALED           1
+#define BITMAP_KEEPASPECTRATIO  1
+
 struct grub_dirty_region
 {
   int top_left_x;
@@ -112,6 +116,8 @@
 
 static struct grub_video_render_target *text_layer;
 
+static int bitmap_x;
+static int bitmap_y;
 static unsigned int bitmap_width;
 static unsigned int bitmap_height;
 static struct grub_video_bitmap *bitmap;
@@ -513,43 +519,95 @@
     {
       /* Render bitmap as background.  */
       grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x, y, 
-                              x, y, 
+                              x - bitmap_x, y - bitmap_y,
                               width, height);
       
       /* If bitmap is smaller than requested blit area, use background 
          color.  */
       color = virtual_screen.bg_color;
 
+      /* Fill top side of the bitmap (including corners) if needed.  */
+      if ((int)y < bitmap_y)
+        {
+          int h = height;
+          
+          if (y + h > bitmap_y)
+            {
+              h = bitmap_y - y;
+            }
+          
+          /* Render background layer.  */
+          grub_video_fill_rect (color, x, y, width, h);        
+        }
+
+      /* Fill left side of the bitmap if needed.  */
+      if ((x < bitmap_x) && (y < bitmap_y + bitmap_height)
+          && (y + height > bitmap_y))
+        {
+          int w = width;
+          int h = height;
+          unsigned int ty = y;
+
+          if (x + w > bitmap_x)
+            {
+              w = bitmap_x - x;
+            }
+
+          if (ty < bitmap_y)
+            {
+              h -= bitmap_y - ty;
+              ty = bitmap_y;
+            }
+          
+          if (ty + h > bitmap_y + bitmap_height)
+            {
+              h = bitmap_y + bitmap_height - ty;
+            }
+          
+          /* Render background layer.  */
+          grub_video_fill_rect (color, x, ty, w, h);        
+        }
+ 
       /* Fill right side of the bitmap if needed.  */
-      if ((x + width >= bitmap_width) && (y < bitmap_height))
+      if ((x + width > bitmap_x + bitmap_width)
+          && (y < bitmap_y + bitmap_height) && (y + height > bitmap_y))
         {
-          int w = (x + width) - bitmap_width;
+          int w = width;
           int h = height;
           unsigned int tx = x;
+          unsigned int ty = y;
 
-          if (y + height >= bitmap_height)
+          if (tx < bitmap_x + bitmap_width)
             {
-              h = bitmap_height - y;
+              w -= bitmap_x + bitmap_width - tx;
+              tx = bitmap_x + bitmap_width;
             }
           
-          if (bitmap_width > tx)
+          if (ty < bitmap_y)
             {
-              tx = bitmap_width;
+              h -= bitmap_y - ty;
+              ty = bitmap_y;
             }
           
+          if (ty + h > bitmap_y + bitmap_height)
+            {
+              h = bitmap_y + bitmap_height - ty;
+            }
+
           /* Render background layer.  */
-          grub_video_fill_rect (color, tx, y, w, h);        
+          grub_video_fill_rect (color, tx, ty, w, h);        
         }
       
-      /* Fill bottom side of the bitmap if needed.  */
-      if (y + height >= bitmap_height)
+      /* Fill bottom side of the bitmap (including corners) if needed.  */
+      if (y + height > bitmap_y + bitmap_height)
         {
-          int h = (y + height) - bitmap_height;
+          int h = height;
           unsigned int ty = y;
           
-          if (bitmap_height > ty)
+          if (ty < bitmap_y + bitmap_height)
             {
-              ty = bitmap_height;
+              h -= bitmap_y + bitmap_height - ty;
+              ty = bitmap_y + bitmap_height;
             }
           
           /* Render background layer.  */
@@ -1041,10 +1099,36 @@
     /* If bitmap was loaded correctly, display it.  */
     if (bitmap)
       {
+        /* Scale the bitmap if desired. There is the option of preserving
+         * the aspect ratio of the bitmap.  */
+#if BITMAP_SCALED
+        struct grub_video_bitmap *scaled_bitmap = 0;
+#  if BITMAP_KEEPASPECTRATIO
+        grub_video_bitmap_scale_keepaspect (&scaled_bitmap, bitmap,
+                                            mode_info.width, mode_info.height);
+#  else
+        grub_video_bitmap_scale (&scaled_bitmap, bitmap,
+                                 mode_info.width, mode_info.height);
+#  endif
+        if (grub_errno != GRUB_ERR_NONE)
+          return grub_errno;
+        grub_video_bitmap_destroy (bitmap);
+        bitmap = scaled_bitmap;
+#endif
+
         /* Determine bitmap dimensions.  */
         bitmap_width = grub_video_bitmap_get_width (bitmap);
-        bitmap_height = grub_video_bitmap_get_width (bitmap);
+        bitmap_height = grub_video_bitmap_get_height (bitmap);
         
+        /* Either center bitmap or put it in upper left corner.  */
+#if BITMAP_CENTERED
+        bitmap_x = ((int)mode_info.width-(int)bitmap_width)/2;
+        bitmap_y = ((int)mode_info.height-(int)bitmap_height)/2;
+#else
+        bitmap_x = 0;
+        bitmap_y = 0;
+#endif
+
         /* Mark whole screen as dirty.  */
         dirty_region_reset ();
         dirty_region_add (0, 0, mode_info.width, mode_info.height);

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

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