diff --git a/ChangeLog.scale b/ChangeLog.scale
new file mode 100644
index 000000000..eb12d3b21
--- /dev/null
+++ b/ChangeLog.scale
@@ -0,0 +1,16 @@
+2009-08-24  Colin D Bennett  <colin@gibibit.com>
+
+	Bitmap scaling support.
+
+	* conf/common.rmk (pkglib_MODULES): Add bitmap_scale.mod.
+	(bitmap_scale_mod_SOURCES): New variable.
+	(bitmap_scale_mod_CFLAGS): Likewise.
+	(bitmap_scale_mod_LDFLAGS): Likewise.
+	* include/grub/bitmap_scale.h: New file.
+	* term/gfxterm.c (BACKGROUND_CMD_ARGINDEX_MODE): New definiton.
+	(background_image_cmd_options): New variable.
+	(grub_gfxterm_background_image_cmd): Support bitmap stretching.
+	(cmd): Rename and change type to ...
+	(background_image_cmd_handle): ... this. All users updated.
+	(GRUB_MOD_INIT(term_gfxterm)): Make background_image extended command.
+	* video/bitmap_scale.c: New file.
diff --git a/conf/common.rmk b/conf/common.rmk
index 173f24b62..98681bc1c 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -567,6 +567,12 @@ bitmap_mod_SOURCES = video/bitmap.c
 bitmap_mod_CFLAGS = $(COMMON_CFLAGS)
 bitmap_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For bitmap_scale.mod
+pkglib_MODULES += bitmap_scale.mod
+bitmap_scale_mod_SOURCES = video/bitmap_scale.c
+bitmap_scale_mod_CFLAGS = $(COMMON_CFLAGS)
+bitmap_scale_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # For tga.mod
 tga_mod_SOURCES = video/readers/tga.c
 tga_mod_CFLAGS = $(COMMON_CFLAGS)
diff --git a/include/grub/bitmap_scale.h b/include/grub/bitmap_scale.h
new file mode 100644
index 000000000..7ea9d7914
--- /dev/null
+++ b/include/grub/bitmap_scale.h
@@ -0,0 +1,48 @@
+/* bitmap_scale.h - Bitmap scaling functions. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_BITMAP_SCALE_HEADER
+#define GRUB_BITMAP_SCALE_HEADER 1
+
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/bitmap_scale.h>
+
+enum grub_video_bitmap_scale_method
+{
+  /* Choose the fastest interpolation algorithm.  */
+  GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST,
+  /* Choose the highest quality interpolation algorithm.  */
+  GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST,
+
+  /* Specific algorithms:  */
+  /* Nearest neighbor interpolation.  */
+  GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST,
+  /* Bilinear interpolation.  */
+  GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR
+};
+
+grub_err_t
+grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst,
+                                 int dst_width, int dst_height,
+                                 struct grub_video_bitmap *src,
+                                 enum
+                                 grub_video_bitmap_scale_method scale_method);
+
+#endif /* ! GRUB_BITMAP_SCALE_HEADER */
diff --git a/include/grub/xnu.h b/include/grub/xnu.h
index c3902e670..b058bd1b9 100644
--- a/include/grub/xnu.h
+++ b/include/grub/xnu.h
@@ -104,4 +104,7 @@ extern grub_uint32_t grub_xnu_heap_real_start;
 extern grub_size_t grub_xnu_heap_size;
 extern char *grub_xnu_heap_start;
 extern struct grub_video_bitmap *grub_xnu_bitmap;
+typedef enum {GRUB_XNU_BITMAP_CENTER, GRUB_XNU_BITMAP_STRETCH}
+  grub_xnu_bitmap_mode_t;
+extern grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
 #endif
diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c
index 839d0ad44..1a222e6e5 100644
--- a/loader/i386/pc/xnu.c
+++ b/loader/i386/pc/xnu.c
@@ -22,6 +22,7 @@
 #include <grub/mm.h>
 #include <grub/cpu/xnu.h>
 #include <grub/video_fb.h>
+#include <grub/bitmap_scale.h>
 
 #define min(a,b) (((a) < (b)) ? (a) : (b))
 #define max(a,b) (((a) > (b)) ? (a) : (b))
@@ -37,6 +38,7 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params)
   char *tmp, *modevar;
   void *framebuffer;
   grub_err_t err;
+  struct grub_video_bitmap *bitmap = NULL;
 
   modevar = grub_env_get ("gfxpayload");
   /* Consider only graphical 32-bit deep modes.  */
@@ -63,31 +65,46 @@ 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");
+
   if (grub_xnu_bitmap)
