# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: address@hidden # target_branch: file:///scratch/grub2-bzr/trunk/ # testament_sha1: 8415728ce2329e5fd5f014c5bbfc5e69eb9ceed2 # timestamp: 2010-02-18 00:45:55 +0100 # base_revision_id: address@hidden # # Begin patch === added file 'commands/leaf.xbm' --- commands/leaf.xbm 1970-01-01 00:00:00 +0000 +++ commands/leaf.xbm 2010-02-17 23:42:49 +0000 @@ -0,0 +1,14 @@ +#define leaf_width 32 +#define leaf_height 32 +static unsigned char leaf_bits[] = { + 0x00, 0xf0, 0x07, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x80, 0xff, 0xff, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x0f, + 0xf8, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x1f, + 0xfe, 0xff, 0xff, 0x3f, 0xfe, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x3f, + 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x03, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x3f, 0x00, + 0xff, 0xff, 0x07, 0x00, 0xff, 0x00, 0x00, 0x00 }; === modified file 'commands/videotest.c' --- commands/videotest.c 2010-02-16 12:23:08 +0000 +++ commands/videotest.c 2010-02-17 23:42:49 +0000 @@ -25,6 +25,102 @@ #include #include #include +#include +#include /*for some reason render target type is defined there */ +#include +#include "leaf.xbm" + +#if (leaf_width != leaf_height) +#error Leaf must be a square bitmap. +#endif +#define LEAF_SIZE leaf_width + +unsigned char leaf_data[LEAF_SIZE*LEAF_SIZE]; + +struct grub_video_bitmap leaves[4]; + +static void init_leaves(void) +{ + static int initialized = 0; + int i,j; + if (initialized) return; + + /* XBM bitmaps have bit order opposite to GRUB bitmaps. */ + for (i = 0; i < LEAF_SIZE * LEAF_SIZE; i++) + for (j = 0 ; j < 8 ; j++) + leaf_data[i] |= ((leaf_bits[i] >> j) & 1) << (7 - j); + + leaves[0].mode_info.width = LEAF_SIZE; + leaves[0].mode_info.height = LEAF_SIZE; + leaves[0].mode_info.transform = FB_TRAN_EAST; + leaves[0].mode_info.mode_type = + (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; + leaves[0].mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; + leaves[0].mode_info.bpp = 1; + leaves[0].mode_info.bytes_per_pixel = 0; + leaves[0].mode_info.pitch = LEAF_SIZE; + leaves[0].mode_info.number_of_colors = 2; + leaves[0].mode_info.bg_red = 128; + leaves[0].mode_info.fg_red = 255; + leaves[0].mode_info.bg_green = 128; + leaves[0].mode_info.bg_alpha = 128; + leaves[0].mode_info.fg_alpha = 255; + leaves[0].mode_info.fg_green = 255; + leaves[0].mode_info.bg_blue = 128; + leaves[0].mode_info.fg_blue = 255; + leaves[0].data = leaf_data; + + grub_memcpy(&leaves[1], leaves, sizeof(leaves[0])); + grub_memcpy(&leaves[2], leaves, 2* sizeof(leaves[0])); + leaves[1].mode_info.transform = FB_TRAN_NORTH; + leaves[2].mode_info.transform = FB_TRAN_WEST; + leaves[3].mode_info.transform = FB_TRAN_SOUTH; + leaves[0].mode_info.fg_red = 0; + leaves[1].mode_info.fg_green = 0; + leaves[2].mode_info.fg_blue = 0; + leaves[3].mode_info.fg_blue = 0; + leaves[3].mode_info.fg_green = 0; + leaves[3].mode_info.fg_red = 0; + + initialized = 1; +} + +#define BORDER 32 +#define SCROLL_INC_1 2 +#define SCROLL_INC_2 4 + +static void draw_leaves(void) +{ + int i; + grub_font_t sans; + grub_video_color_t text_color, fill_color, back_color; + + sans = grub_font_get ("Unknown Regular 16"); + text_color = grub_video_map_rgb (255, 255, 255); + fill_color = grub_video_map_rgba (0, 192, 0, 192); + back_color = grub_video_map_rgba (0, 0, 255, 255); + + grub_video_fill_rect(fill_color, 0, 0, BORDER, BORDER); + grub_video_blit_bitmap(leaves, GRUB_VIDEO_BLIT_REPLACE, BORDER, BORDER, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 1, GRUB_VIDEO_BLIT_REPLACE, BORDER+LEAF_SIZE, BORDER, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 2, GRUB_VIDEO_BLIT_REPLACE, BORDER+LEAF_SIZE, BORDER+LEAF_SIZE, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 3, GRUB_VIDEO_BLIT_REPLACE, BORDER, BORDER+LEAF_SIZE, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves, GRUB_VIDEO_BLIT_BLEND, BORDER+LEAF_SIZE*2, BORDER, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 1, GRUB_VIDEO_BLIT_BLEND, BORDER+LEAF_SIZE*3, BORDER, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 2, GRUB_VIDEO_BLIT_BLEND, BORDER+LEAF_SIZE*3, BORDER+LEAF_SIZE, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_blit_bitmap(leaves + 3, GRUB_VIDEO_BLIT_BLEND, BORDER+LEAF_SIZE*2, BORDER+LEAF_SIZE, 0, 0, LEAF_SIZE, LEAF_SIZE); + grub_video_fill_rect(fill_color, BORDER+LEAF_SIZE*4, BORDER, LEAF_SIZE*2, LEAF_SIZE*2); + grub_font_draw_string ("Leaves", sans, text_color, BORDER + LEAF_SIZE / 2, 4*LEAF_SIZE + BORDER); + for (i = 0; i < BORDER ; i+=SCROLL_INC_1) + grub_video_scroll(back_color,-SCROLL_INC_1,0); + for (i = 0; i < BORDER ; i+=SCROLL_INC_1) + grub_video_scroll(back_color,0,-SCROLL_INC_1); + for (i = 0; i < BORDER ; i+=SCROLL_INC_2) + grub_video_scroll(back_color,+SCROLL_INC_2,+SCROLL_INC_2); + grub_video_fill_rect(fill_color, 0, 0, BORDER, BORDER); + +} static grub_err_t grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), @@ -33,10 +129,10 @@ { grub_err_t err; grub_video_color_t color; - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; + int x; + int y; + int width; + int height; int i; grub_font_t sansbig; grub_font_t sans; @@ -52,7 +148,55 @@ if (err) return err; - grub_video_get_viewport (&x, &y, &width, &height); + if (1) /* TODO: check that we are actually using a video_fb target */ + { + struct grub_video_fbrender_target * tgt; + grub_video_color_t frame = grub_video_map_rgb(255,0,0); + grub_video_fb_get_active_render_target(&tgt); + + init_leaves(); + grub_video_get_viewport (&x, &y, &width, &height); + x += BORDER; y += BORDER; width -= 2* BORDER; height -= 2* BORDER; + grub_video_fill_rect(frame, x-1, y-1, width+2, height+2); + frame = grub_video_map_rgb(0,0,0); + grub_video_fill_rect(frame, x, y, width, height); + grub_video_set_viewport (x, y, width, height); + + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_MIRROR; + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_FLIP|FB_TRAN_MIRROR; + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_FLIP; + draw_leaves(); + + grub_getkey (); + + tgt->mode_info.transform=FB_TRAN_NORTH; + color = grub_video_map_rgb (0, 0, 0); + grub_video_fill_rect (color, 0, 0, width, height); + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_EAST; + grub_video_set_viewport (y, x, height, width); + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_SOUTH; + grub_video_set_viewport (x, y, width, height); + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_WEST; + grub_video_set_viewport (y, x, height, width); + draw_leaves(); + + tgt->mode_info.transform=FB_TRAN_NORTH; + grub_video_set_viewport (x, y, width, height); + + grub_getkey (); + } grub_video_create_render_target (&text_layer, width, height, GRUB_VIDEO_MODE_TYPE_RGB @@ -118,7 +262,8 @@ str = "Unicode test: happy\xE2\x98\xBA \xC2\xA3 5.00" " \xC2\xA1\xCF\x84\xC3\xA4u! " - " \xE2\x84\xA4\xE2\x8A\x87\xE2\x84\x9D"; + " \xE2\x84\xA4\xE2\x8A\x87\xE2\x84\x9D" + " \343\201\202!" /* hiragana letter a */; color = grub_video_map_rgb (128, 128, 255); /* All characters in the string exist in the 'Fixed 20' (10x20) font. */ === modified file 'font/font.c' --- font/font.c 2010-02-16 12:23:08 +0000 +++ font/font.c 2010-02-17 23:14:11 +0000 @@ -1062,8 +1062,10 @@ if (glyph->width == 0 || glyph->height == 0) return GRUB_ERR_NONE; + /* TODO: add support for bitmaps to create_bitmap */ glyph_bitmap.mode_info.width = glyph->width; glyph_bitmap.mode_info.height = glyph->height; + glyph_bitmap.mode_info.transform = 0; glyph_bitmap.mode_info.mode_type = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; === modified file 'gfxmenu/gfxmenu.c' --- gfxmenu/gfxmenu.c 2010-01-06 09:50:50 +0000 +++ gfxmenu/gfxmenu.c 2010-02-17 23:14:11 +0000 @@ -53,6 +53,7 @@ struct grub_menu_viewer *instance; grub_err_t err; struct grub_video_mode_info mode_info; + int screen_width, screen_height; theme_path = grub_env_get ("theme"); if (! theme_path) @@ -81,14 +82,16 @@ return err; } + grub_video_get_max_viewport(&screen_width, &screen_height); + if (!cached_view || grub_strcmp (cached_view->theme_path, theme_path) != 0 - || cached_view->screen.width != mode_info.width - || cached_view->screen.height != mode_info.height) + || cached_view->screen.width != (unsigned)screen_width + || cached_view->screen.height != (unsigned)screen_height) { grub_free (cached_view); /* Create the view. */ - cached_view = grub_gfxmenu_view_new (theme_path, mode_info.width, - mode_info.height); + cached_view = grub_gfxmenu_view_new (theme_path, screen_width, + screen_height); } if (! cached_view) === modified file 'include/grub/fbblit.h' --- include/grub/fbblit.h 2009-08-28 13:54:20 +0000 +++ include/grub/fbblit.h 2010-02-17 23:14:11 +0000 @@ -28,7 +28,7 @@ grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, struct grub_video_fbblit_info *src, int x, int y, int width, int height, - int offset_x, int offset_y); + int offset_x, int offset_y, int transform); void grub_video_fbblit_replace_directN (struct grub_video_fbblit_info *dst, @@ -94,7 +94,7 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, struct grub_video_fbblit_info *src, int x, int y, int width, int height, - int offset_x, int offset_y); + int offset_x, int offset_y, int transform); void grub_video_fbblit_blend_BGRA8888_RGBA8888 (struct grub_video_fbblit_info *dst, === modified file 'include/grub/fbfill.h' --- include/grub/fbfill.h 2009-08-18 17:26:35 +0000 +++ include/grub/fbfill.h 2010-02-17 22:25:37 +0000 @@ -32,10 +32,10 @@ struct { - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; + int x; + int y; + int width; + int height; } viewport; /* Indicates whether the data has been allocated by us and must be freed === added file 'include/grub/fbtran.h' --- include/grub/fbtran.h 1970-01-01 00:00:00 +0000 +++ include/grub/fbtran.h 2010-02-17 23:14:11 +0000 @@ -0,0 +1,233 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ + + +#ifndef GRUB_FBTRAN_HEADER +#define GRUB_FBTRAN_HEADER 1 + +#include +#include +#include + +/* NOTE: This header is private header for fb driver and should not be used + in other parts of the code. */ + +/* Supported operations are simple and easy to understand (and also happen to + * form a symmetry group). + * + * MIRROR | swap image across (around) the vertical axis 1-2 2-1 + * | | -> | | + * 3-4 4-3 + * FLIP - swap image across the horizontal axis - upside down + * SWAP \ swap image across the x=y axis - swap the x and y coordinates + * + * All the operations are self-inversions. + * || = -- = // = 0 (identity) + * + * | and - are commutative. + * + * |- = -| + * + * The relationship between \ and |,- is more peculiar: + * + * \- = |\ + * \| = -\ + * + * The typical display operations used to adjust displayed picture for use with + * rotated display equipment and/or mirrors are FLIP, MIRROR, and rot90, + * rot180, rot270. + * + * The way this rotation works is somewhat confusing. If we say "rotate left" + * does that mean to rotate the screen left (and rotate the picture right to + * compensate) or the other way around? + * + * So I will try to explain in different terms for clarity. + * Let's say that what you normally get is "facing the north side of the + * screen" or "N" for short. If you turn the screen anti-clockwise you get E, + * etc. + * + * If we apply the simple transforms (MIRROR, FLIP) first to get the correct + * picture when facing the east side of the screen we need the -\ transform. + * + * E = -rot90 = -\ + * S = -rot180 = -\-\ = -|\\ = -| + * W = -rot270 = -|-\ = |\ + * + * As is seen from the above every supported screen transform can be represented by + * applying the basic -,| and \ operations at most ance in a fixed order. + * Thus each supoprted transform can be represented by a three-bit bitfield specifying + * which basic operations should be applied. + * + * Forward transform is from user visible cordinates to framebuffer memory coordinates. + */ + +typedef enum { + FB_TRAN_SWAP = (1 << 0), + FB_TRAN_FLIP = (1 << 1), + FB_TRAN_MIRROR = (1 << 2), + //FB_TRAN_MASK = 7, + //FB_TRAN_SIMPLE = 6, + FB_TRAN_NORTH = 0, + FB_TRAN_EAST = (FB_TRAN_FLIP | FB_TRAN_SWAP), + FB_TRAN_SOUTH = (FB_TRAN_FLIP | FB_TRAN_MIRROR), + FB_TRAN_WEST = (FB_TRAN_MIRROR | FB_TRAN_SWAP), +} fb_trans_t; + +/* internal function used for transforms */ +static inline int fb_tran_swap_tran(int transform) +{ + int old = transform; + transform &= FB_TRAN_SWAP; + if (old & FB_TRAN_MIRROR) + transform ^= FB_TRAN_FLIP; + if (old & FB_TRAN_FLIP) + transform ^= FB_TRAN_MIRROR; + return transform; +} + +/* Return a new bitmap for the transformation which is the result of applying + * the transformations present in the first bitmap, and then transformations in + * the second bitmap (composition). Should work on modes that have the same + * width and height at the point the transfom is appended. In other words: + * Internal use only. */ +static inline int fb_tran_append(int transform_first, int transform_second) { + return (transform_first & FB_TRAN_SWAP) ? (transform_first ^ + fb_tran_swap_tran(transform_second)) + : (transform_first ^ transform_second); } + +/* Return a bitmap for transformation that negates the specified + * transformation. (inversion) + * Note: the possible operations aren't commutative but left and right + * inversion are the same for supported operations.*/ +static inline int fb_tran_invert(int transform) { return (transform & + FB_TRAN_SWAP) ? + fb_tran_swap_tran(transform) : transform; } + +/* Create a bitmap for transformation that has to be applied after the first + * transformation to obtain the second transformation. (decomposition/factorization) + * Internal use only.*/ +static inline int fb_tran_diff(int transform_first, int transform_second) +{ + return fb_tran_append(fb_tran_invert(transform_first), transform_second); +} + +/* transform screen dimensions */ +static inline grub_err_t fb_tran_dim_back(int * width, int * height, int transform) +{ + if (transform & FB_TRAN_SWAP) + grub_swap(*width, *height); + return GRUB_ERR_NONE; +} + +static inline grub_err_t fb_tran_dim(unsigned * width, unsigned * height, int transform) +{ + if (transform & FB_TRAN_SWAP) + grub_swap(*width, *height); + return GRUB_ERR_NONE; +} + +/* internal - apply coordinate transform to a point */ +static inline grub_err_t fb_tran_point_intern(int *x, int *y, int width, int height, int transform, int user_coordinates) +{ + if (user_coordinates && (transform & FB_TRAN_SWAP)) + grub_swap(width, height); + if (transform & FB_TRAN_MIRROR) + *x = width -1 - *x; + if (transform & FB_TRAN_FLIP) + *y = height -1 - *y; + if (transform & FB_TRAN_SWAP) + grub_swap(*x, *y); + return GRUB_ERR_NONE; +} + +/* apply coordinate transform to a point */ +static inline grub_err_t fb_tran_point(int *x, int *y, const struct grub_video_mode_info *mode) +{ + return fb_tran_point_intern(x, y, mode->width, mode->height, mode->transform, 1); +} + +/* apply reverse coordinate transform to a point */ +static inline grub_err_t fb_tran_point_back(int *x, int *y, const struct grub_video_mode_info *mode) +{ + return fb_tran_point_intern(x, y, mode->width, mode->height, fb_tran_invert(mode->transform), 0); +} + +/* internal - apply coordinate transform to a rectangle */ +static inline grub_err_t fb_tran_rect_intern(int *x, int *y, int *width, int *height, int mode_width, int mode_height, int transform, int user_coordinates) +{ + int x2, y2; + grub_fb_norm_rect(x, y, width, height); + x2 = *x + *width; + y2 = *y + *height; + fb_tran_point_intern(x, y, mode_width, mode_height, transform, user_coordinates); + fb_tran_point_intern(&x2, &y2, mode_width, mode_height, transform, user_coordinates); + if(*x > x2) + { + grub_swap(*x, x2); + (*x)++; + x2++; + } + if(*y > y2) + { + grub_swap(*y, y2); + (*y)++; + y2++; + } + *width = x2 - *x; + *height = y2 - *y; + return GRUB_ERR_NONE; +} + +/* apply coordinate transform to a rectangle */ +static inline grub_err_t fb_tran_rect(int *x, int *y, int *width, int *height, const struct grub_video_mode_info *mode) +{ + return fb_tran_rect_intern(x, y, width, height, mode->width, mode->height, mode->transform,1); +} + +/* apply reverse coordinate transform to a rectangle */ +static inline grub_err_t fb_tran_rect_back(int *x, int *y, int *width, int *height, const struct grub_video_mode_info *mode) +{ + return fb_tran_rect_intern(x, y, width, height, mode->width, mode->height, fb_tran_invert(mode->transform),0); +} + +/* Return bitmap of transform that is to be applied during blit. + * The source and target point are transformed to the framebuffer coordinates + * so that src_x,src_y is copied to x,y. + */ +static inline grub_err_t fb_tran_blit(int *src_x, int *src_y, int *width, int *height, + int *x, int *y, int *transform, + const struct grub_video_mode_info *source_mode, + const struct grub_video_mode_info *target_mode) +{ + int sx1 = *src_x; + int sy1 = *src_y; + int sx2, sy2; + + fb_tran_rect(src_x, src_y, width, height, source_mode); + sx2 = *src_x; + sy2 = *src_y; + fb_tran_point_back(&sx2, &sy2, source_mode); + *x += sx2 - sx1; + *y += sy2 - sy1; + fb_tran_point(x, y, target_mode); + *transform = fb_tran_diff(source_mode->transform, target_mode->transform); + return GRUB_ERR_NONE; +} + + +#endif /* ! GRUB_FBTRAN_HEADER */ === modified file 'include/grub/gui.h' --- include/grub/gui.h 2010-01-06 00:42:21 +0000 +++ include/grub/gui.h 2010-02-17 22:25:37 +0000 @@ -173,10 +173,10 @@ static __inline void grub_gui_save_viewport (grub_video_rect_t *r) { - grub_video_get_viewport ((unsigned *) &r->x, - (unsigned *) &r->y, - (unsigned *) &r->width, - (unsigned *) &r->height); + grub_video_get_viewport ((int *) &r->x, + (int *) &r->y, + (int *) &r->width, + (int *) &r->height); } static __inline void === modified file 'include/grub/misc.h' --- include/grub/misc.h 2010-02-03 00:24:07 +0000 +++ include/grub/misc.h 2010-02-17 23:14:11 +0000 @@ -267,6 +267,8 @@ /* Inline functions. */ +#define grub_swap(a,b) {typeof (a) _tmpval ## __LINE__; _tmpval ## __LINE__ = (b); (b) = (a); (a) = _tmpval ## __LINE__; } + static inline unsigned int grub_abs (int x) { @@ -285,6 +287,15 @@ return y; } +static inline long +grub_min (long x, long y) +{ + if (x > y) + return y; + else + return x; +} + /* Rounded-up division */ static inline unsigned int grub_div_roundup (unsigned int x, unsigned int y) === modified file 'include/grub/video.h' --- include/grub/video.h 2010-02-03 00:24:07 +0000 +++ include/grub/video.h 2010-02-17 23:14:11 +0000 @@ -155,6 +155,8 @@ grub_uint8_t fg_green; grub_uint8_t fg_blue; grub_uint8_t fg_alpha; + + int transform; }; /* A 2D rectangle type. */ @@ -196,7 +198,8 @@ grub_err_t (*fini) (void); grub_err_t (*setup) (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask); + unsigned int mode_type, unsigned int mode_mask, + int transform); grub_err_t (*get_info) (struct grub_video_mode_info *mode_info); @@ -209,11 +212,11 @@ grub_err_t (*get_palette) (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); - grub_err_t (*set_viewport) (unsigned int x, unsigned int y, - unsigned int width, unsigned int height); - - grub_err_t (*get_viewport) (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height); + grub_err_t (*set_viewport) (int x, int y, int width, int height); + + grub_err_t (*get_max_viewport) (int *width, int *height); + + grub_err_t (*get_viewport) (int *x, int *y, int *width, int *height); grub_video_color_t (*map_color) (grub_uint32_t color_name); @@ -228,17 +231,17 @@ grub_uint8_t *blue, grub_uint8_t *alpha); grub_err_t (*fill_rect) (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t (*blit_bitmap) (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t (*blit_render_target) (struct grub_video_render_target *source, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t (*scroll) (grub_video_color_t color, int dx, int dy); @@ -283,11 +286,13 @@ grub_err_t grub_video_get_palette (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); -grub_err_t grub_video_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height); - -grub_err_t grub_video_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height); +grub_err_t grub_video_set_viewport (int x, int y, + int width, int height); + +grub_err_t grub_video_get_max_viewport (int *width, int *height); + +grub_err_t grub_video_get_viewport (int *x, int *y, + int *width, int *height); grub_video_color_t grub_video_map_color (grub_uint32_t color_name); @@ -302,19 +307,18 @@ grub_uint8_t *blue, grub_uint8_t *alpha); grub_err_t grub_video_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t grub_video_blit_render_target (struct grub_video_render_target *source, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, - unsigned int height); + int width, int height); grub_err_t grub_video_scroll (grub_video_color_t color, int dx, int dy); === modified file 'include/grub/video_fb.h' --- include/grub/video_fb.h 2009-11-14 16:00:39 +0000 +++ include/grub/video_fb.h 2010-02-17 23:14:11 +0000 @@ -16,6 +16,12 @@ * along with GRUB. If not, see . */ +/* SPECIAL NOTE! + + - In this driver we assume that every memory can be accessed by same memory + bus. If there are different address spaces do not use this code as a base + code for other archs. */ + #ifndef GRUB_VIDEO_FB_HEADER #define GRUB_VIDEO_FB_HEADER 1 @@ -30,6 +36,22 @@ struct grub_video_fbrender_target; +/* make width and height of a rectangle non-negative */ +static inline grub_err_t grub_fb_norm_rect(int *x, int *y, int *width, int *height) +{ + if(*width < 0) + { + *width = -*width; + *x -= *width - 1; + } + if(*height < 0) + { + *height = -*height; + *y -= *height - 1; + } + return GRUB_ERR_NONE; +} + #define GRUB_VIDEO_FBSTD_NUMCOLORS 16 extern struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_NUMCOLORS]; @@ -49,11 +71,14 @@ grub_video_fb_set_palette (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); grub_err_t -grub_video_fb_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height); -grub_err_t -grub_video_fb_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height); +grub_video_fb_set_viewport (int x, int y, + int width, int height); +grub_err_t +grub_video_fb_get_max_viewport (int *width, int *height); + +grub_err_t +grub_video_fb_get_viewport (int *x, int *y, + int *width, int *height); grub_video_color_t grub_video_fb_map_color (grub_uint32_t color_name); @@ -79,19 +104,19 @@ grub_err_t grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); + int width, int height); grub_err_t grub_video_fb_scroll (grub_video_color_t color, int dx, int dy); === modified file 'term/gfxterm.c' --- term/gfxterm.c 2010-02-03 00:24:07 +0000 +++ term/gfxterm.c 2010-02-17 23:14:11 +0000 @@ -319,6 +319,7 @@ grub_video_color_t color; grub_err_t err; int double_redraw; + int view_width, view_height; err = grub_video_get_info (&mode_info); /* Figure out what mode we ended up. */ @@ -329,14 +330,15 @@ double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + grub_video_get_max_viewport(&view_width, &view_height); /* Make sure screen is black. */ color = grub_video_map_rgb (0, 0, 0); - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (color, 0, 0, view_width, view_height); if (double_redraw) { grub_video_swap_buffers (); - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (color, 0, 0, view_width, view_height); } bitmap = 0; @@ -348,7 +350,7 @@ grub_gfxterm_decorator_hook = NULL; return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, - 0, 0, mode_info.width, mode_info.height, + 0, 0, view_width, view_height, double_redraw, font_name, DEFAULT_BORDER_WIDTH); } @@ -417,10 +419,10 @@ grub_video_set_active_render_target (render_target); /* Save viewport and set it to our window. */ - grub_video_get_viewport ((unsigned *) &saved_view.x, - (unsigned *) &saved_view.y, - (unsigned *) &saved_view.width, - (unsigned *) &saved_view.height); + grub_video_get_viewport ((int *) &saved_view.x, + (int *) &saved_view.y, + (int *) &saved_view.width, + (int *) &saved_view.height); grub_video_set_viewport (window.x, window.y, window.width, window.height); if (bitmap) @@ -707,10 +709,10 @@ grub_video_set_active_render_target (render_target); /* Save viewport and set it to our window. */ - grub_video_get_viewport ((unsigned *) &saved_view.x, - (unsigned *) &saved_view.y, - (unsigned *) &saved_view.width, - (unsigned *) &saved_view.height); + grub_video_get_viewport ((int *) &saved_view.x, + (int *) &saved_view.y, + (int *) &saved_view.width, + (int *) &saved_view.height); grub_video_set_viewport (window.x, window.y, window.width, window.height); i = window.double_repaint ? 2 : 1; === modified file 'util/grub-mkrescue.in' --- util/grub-mkrescue.in 2009-12-22 17:20:27 +0000 +++ util/grub-mkrescue.in 2010-01-19 10:41:54 +0000 @@ -37,7 +37,7 @@ # Print the usage. usage () { cat <. EOF === modified file 'util/sdl.c' --- util/sdl.c 2010-02-07 01:47:18 +0000 +++ util/sdl.c 2010-02-17 23:14:11 +0000 @@ -70,7 +70,8 @@ static grub_err_t grub_video_sdl_setup (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask) + unsigned int mode_type, unsigned int mode_mask, + int transform) { int depth; int flags = 0; @@ -102,6 +103,10 @@ grub_memset (&sdl_render_target, 0, sizeof (sdl_render_target)); + /* TODO: transform the picture back before displaying in the window. + * Also swap width and height as required. */ + mode_info.transform = transform; + mode_info.width = window->w; mode_info.height = window->h; mode_info.mode_type = 0; @@ -208,6 +213,7 @@ .set_palette = grub_video_sdl_set_palette, .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_fb_set_viewport, + .get_max_viewport = grub_video_fb_get_max_viewport, .get_viewport = grub_video_fb_get_viewport, .map_color = grub_video_fb_map_color, .map_rgb = grub_video_fb_map_rgb, === modified file 'video/bitmap.c' --- video/bitmap.c 2009-12-24 22:53:05 +0000 +++ video/bitmap.c 2010-02-17 23:14:11 +0000 @@ -75,6 +75,7 @@ mode_info->width = width; mode_info->height = height; mode_info->blit_format = blit_format; + mode_info->transform = 0; switch (blit_format) { === modified file 'video/efi_gop.c' --- video/efi_gop.c 2010-02-03 00:24:07 +0000 +++ video/efi_gop.c 2010-02-17 23:14:11 +0000 @@ -185,7 +185,8 @@ static grub_err_t grub_video_gop_setup (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused)), + int transform) { unsigned int depth; struct grub_efi_gop_mode_info *info = NULL; @@ -288,6 +289,7 @@ return err; } + framebuffer.mode_info.transform = transform; framebuffer.ptr = (void *) (grub_addr_t) gop->mode->fb_base; grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n", @@ -363,6 +365,7 @@ .set_palette = grub_video_fb_set_palette, .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_fb_set_viewport, + .get_max_viewport = grub_video_fb_get_max_viewport, .get_viewport = grub_video_fb_get_viewport, .map_color = grub_video_fb_map_color, .map_rgb = grub_video_fb_map_rgb, === modified file 'video/efi_uga.c' --- video/efi_uga.c 2010-02-03 00:24:07 +0000 +++ video/efi_uga.c 2010-02-17 23:14:11 +0000 @@ -198,7 +198,8 @@ static grub_err_t grub_video_uga_setup (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused)), + int transform) { unsigned int depth; int found = 0; @@ -231,6 +232,7 @@ grub_err_t err; framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.transform = transform; framebuffer.mode_info.bpp = 32; framebuffer.mode_info.bytes_per_pixel = 4; framebuffer.mode_info.number_of_colors = 256; /* TODO: fix me. */ @@ -310,6 +312,7 @@ .set_palette = grub_video_fb_set_palette, .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_fb_set_viewport, + .get_max_viewport = grub_video_fb_get_max_viewport, .get_viewport = grub_video_fb_get_viewport, .map_color = grub_video_fb_map_color, .map_rgb = grub_video_fb_map_rgb, === modified file 'video/fb/fbblit.c' --- video/fb/fbblit.c 2009-08-28 13:54:20 +0000 +++ video/fb/fbblit.c 2010-02-17 23:14:11 +0000 @@ -16,13 +16,7 @@ * along with GRUB. If not, see . */ -/* SPECIAL NOTES! - - Please note following when reading the code below: - - - In this driver we assume that every memory can be accessed by same memory - bus. If there are different address spaces do not use this code as a base - code for other archs. +/* SPECIAL NOTE! - Every function in this code assumes that bounds checking has been done in previous phase and they are opted out in here. */ @@ -33,16 +27,19 @@ #include #include #include +#include /* Generic replacing blitter (slow). Works for every supported format. */ void grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, struct grub_video_fbblit_info *src, int x, int y, int width, int height, - int offset_x, int offset_y) + int offset_x, int offset_y, int transform) { int i; int j; + int dx = (transform & FB_TRAN_MIRROR) ? -1 : 1; + int dy = (transform & FB_TRAN_FLIP) ? -1 : 1; grub_uint8_t src_red; grub_uint8_t src_green; grub_uint8_t src_blue; @@ -62,8 +59,11 @@ dst_color = grub_video_fb_map_rgba (src_red, src_green, src_blue, src_alpha); - set_pixel (dst, x + i, y + j, dst_color); - } + if (transform & FB_TRAN_SWAP) + set_pixel (dst, x + j*dy, y + i*dx, dst_color); + else + set_pixel (dst, x + i*dx, y + j*dy, dst_color); + } } } @@ -711,10 +711,12 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, struct grub_video_fbblit_info *src, int x, int y, int width, int height, - int offset_x, int offset_y) + int offset_x, int offset_y, int transform) { int i; int j; + int dx = (transform & FB_TRAN_MIRROR) ? -1 : 1; + int dy = (transform & FB_TRAN_FLIP) ? -1 : 1; for (j = 0; j < height; j++) { @@ -741,12 +743,17 @@ if (src_alpha == 255) { dst_color = grub_video_fb_map_rgba (src_red, src_green, - src_blue, src_alpha); - set_pixel (dst, x + i, y + j, dst_color); + src_blue, src_alpha); + if (transform & FB_TRAN_SWAP) + set_pixel (dst, x + j*dy, y + i*dx, dst_color); + else + set_pixel (dst, x + i*dx, y + j*dy, dst_color); continue; } - dst_color = get_pixel (dst, x + i, y + j); + dst_color = (transform & FB_TRAN_SWAP) ? + get_pixel (dst, x + j*dy, y + i*dx) : + get_pixel (dst, x + i*dx, y + j*dy) ; grub_video_fb_unmap_color_int (dst, dst_color, &dst_red, &dst_green, &dst_blue, &dst_alpha); @@ -762,7 +769,10 @@ dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue, dst_alpha); - set_pixel (dst, x + i, y + j, dst_color); + if (transform & FB_TRAN_SWAP) + set_pixel (dst, x + j*dy, y + i*dx, dst_color); + else + set_pixel (dst, x + i*dx, y + j*dy, dst_color); } } } === modified file 'video/fb/fbfill.c' --- video/fb/fbfill.c 2009-08-14 12:41:58 +0000 +++ video/fb/fbfill.c 2010-01-19 20:31:47 +0000 @@ -16,13 +16,7 @@ * along with GRUB. If not, see . */ -/* SPECIAL NOTES! - - Please note following when reading the code below: - - - In this driver we assume that every memory can be accessed by same memory - bus. If there are different address spaces do not use this code as a base - code for other archs. +/* SPECIAL NOTE! - Every function in this code assumes that bounds checking has been done in previous phase and they are opted out in here. */ === modified file 'video/fb/fbutil.c' --- video/fb/fbutil.c 2009-08-18 17:26:35 +0000 +++ video/fb/fbutil.c 2010-01-19 20:31:47 +0000 @@ -16,13 +16,7 @@ * along with GRUB. If not, see . */ -/* SPECIAL NOTES! - - Please note following when reading the code below: - - - In this driver we assume that every memory can be accessed by same memory - bus. If there are different address spaces do not use this code as a base - code for other archs. +/* SPECIAL NOTE! - Every function in this code assumes that bounds checking has been done in previous phase and they are opted out in here. */ === modified file 'video/fb/video_fb.c' --- video/fb/video_fb.c 2010-02-03 00:24:07 +0000 +++ video/fb/video_fb.c 2010-02-17 23:14:11 +0000 @@ -23,6 +23,7 @@ #include #include #include +#include #include static struct grub_video_fbrender_target *render_target; @@ -119,28 +120,37 @@ } grub_err_t -grub_video_fb_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height) +grub_video_fb_set_viewport (int x, int y, int width, int height) { - /* Make sure viewport is withing screen dimensions. If viewport was set + /* Make sure viewport is within screen dimensions. If viewport was set to be out of the region, mark its size as zero. */ - if (x > render_target->mode_info.width) + int mode_width, mode_height; + + grub_video_fb_get_max_viewport (&mode_width, &mode_height); + grub_fb_norm_rect(&x, &y, &width, &height); + + if (x > mode_width) { x = 0; width = 0; } - if (y > render_target->mode_info.height) + if (y > mode_height) { y = 0; height = 0; } - if (x + width > render_target->mode_info.width) - width = render_target->mode_info.width - x; - - if (y + height > render_target->mode_info.height) - height = render_target->mode_info.height - y; + if (x < 0) { width += x; x = 0; } + if (width < 0) width = 0; + if (y < 0) { height += y; y = 0; } + if (height < 0) height = 0; + + if (x + width > mode_width) + width = mode_width - x; + + if (y + height > mode_height) + height = mode_height - y; render_target->viewport.x = x; render_target->viewport.y = y; @@ -151,8 +161,19 @@ } grub_err_t -grub_video_fb_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height) +grub_video_fb_get_max_viewport (int *width, int *height) +{ + unsigned mode_width = render_target->mode_info.width; + unsigned mode_height = render_target->mode_info.height; + fb_tran_dim(&mode_width, &mode_height, render_target->mode_info.transform); + if (width) *width = mode_width; + if (height) *height = mode_height; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_viewport (int *x, int *y, int *width, int *height) { if (x) *x = render_target->viewport.x; if (y) *y = render_target->viewport.y; @@ -401,14 +422,15 @@ grub_err_t grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height) + int width, int height) { struct grub_video_fbblit_info target; + grub_fb_norm_rect(&x, &y, &width, &height); /* Make sure there is something to do. */ - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) + if ((x >= render_target->viewport.width) || (x + width < 0)) return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) + if ((y >= render_target->viewport.height) || (y + height < 0)) return GRUB_ERR_NONE; /* Do not allow drawing out of viewport. */ @@ -436,6 +458,9 @@ target.mode_info = &render_target->mode_info; target.data = render_target->data; + /* transform coordinates */ + fb_tran_rect(&x, &y, &width, &height, &render_target->mode_info); + /* Try to figure out more optimized version. Note that color is already mapped to target format so we can make assumptions based on that. */ if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) @@ -487,11 +512,15 @@ common_blitter (struct grub_video_fbblit_info *target, struct grub_video_fbblit_info *source, enum grub_video_blit_operators oper, int x, int y, - unsigned int width, unsigned int height, + int width, int height, int offset_x, int offset_y) { + int transform; + fb_tran_blit(&offset_x, &offset_y, &width, &height, &x, &y, &transform, + source->mode_info, target->mode_info); if (oper == GRUB_VIDEO_BLIT_REPLACE) { + if (!transform) { /* Try to figure out more optimized version for replace operator. */ if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) { @@ -620,13 +649,15 @@ return; } } + } /* !transform */ /* No optimized replace operator found, use default (slow) blitter. */ grub_video_fbblit_replace (target, source, x, y, width, height, - offset_x, offset_y); + offset_x, offset_y, transform); } else { + if (!transform) { /* Try to figure out more optimized blend operator. */ if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) { @@ -742,10 +773,11 @@ } + } /* !transform */ /* No optimized blend operation found, use default (slow) blitter. */ grub_video_fbblit_blend (target, source, x, y, width, height, - offset_x, offset_y); + offset_x, offset_y, transform); } } @@ -753,27 +785,34 @@ grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height) + int width, int height) { struct grub_video_fbblit_info source; struct grub_video_fbblit_info target; + int src_x = offset_x; + int src_y = offset_y; + + /* Normalize source rectangle and shift target insert point accordingly. */ + grub_fb_norm_rect(&offset_x, &offset_y, &width, &height); + x += offset_x - src_x; + y += offset_y - src_y; /* Make sure there is something to do. */ if ((width == 0) || (height == 0)) return GRUB_ERR_NONE; - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) + if ((x >= render_target->viewport.width) || (x + width < 0)) return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) + if ((y >= render_target->viewport.height) || (y + height < 0)) return GRUB_ERR_NONE; if ((x + (int)bitmap->mode_info.width) < 0) return GRUB_ERR_NONE; if ((y + (int)bitmap->mode_info.height) < 0) return GRUB_ERR_NONE; if ((offset_x >= (int)bitmap->mode_info.width) - || (offset_x + (int)width < 0)) + || (offset_x + width < 0)) return GRUB_ERR_NONE; if ((offset_y >= (int)bitmap->mode_info.height) - || (offset_y + (int)height < 0)) + || (offset_y + height < 0)) return GRUB_ERR_NONE; /* If we have negative coordinates, optimize drawing to minimum. */ @@ -811,16 +850,16 @@ if ((y + height) > render_target->viewport.height) height = render_target->viewport.height - y; - if ((offset_x + width) > bitmap->mode_info.width) + if ((offset_x + width) > (int)bitmap->mode_info.width) width = bitmap->mode_info.width - offset_x; - if ((offset_y + height) > bitmap->mode_info.height) + if ((offset_y + height) > (int)bitmap->mode_info.height) height = bitmap->mode_info.height - offset_y; /* Limit drawing to source render target dimensions. */ - if (width > bitmap->mode_info.width) + if (width > (int)bitmap->mode_info.width) width = bitmap->mode_info.width; - if (height > bitmap->mode_info.height) + if (height > (int)bitmap->mode_info.height) height = bitmap->mode_info.height; /* Add viewport offset. */ @@ -844,27 +883,37 @@ grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height) + int width, int height) { struct grub_video_fbblit_info source_info; struct grub_video_fbblit_info target_info; + int src_x = offset_x; + int src_y = offset_y; + int source_width = source->mode_info.width; + int source_height = source->mode_info.height; + fb_tran_dim_back(&source_width, &source_height, source->mode_info.transform); + + /* Normalize source rectangle and shift target insert point accordingly. */ + grub_fb_norm_rect(&offset_x, &offset_y, &width, &height); + x += offset_x - src_x; + y += offset_y - src_y; /* Make sure there is something to do. */ if ((width == 0) || (height == 0)) return GRUB_ERR_NONE; - if ((x >= (int)render_target->viewport.width) || (x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((y >= (int)render_target->viewport.height) || (y + (int)height < 0)) - return GRUB_ERR_NONE; - if ((x + (int)source->mode_info.width) < 0) - return GRUB_ERR_NONE; - if ((y + (int)source->mode_info.height) < 0) - return GRUB_ERR_NONE; - if ((offset_x >= (int)source->mode_info.width) - || (offset_x + (int)width < 0)) - return GRUB_ERR_NONE; - if ((offset_y >= (int)source->mode_info.height) - || (offset_y + (int)height < 0)) + if ((x >= render_target->viewport.width) || (x + width < 0)) + return GRUB_ERR_NONE; + if ((y >= render_target->viewport.height) || (y + height < 0)) + return GRUB_ERR_NONE; + if ((x + source_width) < 0) + return GRUB_ERR_NONE; + if ((y + source_height) < 0) + return GRUB_ERR_NONE; + if ((offset_x >= source_width) + || (offset_x + width < 0)) + return GRUB_ERR_NONE; + if ((offset_y >= source_height) + || (offset_y + height < 0)) return GRUB_ERR_NONE; /* If we have negative coordinates, optimize drawing to minimum. */ @@ -902,17 +951,17 @@ if ((y + height) > render_target->viewport.height) height = render_target->viewport.height - y; - if ((offset_x + width) > source->mode_info.width) - width = source->mode_info.width - offset_x; - if ((offset_y + height) > source->mode_info.height) - height = source->mode_info.height - offset_y; + if ((offset_x + width) > source_width) + width = source_width - offset_x; + if ((offset_y + height) > source_height) + height = source_height - offset_y; /* Limit drawing to source render target dimensions. */ - if (width > source->mode_info.width) - width = source->mode_info.width; + if (width > source_width) + width = source_width; - if (height > source->mode_info.height) - height = source->mode_info.height; + if (height > source_height) + height = source_height; /* Add viewport offset. */ x += render_target->viewport.x; @@ -940,6 +989,7 @@ int src_y; int dst_x; int dst_y; + int t_width, t_height; /* 1. Check if we have something to do. */ if ((dx == 0) && (dy == 0)) @@ -970,9 +1020,16 @@ dst_y = render_target->viewport.y + dy; } + t_width = width; + t_height = height; + fb_tran_rect(&src_x, &src_y, &t_width, &t_height, &render_target->mode_info); + t_width = width; + t_height = height; + fb_tran_rect(&dst_x, &dst_y, &t_width, &t_height, &render_target->mode_info); + /* 2. Check if there is need to copy data. */ - if ((grub_abs (dx) < render_target->viewport.width) - && (grub_abs (dy) < render_target->viewport.height)) + if (((int)grub_abs (dx) < render_target->viewport.width) + && ((int)grub_abs (dy) < render_target->viewport.height)) { /* 3. Move data in render target. */ struct grub_video_fbblit_info target; @@ -983,18 +1040,25 @@ target.data = render_target->data; linedelta = target.mode_info->pitch - - width * target.mode_info->bytes_per_pixel; - linelen = width * target.mode_info->bytes_per_pixel; + - t_width * target.mode_info->bytes_per_pixel; + linelen = t_width * target.mode_info->bytes_per_pixel; + if (src_y < dst_y || (src_y == dst_y && src_x < dst_x)) + { + src_x += t_width - 1; + dst_x += t_width - 1; + src_y += t_height - 1; + dst_y += t_height - 1; + } #define DO_SCROLL \ /* Check vertical direction of the move. */ \ - if (dy < 0 || (dy == 0 && dx < 0)) \ + if (src_y > dst_y || (src_y == dst_y && src_x > dst_x)) \ { \ dst = (void *) grub_video_fb_get_video_ptr (&target, \ dst_x, dst_y); \ src = (void *) grub_video_fb_get_video_ptr (&target, \ src_x, src_y); \ /* 3a. Move data upwards. */ \ - for (j = 0; j < height; j++) \ + for (j = 0; j < t_height; j++) \ { \ for (i = 0; i < linelen; i++) \ *(dst++) = *(src++); \ @@ -1006,12 +1070,10 @@ { \ /* 3b. Move data downwards. */ \ dst = (void *) grub_video_fb_get_video_ptr (&target, \ - dst_x + width - 1, \ - dst_y + height - 1); \ + dst_x, dst_y); \ src = (void *) grub_video_fb_get_video_ptr (&target, \ - src_x + width - 1, \ - src_y + height - 1); \ - for (j = 0; j < height; j++) \ + src_x, src_y); \ + for (j = 0; j < t_height; j++) \ { \ for (i = 0; i < linelen; i++) \ *(dst--) = *(src--); \ @@ -1052,7 +1114,7 @@ { grub_uint8_t *src, *dst; DO_SCROLL - } + } } /* 4. Fill empty space with specified color. In this implementation @@ -1064,7 +1126,7 @@ grub_video_fb_fill_rect (color, 0, 0, render_target->viewport.width, dy); else if (dy < 0) { - if (render_target->viewport.height < grub_abs (dy)) + if (render_target->viewport.height < (int)grub_abs (dy)) dy = -render_target->viewport.height; grub_video_fb_fill_rect (color, 0, render_target->viewport.height + dy, @@ -1077,7 +1139,7 @@ dx, render_target->viewport.height); else if (dx < 0) { - if (render_target->viewport.width < grub_abs (dx)) + if (render_target->viewport.width < (int)grub_abs (dx)) dx = -render_target->viewport.width; grub_video_fb_fill_rect (color, render_target->viewport.width + dx, 0, @@ -1095,6 +1157,7 @@ { struct grub_video_fbrender_target *target; unsigned int size; + int transform = render_target->mode_info.transform; /* Validate arguments. */ if ((! result) @@ -1120,7 +1183,12 @@ target->viewport.width = width; target->viewport.height = height; + /* Set up the target so that it has the same direction as the current target */ + /* TODO: Implement other directions, too */ + fb_tran_dim(&width, &height, transform); + /* Setup render target format. */ + target->mode_info.transform = transform; target->mode_info.width = width; target->mode_info.height = height; target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB @@ -1185,10 +1253,11 @@ target->viewport.y = 0; target->viewport.width = mode_info->width; target->viewport.height = mode_info->height; + fb_tran_dim_back(&(target->viewport.width), &(target->viewport.height), mode_info->transform); /* Clear render target with black and maximum transparency. */ for (y = 0; y < mode_info->height; y++) - grub_memset (target->data + mode_info->pitch * y, 0, + grub_memset ((char *)target->data + mode_info->pitch * y, 0, mode_info->bytes_per_pixel * mode_info->width); /* Save result to caller. */ === modified file 'video/i386/pc/vbe.c' --- video/i386/pc/vbe.c 2010-02-03 00:24:07 +0000 +++ video/i386/pc/vbe.c 2010-02-17 23:14:11 +0000 @@ -368,7 +368,8 @@ status = grub_vbe_bios_set_mode (initial_vbe_mode, 0); if (status != GRUB_VBE_STATUS_OK) /* TODO: Decide, is this something we want to do. */ - return grub_errno; + return grub_error(GRUB_ERR_BAD_DEVICE, + "cannot reset to initial VBE mode %x", initial_vbe_mode); last_set_mode = initial_vbe_mode; /* TODO: Free any resources allocated by driver. */ @@ -561,7 +562,8 @@ static grub_err_t grub_video_vbe_setup (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask) + unsigned int mode_type, unsigned int mode_mask, + int transform) { grub_uint16_t *p; struct grub_vbe_mode_info_block vbe_mode_info; @@ -681,6 +683,8 @@ framebuffer.mode_info.reserved_mask_size = active_vbe_mode_info.rsvd_mask_size; framebuffer.mode_info.reserved_field_pos = active_vbe_mode_info.rsvd_field_position; + framebuffer.mode_info.transform = transform; + framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); /* Set up double buffering and targets. */ @@ -790,6 +794,7 @@ .set_palette = grub_video_vbe_set_palette, .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_fb_set_viewport, + .get_max_viewport = grub_video_fb_get_max_viewport, .get_viewport = grub_video_fb_get_viewport, .map_color = grub_video_fb_map_color, .map_rgb = grub_video_fb_map_rgb, === modified file 'video/sm712.c' --- video/sm712.c 2010-01-20 19:49:18 +0000 +++ video/sm712.c 2010-02-17 23:14:11 +0000 @@ -62,7 +62,8 @@ static grub_err_t grub_video_sm712_setup (unsigned int width, unsigned int height, - unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused)), + int transform) { int depth; grub_err_t err; @@ -108,6 +109,7 @@ } /* Fill mode info details. */ + framebuffer.mode_info.transform = transform; framebuffer.mode_info.width = 1024; framebuffer.mode_info.height = 600; framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; @@ -200,6 +202,7 @@ .set_palette = grub_video_sm712_set_palette, .get_palette = grub_video_fb_get_palette, .set_viewport = grub_video_fb_set_viewport, + .get_max_viewport = grub_video_fb_get_max_viewport, .get_viewport = grub_video_fb_get_viewport, .map_color = grub_video_fb_map_color, .map_rgb = grub_video_fb_map_rgb, === modified file 'video/video.c' --- video/video.c 2010-02-03 00:24:07 +0000 +++ video/video.c 2010-02-17 23:14:11 +0000 @@ -21,6 +21,7 @@ #include #include #include +#include /* The list of video adapters registered to system. */ static grub_video_adapter_t grub_video_adapter_list; @@ -233,10 +234,9 @@ return grub_video_adapter_active->get_palette (start, count, palette_data); } -/* Set viewport dimensions. */ +/* Set viewport. */ grub_err_t -grub_video_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height) +grub_video_set_viewport (int x, int y, int width, int height) { if (! grub_video_adapter_active) return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); @@ -244,10 +244,19 @@ return grub_video_adapter_active->set_viewport (x, y, width, height); } -/* Get viewport dimensions. */ -grub_err_t -grub_video_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height) +/* Get maximum viewport dimensions. */ +grub_err_t +grub_video_get_max_viewport (int *width, int *height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_max_viewport (width, height); +} + +/* Get viewport. */ +grub_err_t +grub_video_get_viewport (int *x, int *y, int *width, int *height) { if (! grub_video_adapter_active) return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); @@ -305,7 +314,7 @@ /* Fill rectangle using specified color. */ grub_err_t grub_video_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height) + int width, int height) { if (! grub_video_adapter_active) return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); @@ -318,7 +327,7 @@ grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height) + int width, int height) { if (! grub_video_adapter_active) return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); @@ -333,7 +342,7 @@ grub_video_blit_render_target (struct grub_video_render_target *target, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height) + int width, int height) { if (! grub_video_adapter_active) return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); @@ -409,11 +418,15 @@ /* Parse x[x]*/ static grub_err_t -parse_modespec (const char *current_mode, int *width, int *height, int *depth) +parse_modespec (const char *current_mode, int *width, int *height, int *depth, int* transform) { const char *value; const char *param = current_mode; + int flip = 0; + int mirror = 0; + int swap = 0; + *transform = 0; *width = *height = *depth = -1; if (grub_strcmp (param, "auto") == 0) @@ -422,6 +435,36 @@ return GRUB_ERR_NONE; } + /* TODO: document direction */ + /* Parse [Dir]x[x]*/ + for (;;param++) + switch (*param) { + case 'N': + case 'n': *transform = 0; + break; + case 'E': + case 'e': *transform = FB_TRAN_EAST; + break; + case 'S': + case 's': *transform = FB_TRAN_SOUTH; + break; + case 'W': + case 'w': *transform = FB_TRAN_WEST; + break; + case 'M': + case 'm': mirror = 1; break; + case 'F': + case 'f': flip = 1; break; + case 'X': + case 'x': swap = 1; break; + default: goto end_direction_parse_loop; + } +end_direction_parse_loop: + + if (mirror) *transform ^= FB_TRAN_MIRROR; + if (flip) *transform ^= FB_TRAN_FLIP; + if (swap) *transform ^= FB_TRAN_SWAP; + /* Find width value. */ value = param; param = grub_strchr(param, 'x'); @@ -553,6 +596,7 @@ int width = -1; int height = -1; int depth = -1; + int transform = 0; grub_err_t err; unsigned int flags = modevalue; unsigned int flagmask = modemask; @@ -600,7 +644,7 @@ } } - err = parse_modespec (current_mode, &width, &height, &depth); + err = parse_modespec (current_mode, &width, &height, &depth, &transform); if (err) { /* Free memory before returning. */ @@ -645,7 +689,7 @@ } /* Try to initialize video mode. */ - err = p->setup (width, height, flags, flagmask); + err = p->setup (width, height, flags, flagmask, transform); if (err != GRUB_ERR_NONE) { p->fini (); # Begin bundle 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 YqwPpeLkKXQhsZh+wXckU4UJBiER5UA=