Compare commits

...

2 Commits

Author SHA1 Message Date
Vladimir Serbinenko
e29466b0b5 WIP: rotate. 2017-02-09 02:25:12 +01:00
Vladimir Serbinenko
281b8b44ed video_fb: Fix blue collor if using unoptimized blitter.
when unmapping the color what matters is the mode of source, not target.
2017-02-09 02:22:24 +01:00
5 changed files with 332 additions and 141 deletions

View File

@@ -34,39 +34,37 @@
#include <grub/types.h>
#include <grub/video.h>
/* Generic replacing blitter (slow). Works for every supported format. */
static 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)
static inline grub_uint8_t
alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha)
{
int i;
int j;
grub_uint8_t src_red;
grub_uint8_t src_green;
grub_uint8_t src_blue;
grub_uint8_t src_alpha;
grub_video_color_t src_color;
grub_video_color_t dst_color;
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
src_color = get_pixel (src, i + offset_x, j + offset_y);
grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green,
&src_blue, &src_alpha);
dst_color = grub_video_fb_map_rgba (src_red, src_green,
src_blue, src_alpha);
set_pixel (dst, x + i, y + j, dst_color);
}
}
grub_uint16_t s;
grub_uint16_t h, l;
s = (fg * alpha) + (bg * (255 ^ alpha));
/* Optimised division by 255. */
h = s >> 8;
l = s & 0xff;
if (h + l >= 255)
h++;
return h;
}
#define SUFFIX(x) x
#define ADD_X 0
#define ADD_Y 0
#define TRANS_X(x, y) x
#define TRANS_Y(x, y) y
#include "fbblit_rot.c"
#define SUFFIX(x) x ## _90
#define TRANS_X(x, y) (y)
#define TRANS_Y(x, y) (-(x))
#include "fbblit_rot.c"
#define SUFFIX(x) x ## _270
#define TRANS_X(x, y) (-(y))
#define TRANS_Y(x, y) (x)
#include "fbblit_rot.c"
/* Block copy replacing blitter. Works with modes multiple of 8 bits. */
static void
grub_video_fbblit_replace_directN (struct grub_video_fbblit_info *dst,
@@ -1145,78 +1143,6 @@ grub_video_fbblit_replace_index_RGB888 (struct grub_video_fbblit_info *dst,
}
}
static inline grub_uint8_t
alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha)
{
grub_uint16_t s;
grub_uint16_t h, l;
s = (fg * alpha) + (bg * (255 ^ alpha));
/* Optimised division by 255. */
h = s >> 8;
l = s & 0xff;
if (h + l >= 255)
h++;
return h;
}
/* Generic blending blitter. Works for every supported format. */
static 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 i;
int j;
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
grub_uint8_t src_red;
grub_uint8_t src_green;
grub_uint8_t src_blue;
grub_uint8_t src_alpha;
grub_uint8_t dst_red;
grub_uint8_t dst_green;
grub_uint8_t dst_blue;
grub_uint8_t dst_alpha;
grub_video_color_t src_color;
grub_video_color_t dst_color;
src_color = get_pixel (src, i + offset_x, j + offset_y);
grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green,
&src_blue, &src_alpha);
if (src_alpha == 0)
continue;
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);
continue;
}
dst_color = get_pixel (dst, x + i, y + j);
grub_video_fb_unmap_color_int (dst, dst_color, &dst_red,
&dst_green, &dst_blue, &dst_alpha);
dst_red = alpha_dilute (dst_red, src_red, src_alpha);
dst_green = alpha_dilute (dst_green, src_green, src_alpha);
dst_blue = alpha_dilute (dst_blue, src_blue, src_alpha);
dst_alpha = src_alpha;
dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue,
dst_alpha);
set_pixel (dst, x + i, y + j, dst_color);
}
}
}
/* Optimized blending blitter for RGBA8888 to BGRA8888. */
static void
grub_video_fbblit_blend_BGRA8888_RGBA8888 (struct grub_video_fbblit_info *dst,
@@ -1936,6 +1862,45 @@ grub_video_fb_dispatch_blit (struct grub_video_fbblit_info *target,
unsigned int width, unsigned int height,
int offset_x, int offset_y)
{
if (target->mode_info->rotation == GRUB_VIDEO_ROTATE_90)
{
int nx = y;
int ny = target->mode_info->width - x;
if (oper == GRUB_VIDEO_BLIT_REPLACE)
{
/* No optimized replace operator found, use default (slow) blitter. */
grub_video_fbblit_replace_90 (target, source, nx, ny, width, height,
offset_x, offset_y);
return;
}
else
{
/* No optimized replace operator found, use default (slow) blitter. */
grub_video_fbblit_blend_90 (target, source, nx, ny, width, height,
offset_x, offset_y);
return;
}
}
if (target->mode_info->rotation == GRUB_VIDEO_ROTATE_270)
{
int nx = target->mode_info->height - y;
int ny = x;
if (oper == GRUB_VIDEO_BLIT_REPLACE)
{
/* No optimized replace operator found, use default (slow) blitter. */
grub_video_fbblit_replace_270 (target, source, nx, ny, width, height,
offset_x, offset_y);
return;
}
else
{
/* No optimized replace operator found, use default (slow) blitter. */
grub_video_fbblit_blend_270 (target, source, nx, ny, width, height,
offset_x, offset_y);
return;
}
}
if (oper == GRUB_VIDEO_BLIT_REPLACE)
{
/* Try to figure out more optimized version for replace operator. */

View File

@@ -0,0 +1,96 @@
/* Generic replacing blitter (slow). Works for every supported format. */
static void
SUFFIX(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 i;
int j;
grub_uint8_t src_red;
grub_uint8_t src_green;
grub_uint8_t src_blue;
grub_uint8_t src_alpha;
grub_video_color_t src_color;
grub_video_color_t dst_color;
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
src_color = get_pixel (src, i + offset_x, j + offset_y);
grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green,
&src_blue, &src_alpha);
dst_color = grub_video_fb_map_rgba (src_red, src_green,
src_blue, src_alpha);
set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color);
}
}
}
/* Generic blending blitter. Works for every supported format. */
static void
SUFFIX(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 i;
int j;
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
grub_uint8_t src_red;
grub_uint8_t src_green;
grub_uint8_t src_blue;
grub_uint8_t src_alpha;
grub_uint8_t dst_red;
grub_uint8_t dst_green;
grub_uint8_t dst_blue;
grub_uint8_t dst_alpha;
grub_video_color_t src_color;
grub_video_color_t dst_color;
src_color = get_pixel (src, i + offset_x, j + offset_y);
grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green,
&src_blue, &src_alpha);
if (src_alpha == 0)
continue;
if (src_alpha == 255)
{
dst_color = grub_video_fb_map_rgba (src_red, src_green,
src_blue, src_alpha);
set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color);
continue;
}
dst_color = get_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j));
grub_video_fb_unmap_color_int (dst, dst_color, &dst_red,
&dst_green, &dst_blue, &dst_alpha);
dst_red = alpha_dilute (dst_red, src_red, src_alpha);
dst_green = alpha_dilute (dst_green, src_green, src_alpha);
dst_blue = alpha_dilute (dst_blue, src_blue, src_alpha);
dst_alpha = src_alpha;
dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue,
dst_alpha);
set_pixel (dst, x + TRANS_X(i, j), y + TRANS_Y(i, j), dst_color);
}
}
}
#undef SUFFIX
#undef ADD_X
#undef ADD_Y
#undef TRANS_X
#undef TRANS_Y

