Colin D Bennett wrote: > This patch adds bitmap scaling support to GRUB. > > It also adds support to gfxterm for scaling the background image to fit > the screen. The background_image command supports a new -m/--mode > option, which takes "stretch" (the default) or "normal" as values. > > The patch is against GRUB trunk revision 1964.
After commit this needs to be documented to wiki/gfxterm with an example perhaps... > Vesa started a discussion about more flexible handling of bitmaps > including the concept of optimizing loaded bitmaps into a > display-specific format, at which point they could no longer be > modified by functions like the bitmap scaling functions. > > This bitmap format optimization would be nice to add, but I think we > can at least add this basic bitmap scaling functionality to start > with. It only supports 24/32 bpp direct color modes at this point to > keep things simple. True. Lets not block the feature because of this. > === added file 'include/grub/bitmap_scale.h' > --- include/grub/bitmap_scale.h 1970-01-01 00:00:00 +0000 > +++ include/grub/bitmap_scale.h 2009-01-31 20:18:45 +0000 > @@ -0,0 +1,48 @@ > +/* bitmap_scale.h - Bitmap scaling functions. */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2008,2009 Free Software Foundation, Inc. New file. Change copyright to 2009. > + * > + * 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 */ > > === modified file 'term/gfxterm.c' > --- term/gfxterm.c 2009-01-19 17:09:53 +0000 > +++ term/gfxterm.c 2009-01-31 20:28:20 +0000 > @@ -27,6 +27,7 @@ > #include <grub/env.h> > #include <grub/video.h> > #include <grub/bitmap.h> > +#include <grub/bitmap_scale.h> > > #define DEFAULT_VIDEO_WIDTH 640 > #define DEFAULT_VIDEO_HEIGHT 480 > @@ -1095,8 +1096,18 @@ > dirty_region_redraw (); > } > > + > +/* 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').", 0, > + ARG_TYPE_STRING}, > + {0, 0, 0, 0, 0, 0} > +}; > + > static grub_err_t > -grub_gfxterm_background_image_cmd (struct grub_arg_list *state __attribute__ > ((unused)), > +grub_gfxterm_background_image_cmd (struct grub_arg_list *state, > int argc, > char **args) > { > @@ -1123,12 +1134,36 @@ > 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) Extra parentheses would be nice around grub_rtrcmp () == 0 compare. > + { > + if (mode_info.width != grub_video_bitmap_get_width (bitmap) > + || mode_info.height != grub_video_bitmap_get_height (bitmap)) This would benefit the same... > + { > + 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. */ Space... > + grub_video_bitmap_destroy (bitmap); > + bitmap = scaled_bitmap; > + } > + } > + } > + > + > /* If bitmap was loaded correctly, display it. */ > if (bitmap) > { > /* 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); > > /* Mark whole screen as dirty. */ > dirty_region_reset (); > @@ -1171,7 +1206,7 @@ > GRUB_COMMAND_FLAG_BOTH, > "background_image", > "Load background image for active terminal", > - 0); > + background_image_cmd_options); > } > > GRUB_MOD_FINI(term_gfxterm) > > === added file 'video/bitmap_scale.c' > --- video/bitmap_scale.c 1970-01-01 00:00:00 +0000 > +++ video/bitmap_scale.c 2009-01-31 20:22:55 +0000 > @@ -0,0 +1,307 @@ > +/* bitmap_scale.c - Bitmap scaling. */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. New file. Change to 2009. > + * > + * 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. */ Space.. > + if (src == 0) > + return grub_error (GRUB_ERR_BAD_ARGUMENT, > + "null src bitmap in grub_video_bitmap_create_scaled"); Add some parentheses below... > + 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, "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"); > + Space... > + /* Create the new bitmap. */ > + grub_err_t ret; > + ret = grub_video_bitmap_create (dst, dst_width, dst_height, > + src->mode_info.blit_format); Move comment to own line (or remove it)... > + 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) > + { Remove one space and add one... > + /* Success: *dst is now a pointer to the scaled bitmap. */ > + return GRUB_ERR_NONE; > + } > + else > + { Space.. > + /* 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) > +{ Space.. > + /* Verify the simplifying assumptions. */ > + if (dst == 0 || src == 0) > + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn"); Add parentheses below... Please feel free to add empty line between different if's if you see it fit. > + 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"); > + Didn't the compile complain about declaring variables within code... or were we targetting for c99 ? > + 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; Space. > + /* 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; > + Space. > + /* 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; > + Space. > + /* 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) > +{ Same story here... > + /* 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; > +} _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel