/* bitmap_scale.c - Bitmap scaling interface. */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2006,2007  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>

/* 
 * 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, "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 = grub_video_bitmap_scale_nn (*dst, src);
      break;
    case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
    case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
      ret = grub_video_bitmap_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;
    }
}