+    {
+      if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
+	err = grub_video_bitmap_create_scaled (&bitmap,
+					       mode_info.width,
+					       mode_info.height,
+					       grub_xnu_bitmap,
+					       GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+      else
+	bitmap = grub_xnu_bitmap;
+    }
+
+  if (bitmap)
     {
       int x, y;
 
-      x = mode_info.width - grub_xnu_bitmap->mode_info.width;
+      x = mode_info.width - bitmap->mode_info.width;
       x /= 2;
-      y = mode_info.height - grub_xnu_bitmap->mode_info.height;
+      y = mode_info.height - bitmap->mode_info.height;
       y /= 2;
-      err = grub_video_blit_bitmap (grub_xnu_bitmap,
+      err = grub_video_blit_bitmap (bitmap,
 				    GRUB_VIDEO_BLIT_REPLACE,
 				    x > 0 ? x : 0,
 				    y > 0 ? y : 0,
 				    x < 0 ? -x : 0,
 				    y < 0 ? -y : 0,
-				    min (grub_xnu_bitmap->mode_info.width,
+				    min (bitmap->mode_info.width,
 					 mode_info.width),
-				    min (grub_xnu_bitmap->mode_info.height,
+				    min (bitmap->mode_info.height,
 					 mode_info.height));
-      if (err)
-	{
-	  grub_print_error ();
-	  grub_errno = GRUB_ERR_NONE;
-	  grub_xnu_bitmap = 0;
-	}
-      err = GRUB_ERR_NONE;
+    }
+  if (err)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      bitmap = 0;
     }
 
   ret = grub_video_get_info_and_fini (&mode_info, &framebuffer);
@@ -100,8 +117,8 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params)
   params->lfb_line_len = mode_info.pitch;
 
   params->lfb_base = PTR_TO_UINT32 (framebuffer);
-  params->lfb_mode = grub_xnu_bitmap
-    ? GRUB_XNU_VIDEO_SPLASH : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+  params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH 
+    : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
 
   return GRUB_ERR_NONE;
 }
diff --git a/loader/xnu.c b/loader/xnu.c
index aac4ae372..e0ae9cefe 100644
--- a/loader/xnu.c
+++ b/loader/xnu.c
@@ -31,6 +31,7 @@
 #include <grub/gzio.h>
 #include <grub/command.h>
 #include <grub/misc.h>
+#include <grub/extcmd.h>
 
 struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
 static int driverspackagenum = 0;
@@ -1272,18 +1273,37 @@ grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
 }
 
 struct grub_video_bitmap *grub_xnu_bitmap = 0;
+grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
+
+/* Option array indices.  */
+#define XNU_SPLASH_CMD_ARGINDEX_MODE 0
+
+static const struct grub_arg_option xnu_splash_cmd_options[] =
+  {
+    {"mode", 'm', 0, "Background image mode.", "stretch|normal",
+     ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
 
 static grub_err_t
-grub_cmd_xnu_splash (grub_command_t cmd __attribute__ ((unused)),
+grub_cmd_xnu_splash (grub_extcmd_t cmd,
 		     int argc, char *args[])
 {
   grub_err_t err;
   if (argc != 1)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
+  if (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
+      grub_strcmp (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
+		   "stretch") == 0)
+    grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
+  else
+    grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
+
   err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
   if (err)
     grub_xnu_bitmap = 0;
+
   return err;
 }
 
@@ -1316,8 +1336,10 @@ grub_xnu_unlock ()
   locked = 0;
 }
 
-static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, cmd_kextdir,
-  cmd_ramdisk, cmd_devtree, cmd_resume, cmd_splash;
+static grub_command_t cmd_kernel, cmd_mkext, cmd_kext, ;
+static grub_command_t cmd_devtree;
+static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
+static grub_extcmd_t cmd_splash;
 
 GRUB_MOD_INIT(xnu)
 {
@@ -1335,8 +1357,11 @@ GRUB_MOD_INIT(xnu)
 				       "It will be seen as md0");
   cmd_devtree = grub_register_command ("xnu_devtree", grub_cmd_xnu_devtree, 0,
 				       "Load XNU devtree");
-  cmd_splash = grub_register_command ("xnu_splash", grub_cmd_xnu_splash, 0,
-				      "Load a splash image for XNU");
+  cmd_splash = grub_register_extcmd ("xnu_splash",
+				     grub_cmd_xnu_splash,
+				     GRUB_COMMAND_FLAG_BOTH, 0,
+				     "Load a splash image for XNU",
+				     xnu_splash_cmd_options);
 
 #ifndef GRUB_UTIL
   cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
@@ -1356,5 +1381,5 @@ GRUB_MOD_FINI(xnu)
   grub_unregister_command (cmd_devtree);
   grub_unregister_command (cmd_ramdisk);
   grub_unregister_command (cmd_kernel);
-  grub_unregister_command (cmd_splash);
+  grub_unregister_extcmd (cmd_splash);
 }
diff --git a/term/gfxterm.c b/term/gfxterm.c
index 6906b77a1..62cf98416 100644
--- a/term/gfxterm.c
+++ b/term/gfxterm.c
@@ -26,6 +26,8 @@
 #include <grub/video.h>
 #include <grub/bitmap.h>
 #include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/bitmap_scale.h>
 
 #define DEFAULT_VIDEO_MODE "auto"
 #define DEFAULT_BORDER_WIDTH	10
@@ -909,11 +911,23 @@ grub_gfxterm_refresh (void)
   dirty_region_reset ();
 }
 
