[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] 3/5 Bitmap scaling
From: |
Vesa Jääskeläinen |
Subject: |
Re: [PATCH] 3/5 Bitmap scaling |
Date: |
Sat, 31 Jan 2009 23:27:55 +0200 |
User-agent: |
Thunderbird 2.0.0.19 (Windows/20081209) |
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;
> +}