2N/A/* bitmap_scale.c - Bitmap scaling. */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/mm.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/video.h>
2N/A#include <grub/bitmap.h>
2N/A#include <grub/bitmap_scale.h>
2N/A#include <grub/types.h>
2N/A#include <grub/dl.h>
2N/A
2N/AGRUB_MOD_LICENSE ("GPLv3+");
2N/A
2N/A/* Prototypes for module-local functions. */
2N/Astatic grub_err_t scale_nn (struct grub_video_bitmap *dst,
2N/A struct grub_video_bitmap *src);
2N/Astatic grub_err_t scale_bilinear (struct grub_video_bitmap *dst,
2N/A struct grub_video_bitmap *src);
2N/A
2N/A/* This function creates a new scaled version of the bitmap SRC. The new
2N/A bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm
2N/A is given by SCALE_METHOD. If an error is encountered, the return code is
2N/A not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or
2N/A it is destroyed before this function returns.
2N/A
2N/A Supports only direct color modes which have components separated
2N/A into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
2N/A But because of this simplifying assumption, the implementation is
2N/A greatly simplified. */
2N/Agrub_err_t
2N/Agrub_video_bitmap_create_scaled (struct grub_video_bitmap **dst,
2N/A int dst_width, int dst_height,
2N/A struct grub_video_bitmap *src,
2N/A enum grub_video_bitmap_scale_method
2N/A scale_method)
2N/A{
2N/A *dst = 0;
2N/A
2N/A /* Verify the simplifying assumptions. */
2N/A if (src == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "null src bitmap in grub_video_bitmap_create_scaled");
2N/A if (src->mode_info.red_field_pos % 8 != 0
2N/A || src->mode_info.green_field_pos % 8 != 0
2N/A || src->mode_info.blue_field_pos % 8 != 0
2N/A || src->mode_info.reserved_field_pos % 8 != 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "src format not supported for scale");
2N/A if (src->mode_info.width == 0 || src->mode_info.height == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "source bitmap has a zero dimension");
2N/A if (dst_width <= 0 || dst_height <= 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "requested to scale to a size w/ a zero dimension");
2N/A if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT,
2N/A "bitmap to scale has inconsistent Bpp and bpp");
2N/A
2N/A /* Create the new bitmap. */
2N/A grub_err_t ret;
2N/A ret = grub_video_bitmap_create (dst, dst_width, dst_height,
2N/A src->mode_info.blit_format);
2N/A if (ret != GRUB_ERR_NONE)
2N/A return ret; /* Error. */
2N/A
2N/A switch (scale_method)
2N/A {
2N/A case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST:
2N/A case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST:
2N/A ret = scale_nn (*dst, src);
2N/A break;
2N/A case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
2N/A case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
2N/A ret = scale_bilinear (*dst, src);
2N/A break;
2N/A default:
2N/A ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value");
2N/A break;
2N/A }
2N/A
2N/A if (ret == GRUB_ERR_NONE)
2N/A {
2N/A /* Success: *dst is now a pointer to the scaled bitmap. */
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A else
2N/A {
2N/A /* Destroy the bitmap and return the error code. */
2N/A grub_video_bitmap_destroy (*dst);
2N/A *dst = 0;
2N/A return ret;
2N/A }
2N/A}
2N/A
2N/A/* Nearest neighbor bitmap scaling algorithm.
2N/A
2N/A Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
2N/A dimensions of DST. This function uses the nearest neighbor algorithm to
2N/A interpolate the pixels.
2N/A
2N/A Supports only direct color modes which have components separated
2N/A into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
2N/A But because of this simplifying assumption, the implementation is
2N/A greatly simplified. */
2N/Astatic grub_err_t
2N/Ascale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
2N/A{
2N/A /* Verify the simplifying assumptions. */
2N/A if (dst == 0 || src == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn");
2N/A if (dst->mode_info.red_field_pos % 8 != 0
2N/A || dst->mode_info.green_field_pos % 8 != 0
2N/A || dst->mode_info.blue_field_pos % 8 != 0
2N/A || dst->mode_info.reserved_field_pos % 8 != 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
2N/A if (src->mode_info.red_field_pos % 8 != 0
2N/A || src->mode_info.green_field_pos % 8 != 0
2N/A || src->mode_info.blue_field_pos % 8 != 0
2N/A || src->mode_info.reserved_field_pos % 8 != 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
2N/A if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
2N/A || dst->mode_info.red_mask_size != src->mode_info.red_mask_size
2N/A || dst->mode_info.green_field_pos != src->mode_info.green_field_pos
2N/A || dst->mode_info.green_mask_size != src->mode_info.green_mask_size
2N/A || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
2N/A || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
2N/A || dst->mode_info.reserved_field_pos !=
2N/A src->mode_info.reserved_field_pos
2N/A || dst->mode_info.reserved_mask_size !=
2N/A src->mode_info.reserved_mask_size)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
2N/A if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
2N/A if (dst->mode_info.width == 0 || dst->mode_info.height == 0
2N/A || src->mode_info.width == 0 || src->mode_info.height == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
2N/A
2N/A grub_uint8_t *ddata = dst->data;
2N/A grub_uint8_t *sdata = src->data;
2N/A int dw = dst->mode_info.width;
2N/A int dh = dst->mode_info.height;
2N/A int sw = src->mode_info.width;
2N/A int sh = src->mode_info.height;
2N/A int dstride = dst->mode_info.pitch;
2N/A int sstride = src->mode_info.pitch;
2N/A /* bytes_per_pixel is the same for both src and dst. */
2N/A int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
2N/A
2N/A int dy;
2N/A for (dy = 0; dy < dh; dy++)
2N/A {
2N/A int dx;
2N/A for (dx = 0; dx < dw; dx++)
2N/A {
2N/A grub_uint8_t *dptr;
2N/A grub_uint8_t *sptr;
2N/A int sx;
2N/A int sy;
2N/A int comp;
2N/A
2N/A /* Compute the source coordinate that the destination coordinate
2N/A maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
2N/A sx = sw * dx / dw;
2N/A sy = sh * dy / dh;
2N/A
2N/A /* Get the address of the pixels in src and dst. */
2N/A dptr = ddata + dy * dstride + dx * bytes_per_pixel;
2N/A sptr = sdata + sy * sstride + sx * bytes_per_pixel;
2N/A
2N/A /* Copy the pixel color value. */
2N/A for (comp = 0; comp < bytes_per_pixel; comp++)
2N/A dptr[comp] = sptr[comp];
2N/A }
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* Bilinear interpolation image scaling algorithm.
2N/A
2N/A Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
2N/A dimensions of DST. This function uses the bilinear interpolation algorithm
2N/A to interpolate the pixels.
2N/A
2N/A Supports only direct color modes which have components separated
2N/A into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
2N/A But because of this simplifying assumption, the implementation is
2N/A greatly simplified. */
2N/Astatic grub_err_t
2N/Ascale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
2N/A{
2N/A /* Verify the simplifying assumptions. */
2N/A if (dst == 0 || src == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func");
2N/A if (dst->mode_info.red_field_pos % 8 != 0
2N/A || dst->mode_info.green_field_pos % 8 != 0
2N/A || dst->mode_info.blue_field_pos % 8 != 0
2N/A || dst->mode_info.reserved_field_pos % 8 != 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
2N/A if (src->mode_info.red_field_pos % 8 != 0
2N/A || src->mode_info.green_field_pos % 8 != 0
2N/A || src->mode_info.blue_field_pos % 8 != 0
2N/A || src->mode_info.reserved_field_pos % 8 != 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
2N/A if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
2N/A || dst->mode_info.red_mask_size != src->mode_info.red_mask_size
2N/A || dst->mode_info.green_field_pos != src->mode_info.green_field_pos
2N/A || dst->mode_info.green_mask_size != src->mode_info.green_mask_size
2N/A || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
2N/A || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
2N/A || dst->mode_info.reserved_field_pos !=
2N/A src->mode_info.reserved_field_pos
2N/A || dst->mode_info.reserved_mask_size !=
2N/A src->mode_info.reserved_mask_size)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
2N/A if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
2N/A if (dst->mode_info.width == 0 || dst->mode_info.height == 0
2N/A || src->mode_info.width == 0 || src->mode_info.height == 0)
2N/A return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
2N/A
2N/A grub_uint8_t *ddata = dst->data;
2N/A grub_uint8_t *sdata = src->data;
2N/A int dw = dst->mode_info.width;
2N/A int dh = dst->mode_info.height;
2N/A int sw = src->mode_info.width;
2N/A int sh = src->mode_info.height;
2N/A int dstride = dst->mode_info.pitch;
2N/A int sstride = src->mode_info.pitch;
2N/A /* bytes_per_pixel is the same for both src and dst. */
2N/A int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
2N/A
2N/A int dy;
2N/A for (dy = 0; dy < dh; dy++)
2N/A {
2N/A int dx;
2N/A for (dx = 0; dx < dw; dx++)
2N/A {
2N/A grub_uint8_t *dptr;
2N/A grub_uint8_t *sptr;
2N/A int sx;
2N/A int sy;
2N/A int comp;
2N/A
2N/A /* Compute the source coordinate that the destination coordinate
2N/A maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
2N/A sx = sw * dx / dw;
2N/A sy = sh * dy / dh;
2N/A
2N/A /* Get the address of the pixels in src and dst. */
2N/A dptr = ddata + dy * dstride + dx * bytes_per_pixel;
2N/A sptr = sdata + sy * sstride + sx * bytes_per_pixel;
2N/A
2N/A /* If we have enough space to do so, use bilinear interpolation.
2N/A Otherwise, fall back to nearest neighbor for this pixel. */
2N/A if (sx < sw - 1 && sy < sh - 1)
2N/A {
2N/A /* Do bilinear interpolation. */
2N/A
2N/A /* Fixed-point .8 numbers representing the fraction of the
2N/A distance in the x (u) and y (v) direction within the
2N/A box of 4 pixels in the source. */
2N/A int u = (256 * sw * dx / dw) - (sx * 256);
2N/A int v = (256 * sh * dy / dh) - (sy * 256);
2N/A
2N/A for (comp = 0; comp < bytes_per_pixel; comp++)
2N/A {
2N/A /* Get the component's values for the
2N/A four source corner pixels. */
2N/A grub_uint8_t f00 = sptr[comp];
2N/A grub_uint8_t f10 = sptr[comp + bytes_per_pixel];
2N/A grub_uint8_t f01 = sptr[comp + sstride];
2N/A grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel];
2N/A
2N/A /* Do linear interpolations along the top and bottom
2N/A rows of the box. */
2N/A grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256;
2N/A grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256;
2N/A
2N/A /* Interpolate vertically. */
2N/A grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256;
2N/A
2N/A dptr[comp] = fxy;
2N/A }
2N/A }
2N/A else
2N/A {
2N/A /* Fall back to nearest neighbor interpolation. */
2N/A /* Copy the pixel color value. */
2N/A for (comp = 0; comp < bytes_per_pixel; comp++)
2N/A dptr[comp] = sptr[comp];
2N/A }
2N/A }
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}