+/* Option array indices.  */
+#define BACKGROUND_CMD_ARGINDEX_MODE 0
+
+static const struct grub_arg_option background_image_cmd_options[] =
+  {
+    {"mode", 'm', 0, "Background image mode.", "stretch|normal",
+     ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
+
 static grub_err_t
-grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)),
+grub_gfxterm_background_image_cmd (grub_extcmd_t cmd __attribute__ ((unused)),
                                    int argc,
                                    char **args)
 {
+  struct grub_arg_list *state = cmd->state;
+
   /* Check that we have video adapter active.  */
   if (grub_video_get_info(NULL) != GRUB_ERR_NONE)
     return grub_errno;
@@ -937,6 +951,29 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)),
     if (grub_errno != GRUB_ERR_NONE)
       return grub_errno;
 
+    /* Determine if the bitmap should be scaled to fit the screen.  */
+    if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set
+        || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
+                        "stretch") == 0)
+        {
+          if (mode_info.width != grub_video_bitmap_get_width (bitmap)
+              || mode_info.height != grub_video_bitmap_get_height (bitmap))
+            {
+              struct grub_video_bitmap *scaled_bitmap;
+              grub_video_bitmap_create_scaled (&scaled_bitmap,
+                                               mode_info.width,
+                                               mode_info.height,
+                                               bitmap,
+                                               GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+              if (grub_errno == GRUB_ERR_NONE)
+                {
+                  /* Replace the original bitmap with the scaled one.  */
+                  grub_video_bitmap_destroy (bitmap);
+                  bitmap = scaled_bitmap;
+                }
+            }
+        }
+
     /* If bitmap was loaded correctly, display it.  */
     if (bitmap)
       {
@@ -975,18 +1012,22 @@ static struct grub_term_output grub_video_term =
     .next = 0
   };
 
-static grub_command_t cmd;
+static grub_extcmd_t background_image_cmd_handle;
 
 GRUB_MOD_INIT(term_gfxterm)
 {
   grub_term_register_output ("gfxterm", &grub_video_term);
-  cmd = grub_register_command ("background_image",
-			       grub_gfxterm_background_image_cmd,
-			       0, "Load background image for active terminal");
+  background_image_cmd_handle =
+    grub_register_extcmd ("background_image",
+                          grub_gfxterm_background_image_cmd,
+                          GRUB_COMMAND_FLAG_BOTH,
+                          "background_image [-m (stretch|normal)] FILE",
+                          "Load background image for active terminal.",
+                          background_image_cmd_options);
 }
 
 GRUB_MOD_FINI(term_gfxterm)
 {
-  grub_unregister_command (cmd);
+  grub_unregister_extcmd (background_image_cmd_handle);
   grub_term_unregister_output (&grub_video_term);
 }
diff --git a/video/bitmap_scale.c b/video/bitmap_scale.c
new file mode 100644
index 000000000..6f8ff247e
--- /dev/null
+++ b/video/bitmap_scale.c
@@ -0,0 +1,308 @@
+/* bitmap_scale.c - Bitmap scaling. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/types.h>
+
+/* Prototypes for module-local functions.  */
+static grub_err_t scale_nn (struct grub_video_bitmap *dst,
+                            struct grub_video_bitmap *src);
+static grub_err_t scale_bilinear (struct grub_video_bitmap *dst,
+                                  struct grub_video_bitmap *src);
+
+/* This function creates a new scaled version of the bitmap SRC.  The new
+   bitmap has dimensions DST_WIDTH by DST_HEIGHT.  The scaling algorithm
+   is given by SCALE_METHOD.  If an error is encountered, the return code is
+   not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or
+   it is destroyed before this function returns.
+
+   Supports only direct color modes which have components separated
+   into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
+   But because of this simplifying assumption, the implementation is
+   greatly simplified.  */
+grub_err_t
+grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst,
+                                 int dst_width, int dst_height,
+                                 struct grub_video_bitmap *src,
+                                 enum grub_video_bitmap_scale_method
+                                 scale_method)
+{
+  *dst = 0;
+
+  /* Verify the simplifying assumptions. */
+  if (src == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "null src bitmap in grub_video_bitmap_create_scaled");
+  if (src->mode_info.red_field_pos % 8 != 0
+      || src->mode_info.green_field_pos % 8 != 0
+      || src->mode_info.blue_field_pos % 8 != 0
+      || src->mode_info.reserved_field_pos % 8 != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "src format not supported for scale");
+  if (src->mode_info.width == 0 || src->mode_info.height == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "source bitmap has a zero dimension");
+  if (dst_width <= 0 || dst_height <= 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "requested to scale to a size w/ a zero dimension");
+  if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "bitmap to scale has inconsistent Bpp and bpp");
+
+  /* Create the new bitmap. */
+  grub_err_t ret;
+  ret = grub_video_bitmap_create (dst, dst_width, dst_height,
+                                  src->mode_info.blit_format);
+  if (ret != GRUB_ERR_NONE)
+    return ret;                 /* Error. */
+
+  switch (scale_method)
+    {
+    case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST:
+    case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST:
+      ret = scale_nn (*dst, src);
+      break;
+    case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
+    case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
+      ret = scale_bilinear (*dst, src);
+      break;
+    default:
+      ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value");
+      break;
+    }
+
+  if (ret == GRUB_ERR_NONE)
+    {
+      /* Success:  *dst is now a pointer to the scaled bitmap. */
+      return GRUB_ERR_NONE;
+    }
+  else
+    {
+      /* Destroy the bitmap and return the error code. */
+      grub_video_bitmap_destroy (*dst);
+      *dst = 0;
+      return ret;
+    }
+}
+
+/* Nearest neighbor bitmap scaling algorithm.
+
+   Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
+   dimensions of DST.  This function uses the nearest neighbor algorithm to
+   interpolate the pixels.
+
+   Supports only direct color modes which have components separated
+   into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
+   But because of this simplifying assumption, the implementation is
+   greatly simplified.  */
+static grub_err_t
+scale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
+{
+  /* Verify the simplifying assumptions. */
+  if (dst == 0 || src == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn");
+  if (dst->mode_info.red_field_pos % 8 != 0
+      || dst->mode_info.green_field_pos % 8 != 0
+      || dst->mode_info.blue_field_pos % 8 != 0
+      || dst->mode_info.reserved_field_pos % 8 != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
+  if (src->mode_info.red_field_pos % 8 != 0
+      || src->mode_info.green_field_pos % 8 != 0
+      || src->mode_info.blue_field_pos % 8 != 0
+      || src->mode_info.reserved_field_pos % 8 != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
+  if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
+      || dst->mode_info.red_mask_size != src->mode_info.red_mask_size
+      || dst->mode_info.green_field_pos != src->mode_info.green_field_pos
+      || dst->mode_info.green_mask_size != src->mode_info.green_mask_size
+      || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
+      || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
+      || dst->mode_info.reserved_field_pos !=
+      src->mode_info.reserved_field_pos
+      || dst->mode_info.reserved_mask_size !=
+      src->mode_info.reserved_mask_size)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
+  if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
+  if (dst->mode_info.width == 0 || dst->mode_info.height == 0
+      || src->mode_info.width == 0 || src->mode_info.height == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
+
+  grub_uint8_t *ddata = dst->data;
+  grub_uint8_t *sdata = src->data;
+  int dw = dst->mode_info.width;
+  int dh = dst->mode_info.height;
+  int sw = src->mode_info.width;
+  int sh = src->mode_info.height;
+  int dstride = dst->mode_info.pitch;
+  int sstride = src->mode_info.pitch;
+  /* bytes_per_pixel is the same for both src and dst. */
+  int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
+
+  int dy;
+  for (dy = 0; dy < dh; dy++)
+    {
+      int dx;
+      for (dx = 0; dx < dw; dx++)
+        {
+          grub_uint8_t *dptr;
+          grub_uint8_t *sptr;
+          int sx;
+          int sy;
+          int comp;
+
+          /* Compute the source coordinate that the destination coordinate
+             maps to.  Note: sx/sw = dx/dw  =>  sx = sw*dx/dw. */
+          sx = sw * dx / dw;
+          sy = sh * dy / dh;
+
+          /* Get the address of the pixels in src and dst. */
+          dptr = ddata + dy * dstride + dx * bytes_per_pixel;
+          sptr = sdata + sy * sstride + sx * bytes_per_pixel;
+
+          /* Copy the pixel color value. */
+          for (comp = 0; comp < bytes_per_pixel; comp++)
+            dptr[comp] = sptr[comp];
+        }
+    }
+  return GRUB_ERR_NONE;
+}
+
+/* Bilinear interpolation image scaling algorithm.
+
+   Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
+   dimensions of DST.  This function uses the bilinear interpolation algorithm
+   to interpolate the pixels.
+
+   Supports only direct color modes which have components separated
+   into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
+   But because of this simplifying assumption, the implementation is
+   greatly simplified.  */
+static grub_err_t
+scale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
+{
+  /* Verify the simplifying assumptions. */
+  if (dst == 0 || src == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func");
+  if (dst->mode_info.red_field_pos % 8 != 0
+      || dst->mode_info.green_field_pos % 8 != 0
+      || dst->mode_info.blue_field_pos % 8 != 0
+      || dst->mode_info.reserved_field_pos % 8 != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
+  if (src->mode_info.red_field_pos % 8 != 0
+      || src->mode_info.green_field_pos % 8 != 0
+      || src->mode_info.blue_field_pos % 8 != 0
+      || src->mode_info.reserved_field_pos % 8 != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
+  if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
+      || dst->mode_info.red_mask_size != src->mode_info.red_mask_size
+      || dst->mode_info.green_field_pos != src->mode_info.green_field_pos
+      || dst->mode_info.green_mask_size != src->mode_info.green_mask_size
+      || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
+      || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
+      || dst->mode_info.reserved_field_pos !=
+      src->mode_info.reserved_field_pos
+      || dst->mode_info.reserved_mask_size !=
+      src->mode_info.reserved_mask_size)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
+  if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
+  if (dst->mode_info.width == 0 || dst->mode_info.height == 0
+      || src->mode_info.width == 0 || src->mode_info.height == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
+
+  grub_uint8_t *ddata = dst->data;
+  grub_uint8_t *sdata = src->data;
+  int dw = dst->mode_info.width;
+  int dh = dst->mode_info.height;
+  int sw = src->mode_info.width;
+  int sh = src->mode_info.height;
+  int dstride = dst->mode_info.pitch;
+  int sstride = src->mode_info.pitch;
+  /* bytes_per_pixel is the same for both src and dst. */
+  int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
+
+  int dy;
+  for (dy = 0; dy < dh; dy++)
+    {
+      int dx;
+      for (dx = 0; dx < dw; dx++)
+        {
+          grub_uint8_t *dptr;
+          grub_uint8_t *sptr;
+          int sx;
+          int sy;
+          int comp;
+
+          /* Compute the source coordinate that the destination coordinate
+             maps to.  Note: sx/sw = dx/dw  =>  sx = sw*dx/dw. */
+          sx = sw * dx / dw;
+          sy = sh * dy / dh;
+
+          /* Get the address of the pixels in src and dst. */
+          dptr = ddata + dy * dstride + dx * bytes_per_pixel;
+          sptr = sdata + sy * sstride + sx * bytes_per_pixel;
+
+          /* If we have enough space to do so, use bilinear interpolation.
+             Otherwise, fall back to nearest neighbor for this pixel. */
+          if (sx < sw - 1 && sy < sh - 1)
+            {
+              /* Do bilinear interpolation. */
+
+              /* Fixed-point .8 numbers representing the fraction of the
+                 distance in the x (u) and y (v) direction within the
+                 box of 4 pixels in the source. */
+              int u = (256 * sw * dx / dw) - (sx * 256);
+              int v = (256 * sh * dy / dh) - (sy * 256);
+
+              for (comp = 0; comp < bytes_per_pixel; comp++)
+                {
+                  /* Get the component's values for the
+                     four source corner pixels. */
+                  grub_uint8_t f00 = sptr[comp];
+                  grub_uint8_t f10 = sptr[comp + bytes_per_pixel];
+                  grub_uint8_t f01 = sptr[comp + sstride];
+                  grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel];
+
+                  /* Do linear interpolations along the top and bottom
+                     rows of the box. */
+                  grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256;
+                  grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256;
+
+                  /* Interpolate vertically. */
+                  grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256;
+
+                  dptr[comp] = fxy;
+                }
+            }
+          else
+            {
+              /* Fall back to nearest neighbor interpolation. */
+              /* Copy the pixel color value. */
+              for (comp = 0; comp < bytes_per_pixel; comp++)
+                dptr[comp] = sptr[comp];
+            }
+        }
+    }
+  return GRUB_ERR_NONE;
+}