diff --git a/commands/videotest.c b/commands/videotest.c index 8434b1f..b3d003a 100644 --- a/commands/videotest.c +++ b/commands/videotest.c @@ -24,6 +24,114 @@ #include #include #include +#include +#include /*for some reason render target type is defined there */ +#include + +char leaf_data[] = { 0x00, 0x0f, 0xe0, 0x00, + 0x00, 0x7f, 0xfc, 0x00, + 0x01, 0xff, 0xff, 0x00, + 0x03, 0xff, 0xff, 0x80, + + 0x07, 0xff, 0xff, 0xe0, + 0x0f, 0xff, 0xff, 0xf0, + 0x1f, 0xff, 0xff, 0xf0, + 0x3f, 0xff, 0xff, 0xf8, + + 0x3f, 0xff, 0xff, 0xf8, + 0x7f, 0xff, 0xff, 0xfc, + 0x7f, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xfe, + + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xfe, + + 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xfc, + 0xff, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xff, 0xf8, + + 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xe0, + 0xff, 0xff, 0xff, 0xc0, + 0xff, 0xff, 0xff, 0x80, + + 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xfc, 0x00, + 0xff, 0xff, 0xe0, 0x00, + 0xff, 0x00, 0x00, 0x00 }; +#define LEAF_SIZE 32 + +struct grub_video_bitmap leaves[4]; + +static void init_leaves(void) +{ + + 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; +} + +#define BORDER 20 + +static void draw_leaves(void) +{ + grub_font_t sans; + grub_video_color_t text_color, fill_color; + + sans = grub_font_get ("Helvetica Bold 14"); + text_color = grub_video_map_rgb (255, 255, 255); + fill_color = grub_video_map_rgba (0, 192, 0, 192); + + 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); + +} static grub_err_t grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), @@ -49,7 +157,49 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), const char *str; int texty; - 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_fb_get_active_render_target(&tgt); + + init_leaves(); + grub_video_get_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 (x, y, 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 (x, y, 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 @@ -115,7 +265,8 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), 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. */ diff --git a/font/font.c b/font/font.c index a812919..176443a 100644 --- a/font/font.c +++ b/font/font.c @@ -987,8 +987,10 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, 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; diff --git a/include/grub/fbblit.h b/include/grub/fbblit.h index af97dfb..3d63c9e 100644 --- a/include/grub/fbblit.h +++ b/include/grub/fbblit.h @@ -28,7 +28,7 @@ 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); void grub_video_fbblit_replace_directN (struct grub_video_fbblit_info *dst, @@ -94,7 +94,7 @@ void 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, diff --git a/include/grub/fbtran.h b/include/grub/fbtran.h new file mode 100644 index 0000000..407953d --- /dev/null +++ b/include/grub/fbtran.h @@ -0,0 +1,220 @@ +/* + * 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. + * MIRROR | swap image across (around) the vertical axis + * 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-negating. + * || = -- = // = 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 corretct + * picture when facing the east side of the screen we need the -\ transform. + * + * E = -rot90 = -\ + * S = -rot180 = -\-\ = -|\\ = -| + * W = -rot270 = -|-\ = |\ + * + * Forward transform is from user visible cordinates to framebuffer memory coordinates. + */ + +#define FB_TRAN_SWAP 1 +#define FB_TRAN_FLIP 2 +#define FB_TRAN_MIRROR 4 +#define FB_TRAN_MASK 7 +#define FB_TRAN_SIMPLE 6 +#define FB_TRAN_NORTH 0 +#define FB_TRAN_EAST (FB_TRAN_FLIP | FB_TRAN_SWAP) +#define FB_TRAN_SOUTH (FB_TRAN_FLIP | FB_TRAN_MIRROR) +#define FB_TRAN_WEST (FB_TRAN_MIRROR | FB_TRAN_SWAP) + +/* 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. Should work on modes that have the same width and height + * at the point the transfom is appended. In other words: don't use.*/ +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. */ +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. */ +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) + swap_int(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) + swap_unsigned(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)) + swap_int(&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) + swap_int(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) + { + swap_int(x, &x2); + (*x)++; + x2++; + } + if(*y > y2) + { + swap_int(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 */ diff --git a/include/grub/misc.h b/include/grub/misc.h index a63a0b4..3651046 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -212,6 +212,15 @@ grub_max (long x, long y) 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) diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h index 6a93ab0..c7ebe2f 100644 --- a/include/grub/util/misc.h +++ b/include/grub/util/misc.h @@ -39,6 +39,9 @@ extern char *progname; extern int verbosity; +static inline void swap_int(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } +static inline void swap_unsigned(unsigned *a, unsigned *b) { unsigned tmp = *a; *a = *b; *b = tmp; } + void grub_util_warn (const char *fmt, ...); void grub_util_info (const char *fmt, ...); void grub_util_error (const char *fmt, ...) __attribute__ ((noreturn)); diff --git a/include/grub/video.h b/include/grub/video.h index c24b44b..c07de38 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -149,6 +149,8 @@ struct grub_video_mode_info grub_uint8_t fg_green; grub_uint8_t fg_blue; grub_uint8_t fg_alpha; + + int transform; }; struct grub_video_palette_data @@ -171,7 +173,7 @@ struct grub_video_adapter grub_err_t (*fini) (void); grub_err_t (*setup) (unsigned int width, unsigned int height, - unsigned int mode_type); + unsigned int mode_type, int transform); grub_err_t (*get_info) (struct grub_video_mode_info *mode_info); diff --git a/term/gfxterm.c b/term/gfxterm.c index f161499..e5b35b7 100644 --- a/term/gfxterm.c +++ b/term/gfxterm.c @@ -105,6 +105,9 @@ static struct grub_virtual_screen virtual_screen; static struct grub_video_mode_info mode_info; +static int view_width; +static int view_height; + static struct grub_video_render_target *text_layer; static unsigned int bitmap_width; @@ -259,6 +262,7 @@ grub_gfxterm_init (void) grub_video_color_t color; int width; int height; + int view_x, view_y; grub_err_t err; /* Select the font to use. */ @@ -287,14 +291,15 @@ grub_gfxterm_init (void) if (err) return err; + grub_video_get_viewport(&view_x, &view_y, &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); bitmap = 0; /* Leave borders for virtual screen. */ - width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH); - height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH); + width = view_width - (2 * DEFAULT_BORDER_WIDTH); + height = view_height - (2 * DEFAULT_BORDER_WIDTH); /* Create virtual screen. */ if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH, @@ -306,7 +311,7 @@ grub_gfxterm_init (void) /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, view_width, view_height); return (grub_errno = GRUB_ERR_NONE); } @@ -814,7 +819,7 @@ grub_gfxterm_cls (void) /* Clear text layer. */ grub_video_set_active_render_target (text_layer); color = virtual_screen.bg_color; - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (color, 0, 0, view_width, view_height); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); /* Mark virtual screen to be redrawn. */ @@ -900,7 +905,7 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, view_width, view_height); } /* If filename was provided, try to load that. */ @@ -920,7 +925,7 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, view_width, view_height); } } diff --git a/video/bitmap.c b/video/bitmap.c index 7b135a5..88c7bfd 100644 --- a/video/bitmap.c +++ b/video/bitmap.c @@ -75,6 +75,7 @@ grub_video_bitmap_create (struct grub_video_bitmap **bitmap, mode_info->width = width; mode_info->height = height; mode_info->blit_format = blit_format; + mode_info->transform = 0; switch (blit_format) { diff --git a/video/fb/fbblit.c b/video/fb/fbblit.c index d9e04a5..44b6ca7 100644 --- a/video/fb/fbblit.c +++ b/video/fb/fbblit.c @@ -27,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; @@ -56,8 +59,11 @@ grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, 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); + } } } @@ -705,10 +711,12 @@ void 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++) { @@ -735,12 +743,17 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, 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); @@ -756,7 +769,10 @@ grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, 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); } } } diff --git a/video/fb/video_fb.c b/video/fb/video_fb.c index 2fb108e..84001b8 100644 --- a/video/fb/video_fb.c +++ b/video/fb/video_fb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include static struct grub_video_fbrender_target *render_target; @@ -122,22 +123,25 @@ grub_video_fb_set_viewport (int x, int y, int width, int height) grub_fb_norm_rect(&x, &y, &width, &height); /* Make sure viewport is withing screen dimensions. If viewport was set to be out of the region, mark its size as zero. */ - if (x > (int)render_target->mode_info.width) + int mode_width = render_target->mode_info.width; + int mode_height = render_target->mode_info.height; + fb_tran_dim_back(&mode_width, &mode_height, render_target->mode_info.transform); + if (x > mode_width) { x = 0; width = 0; } - if (y > (int)render_target->mode_info.height) + if (y > mode_height) { y = 0; height = 0; } - if (x + width > (int)render_target->mode_info.width) + if (x + width > mode_width) width = render_target->mode_info.width - x; - if (y + height > (int)render_target->mode_info.height) + if (y + height > mode_height) height = render_target->mode_info.height - y; render_target->viewport.x = x; @@ -434,6 +438,9 @@ grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, 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) @@ -488,8 +495,12 @@ common_blitter (struct grub_video_fbblit_info *target, 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) { @@ -618,13 +629,15 @@ common_blitter (struct grub_video_fbblit_info *target, 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) { @@ -740,10 +753,11 @@ common_blitter (struct grub_video_fbblit_info *target, } + } /* !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); } } @@ -855,6 +869,9 @@ grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, 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); @@ -868,14 +885,14 @@ grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, return GRUB_ERR_NONE; if ((y >= render_target->viewport.height) || (y + height < 0)) return GRUB_ERR_NONE; - if ((x + (int)source->mode_info.width) < 0) + if ((x + source_width) < 0) return GRUB_ERR_NONE; - if ((y + (int)source->mode_info.height) < 0) + if ((y + source_height) < 0) return GRUB_ERR_NONE; - if ((offset_x >= (int)source->mode_info.width) + if ((offset_x >= source_width) || (offset_x + width < 0)) return GRUB_ERR_NONE; - if ((offset_y >= (int)source->mode_info.height) + if ((offset_y >= source_height) || (offset_y + height < 0)) return GRUB_ERR_NONE; @@ -914,17 +931,17 @@ grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, if ((y + height) > render_target->viewport.height) height = render_target->viewport.height - y; - if ((offset_x + width) > (int)source->mode_info.width) - width = source->mode_info.width - offset_x; - if ((offset_y + height) > (int)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 > (int)source->mode_info.width) - width = source->mode_info.width; + if (width > source_width) + width = source_width; - if (height > (int)source->mode_info.height) - height = source->mode_info.height; + if (height > source_height) + height = source_height; /* Add viewport offset. */ x += render_target->viewport.x; @@ -952,6 +969,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) 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)) @@ -982,6 +1000,13 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) 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 (((int)grub_abs (dx) < render_target->viewport.width) && ((int)grub_abs (dy) < render_target->viewport.height)) @@ -996,23 +1021,23 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) target.data = render_target->data; /* Check vertical direction of the move. */ - if (dy <= 0) + if (dst_y <= src_y) /* 3a. Move data upwards. */ - for (j = 0; j < height; j++) + for (j = 0; j < t_height; j++) { dst = grub_video_fb_get_video_ptr (&target, dst_x, dst_y + j); src = grub_video_fb_get_video_ptr (&target, src_x, src_y + j); grub_memmove (dst, src, - width * target.mode_info->bytes_per_pixel); + t_width * target.mode_info->bytes_per_pixel); } else /* 3b. Move data downwards. */ - for (j = (height - 1); j >= 0; j--) + for (j = (t_height - 1); j >= 0; j--) { dst = grub_video_fb_get_video_ptr (&target, dst_x, dst_y + j); src = grub_video_fb_get_video_ptr (&target, src_x, src_y + j); grub_memmove (dst, src, - width * target.mode_info->bytes_per_pixel); + t_width * target.mode_info->bytes_per_pixel); } } @@ -1056,6 +1081,7 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, { struct grub_video_fbrender_target *target; unsigned int size; + int transform = render_target->mode_info.transform; /* Validate arguments. */ if ((! result) @@ -1081,7 +1107,12 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, 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 @@ -1146,10 +1177,11 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ 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. */ diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c index 3efcd56..b36d034 100644 --- a/video/i386/pc/vbe.c +++ b/video/i386/pc/vbe.c @@ -381,7 +381,7 @@ grub_video_vbe_fini (void) static grub_err_t grub_video_vbe_setup (unsigned int width, unsigned int height, - unsigned int mode_type) + unsigned int mode_type, int transform) { grub_uint16_t *p; struct grub_vbe_mode_info_block vbe_mode_info; @@ -499,6 +499,8 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); + framebuffer.mode_info.transform = transform; + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr); if (err) diff --git a/video/video.c b/video/video.c index 29d760f..5f7ba75 100644 --- a/video/video.c +++ b/video/video.c @@ -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; @@ -412,6 +413,10 @@ grub_video_set_mode (const char *modestring, int height = -1; int depth = -1; int flags = 0; + int transform = 0; + int flip = 0; + int mirror = 0; + int swap = 0; /* Take copy of env.var. as we don't want to modify that. */ modevar = grub_strdup (modestring); @@ -509,6 +514,10 @@ grub_video_set_mode (const char *modestring, current_mode = tmp; param = tmp; value = NULL; + transform = 0; + mirror = 0; + flip = 0; + swap = 0; /* XXX: we assume that we're in pure text mode if no video mode is initialized. Is it always true? */ @@ -532,7 +541,36 @@ grub_video_set_mode (const char *modestring, } } - /* Parse x[x]*/ + /* 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; @@ -668,7 +706,7 @@ grub_video_set_mode (const char *modestring, } /* Try to initialize video mode. */ - err = p->setup (width, height, flags); + err = p->setup (width, height, flags, transform); if (err != GRUB_ERR_NONE) { p->fini ();