else if (! grub_strcmp ("desktop-image", name))
{
struct grub_video_bitmap *raw_bitmap;
- struct grub_video_bitmap *scaled_bitmap;
char *path;
path = grub_resolve_relative_path (theme_dir, value);
if (! path)
return grub_errno;
}
grub_free(path);
- grub_video_bitmap_create_scaled (&scaled_bitmap,
- view->screen.width,
- view->screen.height,
- raw_bitmap,
- GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
- grub_video_bitmap_destroy (raw_bitmap);
- if (! scaled_bitmap)
- {
- grub_error_push ();
- return grub_error (grub_errno, "error scaling desktop image");
- }
-
- grub_video_bitmap_destroy (view->desktop_image);
- view->desktop_image = scaled_bitmap;
+ grub_video_bitmap_destroy (view->raw_desktop_image);
+ view->raw_desktop_image = raw_bitmap;
+ }
+ else if (! grub_strcmp ("desktop-image-scale-method", name))
+ {
+ if (! value || ! grub_strcmp ("stretch", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
+ else if (! grub_strcmp ("crop", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
+ else if (! grub_strcmp ("padding", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
+ else if (! grub_strcmp ("fitwidth", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
+ else if (! grub_strcmp ("fitheight", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ _("Unsupported scale method: %s"),
+ grub_strdup (value));
+ }
+ else if (! grub_strcmp ("desktop-image-h-align", name))
+ {
+ if (! grub_strcmp ("left", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
+ else if (! grub_strcmp ("center", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
+ else if (! grub_strcmp ("right", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ _("Unsupported horizontal align method: %s"),
+ grub_strdup (value));
+ }
+ else if (! grub_strcmp ("desktop-image-v-align", name))
+ {
+ if (! grub_strcmp ("top", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
+ else if (! grub_strcmp ("center", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
+ else if (! grub_strcmp ("bottom", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ _("Unsupported vertical align method: %s"),
+ grub_strdup (value));
}
else if (! grub_strcmp ("desktop-color", name))
grub_video_parse_color (value, &view->desktop_color);
static void
init_terminal (grub_gfxmenu_view_t view);
+static void
+init_background (grub_gfxmenu_view_t view);
static grub_gfxmenu_view_t term_view;
/* Create a new view object, loading the theme specified by THEME_PATH and
view->title_color = default_fg_color;
view->message_color = default_bg_color;
view->message_bg_color = default_fg_color;
- view->desktop_image = 0;
+ view->raw_desktop_image = 0;
+ view->scaled_desktop_image = 0;
+ view->desktop_image_scale_method = GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
view->desktop_color = default_bg_color;
view->terminal_box = grub_gfxmenu_create_box (0, 0);
view->title_text = grub_strdup (_("GRUB Boot Menu"));
grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
grub_free (p);
}
- grub_video_bitmap_destroy (view->desktop_image);
+ grub_video_bitmap_destroy (view->raw_desktop_image);
+ grub_video_bitmap_destroy (view->scaled_desktop_image);
if (view->terminal_box)
view->terminal_box->destroy (view->terminal_box);
grub_free (view->terminal_font_name);
redraw_background (grub_gfxmenu_view_t view,
const grub_video_rect_t *bounds)
{
- if (view->desktop_image)
+ if (view->scaled_desktop_image)
{
- struct grub_video_bitmap *img = view->desktop_image;
+ struct grub_video_bitmap *img = view->scaled_desktop_image;
grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
bounds->x, bounds->y,
bounds->x - view->screen.x,
{
init_terminal (view);
+ init_background (view);
+
/* Clear the screen; there may be garbage left over in video memory. */
grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
view->screen.x, view->screen.y,
grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box;
}
+static void
+init_background (grub_gfxmenu_view_t view)
+{
+ if (view->scaled_desktop_image)
+ return;
+
+ struct grub_video_bitmap *scaled_bitmap;
+ if (view->desktop_image_scale_method ==
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH)
+ grub_video_bitmap_create_scaled (&scaled_bitmap,
+ view->screen.width,
+ view->screen.height,
+ view->raw_desktop_image,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ grub_video_bitmap_scale_proportional (&scaled_bitmap,
+ view->screen.width,
+ view->screen.height,
+ view->raw_desktop_image,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST,
+ view->desktop_image_scale_method,
+ view->desktop_image_v_align,
+ view->desktop_image_h_align);
+ if (! scaled_bitmap)
+ return;
+ view->scaled_desktop_image = scaled_bitmap;
+
+}
+
/* FIXME: previously notifications were displayed in special case.
Is it necessary?
*/
static grub_err_t scale_bilinear (struct grub_video_bitmap *dst,
struct grub_video_bitmap *src);
+static grub_err_t
+verify_source_bitmap (struct grub_video_bitmap *src)
+{
+ /* Verify the simplifying assumptions. */
+ if (src == 0)
+ return grub_error (GRUB_ERR_BUG,
+ "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_BUG,
+ "src format not supported for scale");
+ if (src->mode_info.width == 0 || src->mode_info.height == 0)
+ return grub_error (GRUB_ERR_BUG,
+ "source bitmap has a zero dimension");
+ if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp)
+ return grub_error (GRUB_ERR_BUG,
+ "bitmap to scale has inconsistent Bpp and bpp");
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_video_bitmap_scale (struct grub_video_bitmap *dst,
+ struct grub_video_bitmap *src,
+ enum grub_video_bitmap_scale_method scale_method)
+{
+ switch (scale_method)
+ {
+ case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST:
+ case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST:
+ return scale_nn (dst, src);
+ case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
+ case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
+ return scale_bilinear (dst, src);
+ default:
+ return grub_error (GRUB_ERR_BUG, "Invalid scale_method value");
+ }
+}
+
/* 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
{
*dst = 0;
- /* Verify the simplifying assumptions. */
- if (src == 0)
- return grub_error (GRUB_ERR_BUG,
- "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_NOT_IMPLEMENTED_YET,
- "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");
+ grub_err_t err = verify_source_bitmap(src);
+ if (err != GRUB_ERR_NONE)
+ return err;
if (dst_width <= 0 || dst_height <= 0)
return grub_error (GRUB_ERR_BUG,
"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_BUG,
- "bitmap to scale has inconsistent Bpp and bpp");
/* Create the new bitmap. */
grub_err_t ret;
if (ret != GRUB_ERR_NONE)
return ret; /* Error. */
- switch (scale_method)
+ ret = grub_video_bitmap_scale (*dst, src, scale_method);
+
+ if (ret == GRUB_ERR_NONE)
{
- case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST:
- case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST:
- ret = scale_nn (*dst, src);
+ /* 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;
+ }
+}
+
+static grub_err_t
+make_h_align (int *x, int *w, int new_w,
+ grub_video_bitmap_h_align_t h_align)
+{
+ grub_err_t ret = GRUB_ERR_NONE;
+ switch (h_align)
+ {
+ case GRUB_VIDEO_BITMAP_H_ALIGN_LEFT:
+ *x = 0;
break;
- case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
- case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
- ret = scale_bilinear (*dst, src);
+ case GRUB_VIDEO_BITMAP_H_ALIGN_CENTER:
+ *x = (*w - new_w) / 2;
+ break;
+ case GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT:
+ *x = *w - new_w;
+ break;
+ default:
+ ret = grub_error (GRUB_ERR_BUG, "Invalid h_align value");
+ break;
+ }
+ *w = new_w;
+ return ret;
+}
+
+static grub_err_t
+make_v_align (int *y, int *h, int new_h,
+ grub_video_bitmap_v_align_t v_align)
+{
+ grub_err_t ret = GRUB_ERR_NONE;
+ switch (v_align)
+ {
+ case GRUB_VIDEO_BITMAP_V_ALIGN_TOP:
+ *y = 0;
+ break;
+ case GRUB_VIDEO_BITMAP_V_ALIGN_CENTER:
+ *y = (*h - new_h) / 2;
+ break;
+ case GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM:
+ *y = *h - new_h;
+ break;
+ default:
+ ret = grub_error (GRUB_ERR_BUG, "Invalid v_align value");
+ break;
+ }
+ *h = new_h;
+ return ret;
+}
+
+grub_err_t
+grub_video_bitmap_scale_proportional (struct grub_video_bitmap **dst,
+ int dst_width, int dst_height,
+ struct grub_video_bitmap *src,
+ enum grub_video_bitmap_scale_method
+ scale_method,
+ grub_video_bitmap_selection_method_t
+ selection_method,
+ grub_video_bitmap_v_align_t v_align,
+ grub_video_bitmap_h_align_t h_align)
+{
+ *dst = 0;
+ grub_err_t ret = verify_source_bitmap(src);
+ if (ret != GRUB_ERR_NONE)
+ return ret;
+ if (dst_width <= 0 || dst_height <= 0)
+ return grub_error (GRUB_ERR_BUG,
+ "requested to scale to a size w/ a zero dimension");
+
+ ret = grub_video_bitmap_create (dst, dst_width, dst_height,
+ src->mode_info.blit_format);
+ if (ret != GRUB_ERR_NONE)
+ return ret; /* Error. */
+
+ int dx0 = 0;
+ int dy0 = 0;
+ int dw = dst_width;
+ int dh = dst_height;
+ int sx0 = 0;
+ int sy0 = 0;
+ int sw = src->mode_info.width;
+ int sh = src->mode_info.height;
+
+ switch (selection_method)
+ {
+ case GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP:
+ /* Comparing sw/sh VS dw/dh. */
+ if (sw * dh < dw * sh)
+ ret = make_v_align (&sy0, &sh, sw * dh / dw, v_align);
+ else
+ ret = make_h_align (&sx0, &sw, sh * dw / dh, h_align);
+ break;
+ case GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING:
+ if (sw * dh < dw * sh)
+ ret = make_h_align (&dx0, &dw, sw * dh / sh, h_align);
+ else
+ ret = make_v_align (&dy0, &dh, sh * dw / sw, v_align);
+ break;
+ case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH:
+ if (sw * dh < dw * sh)
+ ret = make_v_align (&sy0, &sh, sw * dh / dw, v_align);
+ else
+ ret = make_v_align (&dy0, &dh, sh * dw / sw, v_align);
+ break;
+ case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT:
+ if (sw * dh < dw * sh)
+ ret = make_h_align (&dx0, &dw, sw * dh / sh, h_align);
+ else
+ ret = make_h_align (&sx0, &sw, sh * dw / dh, h_align);
break;
default:
- ret = grub_error (GRUB_ERR_BUG, "Invalid scale_method value");
+ ret = grub_error (GRUB_ERR_BUG, "Invalid selection_method value");
break;
}
+ if (ret == GRUB_ERR_NONE)
+ {
+ /* Backup original data. */
+ int src_width_orig = src->mode_info.width;
+ int src_height_orig = src->mode_info.height;
+ grub_uint8_t *src_data_orig = src->data;
+ int dst_width_orig = (*dst)->mode_info.width;
+ int dst_height_orig = (*dst)->mode_info.height;
+ grub_uint8_t *dst_data_orig = (*dst)->data;
+
+ 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 = src->mode_info.bytes_per_pixel;
+
+ /* Crop src and dst. */
+ src->mode_info.width = sw;
+ src->mode_info.height = sh;
+ src->data = (grub_uint8_t *) src->data + sx0 * bytes_per_pixel
+ + sy0 * sstride;
+ (*dst)->mode_info.width = dw;
+ (*dst)->mode_info.height = dh;
+ (*dst)->data = (grub_uint8_t *) (*dst)->data + dx0 * bytes_per_pixel
+ + dy0 * dstride;
+
+ /* Scale our image. */
+ ret = grub_video_bitmap_scale (*dst, src, scale_method);
+
+ /* Restore original data. */
+ src->mode_info.width = src_width_orig;
+ src->mode_info.height = src_height_orig;
+ src->data = src_data_orig;
+ (*dst)->mode_info.width = dst_width_orig;
+ (*dst)->mode_info.height = dst_height_orig;
+ (*dst)->data = dst_data_orig;
+ }
+
if (ret == GRUB_ERR_NONE)
{
/* Success: *dst is now a pointer to the scaled bitmap. */
}
}
-/* 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_bitmaps (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
{
/* Verify the simplifying assumptions. */
if (dst == 0 || src == 0)
- return grub_error (GRUB_ERR_BUG, "null bitmap in scale_nn");
+ return grub_error (GRUB_ERR_BUG, "null bitmap in scale function");
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_NOT_IMPLEMENTED_YET,
+ return grub_error (GRUB_ERR_BUG,
"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_NOT_IMPLEMENTED_YET,
+ return grub_error (GRUB_ERR_BUG,
"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
src->mode_info.reserved_field_pos
|| dst->mode_info.reserved_mask_size !=
src->mode_info.reserved_mask_size)
- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ return grub_error (GRUB_ERR_BUG,
"dst and src not compatible");
if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|| src->mode_info.width == 0 || src->mode_info.height == 0)
return grub_error (GRUB_ERR_BUG, "bitmap has a zero dimension");
+ return GRUB_ERR_NONE;
+}
+
+/* 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)
+{
+ grub_err_t err = verify_bitmaps(dst, src);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
grub_uint8_t *ddata = dst->data;
grub_uint8_t *sdata = src->data;
int dw = dst->mode_info.width;
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_BUG, "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_NOT_IMPLEMENTED_YET, "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_NOT_IMPLEMENTED_YET, "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_NOT_IMPLEMENTED_YET, "dst and src not compatible");
- if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "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_BUG, "bitmap has a zero dimension");
+ grub_err_t err = verify_bitmaps(dst, src);
+ if (err != GRUB_ERR_NONE)
+ return err;
grub_uint8_t *ddata = dst->data;
grub_uint8_t *sdata = src->data;