View File

@@ -32,6 +32,7 @@
#include <grub/fbutil.h>
#include <grub/types.h>
#include <grub/video.h>
#include <grub/misc.h>
/* Generic filler that works for every supported mode. */
static void

View File

@@ -743,7 +743,7 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source,
if ((mode_info->mode_type
& GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
{
if ((framebuffer.render_target->mode_info.mode_type
if ((mode_info->mode_type
& GRUB_VIDEO_MODE_TYPE_ALPHA) != 0
&& color == 0xf0)
{
@@ -830,7 +830,7 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source,
}
static void
dirty (int y, int height)
dirty_untrans (int y, int height)
{
if (framebuffer.render_target != framebuffer.back_target)
return;
@@ -840,6 +840,66 @@ dirty (int y, int height)
framebuffer.current_dirty.last_line = y + height;
}
static void
dirty (int x, int width, int y, int height)
{
if (framebuffer.render_target != framebuffer.back_target)
return;
if (framebuffer.render_target->mode_info.rotation == GRUB_VIDEO_ROTATE_90
|| framebuffer.render_target->mode_info.rotation == GRUB_VIDEO_ROTATE_270)
{
if (framebuffer.current_dirty.first_line > x)
framebuffer.current_dirty.first_line = x;
if (framebuffer.current_dirty.last_line < x + width)
framebuffer.current_dirty.last_line = x + width;
}
else
{
if (framebuffer.current_dirty.first_line > y)
framebuffer.current_dirty.first_line = y;
if (framebuffer.current_dirty.last_line < y + height)
framebuffer.current_dirty.last_line = y + height;
}
}
static void
grub_video_fb_fill_rect_untrans (grub_video_color_t color, int x, int y,
unsigned int width, unsigned int height)
{
struct grub_video_fbblit_info target;
target.mode_info = &framebuffer.render_target->mode_info;
target.data = framebuffer.render_target->data;
grub_video_fb_fill_dispatch (&target, color, x, y,
width, height);
dirty_untrans (y, height);
}
static grub_video_rect_t
grub_video_transform_rectangle (grub_video_rect_t r, const struct grub_video_mode_info *mode_info)
{
grub_video_rect_t n;
switch (mode_info->rotation)
{
case GRUB_VIDEO_ROTATE_NONE:
return r;
case GRUB_VIDEO_ROTATE_90:
n.width = r.height;
n.height = r.width;
n.x = r.y;
n.y = mode_info->width - r.x - r.width;
return n;
case GRUB_VIDEO_ROTATE_270:
n.width = r.height;
n.height = r.width;
n.x = mode_info->height - r.y - r.height;
n.y = r.x;
return n;
}
return r;
}
grub_err_t
grub_video_fb_fill_rect (grub_video_color_t color, int x, int y,
unsigned int width, unsigned int height)
@@ -895,14 +955,22 @@ grub_video_fb_fill_rect (grub_video_color_t color, int x, int y,
x += area_x;
y += area_y;
dirty (y, height);
dirty (x, width, y, height);
/* Use fbblit_info to encapsulate rendering. */
target.mode_info = &framebuffer.render_target->mode_info;
target.data = framebuffer.render_target->data;
grub_video_fb_fill_dispatch (&target, color, x, y,
width, height);
grub_video_rect_t orig = {
.x = x,
.y = y,
.width = width,
.height = height
};
grub_video_rect_t tran = grub_video_transform_rectangle (orig, &framebuffer.render_target->mode_info);
grub_video_fb_fill_dispatch (&target, color, tran.x, tran.y,
tran.width, tran.height);
return GRUB_ERR_NONE;
}
@@ -1008,7 +1076,7 @@ grub_video_fb_blit_source (struct grub_video_fbblit_info *source,
target.data = framebuffer.render_target->data;
/* Do actual blitting. */
dirty (y, height);
dirty (x, width, y, height);
grub_video_fb_dispatch_blit (&target, source, oper, x, y, width, height,
offset_x, offset_y);
@@ -1052,42 +1120,70 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
int src_y;
int dst_x;
int dst_y;
grub_video_rect_t transformed_viewport;
/* 1. Check if we have something to do. */
if ((dx == 0) && (dy == 0))
return GRUB_ERR_NONE;
width = framebuffer.render_target->viewport.width - grub_abs (dx);
height = framebuffer.render_target->viewport.height - grub_abs (dy);
transformed_viewport = grub_video_transform_rectangle (framebuffer.render_target->viewport,
&framebuffer.render_target->mode_info);
dirty (framebuffer.render_target->viewport.y,
framebuffer.render_target->viewport.height);
dirty (transformed_viewport.x,
transformed_viewport.width,
transformed_viewport.y,
transformed_viewport.height);
switch (framebuffer.render_target->mode_info.rotation)
{
case GRUB_VIDEO_ROTATE_NONE:
break;
case GRUB_VIDEO_ROTATE_90:
{
int ndx = dy;
int ndy = -dx;
dx = ndx;
dy = ndy;
}
break;
case GRUB_VIDEO_ROTATE_270:
{
int ndx = -dy;
int ndy = dx;
dx = ndx;
dy = ndy;
}
break;
}
width = transformed_viewport.width - grub_abs (dx);
height = transformed_viewport.height - grub_abs (dy);
if (dx < 0)
{
src_x = framebuffer.render_target->viewport.x - dx;
dst_x = framebuffer.render_target->viewport.x;
src_x = transformed_viewport.x - dx;
dst_x = transformed_viewport.x;
}
else
{
src_x = framebuffer.render_target->viewport.x;
dst_x = framebuffer.render_target->viewport.x + dx;
src_x = transformed_viewport.x;
dst_x = transformed_viewport.x + dx;
}
if (dy < 0)
{
src_y = framebuffer.render_target->viewport.y - dy;
dst_y = framebuffer.render_target->viewport.y;
src_y = transformed_viewport.y - dy;
dst_y = transformed_viewport.y;
}
else
{
src_y = framebuffer.render_target->viewport.y;
dst_y = framebuffer.render_target->viewport.y + dy;
src_y = transformed_viewport.y;
dst_y = transformed_viewport.y + dy;
}
/* 2. Check if there is need to copy data. */
if ((grub_abs (dx) < framebuffer.render_target->viewport.width)
&& (grub_abs (dy) < framebuffer.render_target->viewport.height))
if ((grub_abs (dx) < transformed_viewport.width)
&& (grub_abs (dy) < transformed_viewport.height))
{
/* 3. Move data in render target. */
struct grub_video_fbblit_info target;
@@ -1109,7 +1205,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
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 < height; j++) \
{ \
for (i = 0; i < linelen; i++) \
*(dst++) = *(src++); \
@@ -1128,7 +1224,7 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
src_y + height - 1); \
dst--; \
src--; \
for (j = 0; j < height; j++) \
for (j = 0; j < height; j++) \
{ \
for (i = 0; i < linelen; i++) \
*(dst--) = *(src--); \
@@ -1178,27 +1274,27 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
/* 4a. Fill top & bottom parts. */
if (dy > 0)
grub_video_fb_fill_rect (color, 0, 0, framebuffer.render_target->viewport.width, dy);
grub_video_fb_fill_rect_untrans (color, 0, 0, transformed_viewport.width, dy);
else if (dy < 0)
{
if (framebuffer.render_target->viewport.height < grub_abs (dy))
dy = -framebuffer.render_target->viewport.height;
if (transformed_viewport.height < grub_abs (dy))
dy = -transformed_viewport.height;
grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy,
framebuffer.render_target->viewport.width, -dy);
grub_video_fb_fill_rect_untrans (color, 0, transformed_viewport.height + dy,
transformed_viewport.width, -dy);
}
/* 4b. Fill left & right parts. */
if (dx > 0)
grub_video_fb_fill_rect (color, 0, 0,
dx, framebuffer.render_target->viewport.height);
grub_video_fb_fill_rect_untrans (color, 0, 0,
dx, transformed_viewport.height);
else if (dx < 0)
{
if (framebuffer.render_target->viewport.width < grub_abs (dx))
dx = -framebuffer.render_target->viewport.width;
if (transformed_viewport.width < grub_abs (dx))
dx = -transformed_viewport.width;
grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0,
-dx, framebuffer.render_target->viewport.height);
grub_video_fb_fill_rect_untrans (color, transformed_viewport.width + dx, 0,
-dx, transformed_viewport.height);
}
return GRUB_ERR_NONE;
@@ -1228,6 +1324,8 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result,
/* TODO: Implement other types too.
Currently only 32bit render targets are supported. */
target->mode_info.rotation = GRUB_VIDEO_ROTATE_NONE;
/* Mark render target as allocated. */
target->is_allocated = 1;
@@ -1253,6 +1351,8 @@ grub_video_fb_create_render_target (struct grub_video_fbrender_target **result,
/* Setup render target format. */
target->mode_info.width = width;
target->mode_info.height = height;
target->mode_info.original_width = width;
target->mode_info.original_height = height;
switch (mode_type)
{
case GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
@@ -1336,6 +1436,17 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ
grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info));
target->mode_info.rotation = GRUB_VIDEO_ROTATE_270; ///!!!
target->mode_info.original_width = target->mode_info.width;
target->mode_info.original_height = target->mode_info.height;
if (target->mode_info.rotation == GRUB_VIDEO_ROTATE_90
|| target->mode_info.rotation == GRUB_VIDEO_ROTATE_270)
{
target->mode_info.width = target->mode_info.original_height;
target->mode_info.height = target->mode_info.original_width;
}
/* Reset viewport, region and area to match new mode. */
target->viewport.x = 0;
target->viewport.y = 0;
@@ -1356,9 +1467,9 @@ grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_targ
target->area_offset_y = 0;
/* Clear render target with black and maximum transparency. */
for (y = 0; y < mode_info->height; y++)
for (y = 0; y < target->mode_info.original_height; y++)
grub_memset (target->data + mode_info->pitch * y, 0,
mode_info->bytes_per_pixel * mode_info->width);
mode_info->bytes_per_pixel * target->mode_info.original_width);
/* Save result to caller. */
*result = target;
@@ -1427,7 +1538,7 @@ doublebuf_blit_update_screen (void)
* (framebuffer.current_dirty.last_line
- framebuffer.current_dirty.first_line));
framebuffer.current_dirty.first_line
= framebuffer.back_target->mode_info.height;
= framebuffer.back_target->mode_info.original_height;
framebuffer.current_dirty.last_line = 0;
return GRUB_ERR_NONE;
@@ -1636,7 +1747,7 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask,
framebuffer.render_page = 0;
framebuffer.set_page = 0;
framebuffer.current_dirty.first_line
= framebuffer.back_target->mode_info.height;
= framebuffer.back_target->mode_info.original_height;
framebuffer.current_dirty.last_line = 0;
mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
@@ -1668,6 +1779,9 @@ grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info,
grub_memcpy (mode_info, &(framebuffer.back_target->mode_info),
sizeof (*mode_info));
mode_info->width = framebuffer.back_target->mode_info.original_width;
mode_info->height = framebuffer.back_target->mode_info.original_height;
/* We are about to load a kernel. Switch back to page zero, since some
kernel drivers expect that. */
if (framebuffer.set_page && framebuffer.displayed_page != 0)

View File

@@ -75,6 +75,13 @@ typedef enum grub_video_mode_type
GRUB_VIDEO_MODE_TYPE_INFO_MASK = 0x00FF0000,
} grub_video_mode_type_t;
enum grub_video_rotation
{
GRUB_VIDEO_ROTATE_NONE,
GRUB_VIDEO_ROTATE_90,
GRUB_VIDEO_ROTATE_270,
};
/* The basic render target representing the whole display. This always
renders to the back buffer when double-buffering is in use. */
#define GRUB_VIDEO_RENDER_TARGET_DISPLAY \
@@ -122,12 +129,20 @@ enum grub_video_blit_operators
struct grub_video_mode_info
{
/* Width of the screen. */
/* Width of the screen, before the rotation. */
unsigned int original_width;
/* Height of the screen, before the rotation. */
unsigned int original_height;
/* Width of the screen, after the rotation. */
unsigned int width;
/* Height of the screen. */
/* Height of the screen, after the rotation. */
unsigned int height;
enum grub_video_rotation rotation;
/* Mode type bitmask. Contains information like is it Index color or
RGB mode. */
grub_video_mode_type_t mode_type;