automake commit without merge history

This commit is contained in:
BVK Chaitanya 2010-05-06 11:34:04 +05:30
parent 265d68cd10
commit 8c41176882
810 changed files with 4980 additions and 2508 deletions

253
grub-core/video/bitmap.c Normal file
View file

@ -0,0 +1,253 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/video.h>
#include <grub/bitmap.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/misc.h>
/* List of bitmap readers registered to system. */
static grub_video_bitmap_reader_t bitmap_readers_list;
/* Register bitmap reader. */
void
grub_video_bitmap_reader_register (grub_video_bitmap_reader_t reader)
{
reader->next = bitmap_readers_list;
bitmap_readers_list = reader;
}
/* Unregister bitmap reader. */
void
grub_video_bitmap_reader_unregister (grub_video_bitmap_reader_t reader)
{
grub_video_bitmap_reader_t *p, q;
for (p = &bitmap_readers_list, q = *p; q; p = &(q->next), q = q->next)
if (q == reader)
{
*p = q->next;
break;
}
}
/* Creates new bitmap, saves created bitmap on success to *bitmap. */
grub_err_t
grub_video_bitmap_create (struct grub_video_bitmap **bitmap,
unsigned int width, unsigned int height,
enum grub_video_blit_format blit_format)
{
struct grub_video_mode_info *mode_info;
unsigned int size;
if (!bitmap)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
*bitmap = 0;
if (width == 0 || height == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
*bitmap = (struct grub_video_bitmap *)grub_malloc (sizeof (struct grub_video_bitmap));
if (! *bitmap)
return grub_errno;
mode_info = &((*bitmap)->mode_info);
/* Populate mode_info. */
mode_info->width = width;
mode_info->height = height;
mode_info->blit_format = blit_format;
switch (blit_format)
{
case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888:
mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB
| GRUB_VIDEO_MODE_TYPE_ALPHA;
mode_info->bpp = 32;
mode_info->bytes_per_pixel = 4;
mode_info->number_of_colors = 256;
mode_info->red_mask_size = 8;
mode_info->red_field_pos = 0;
mode_info->green_mask_size = 8;
mode_info->green_field_pos = 8;
mode_info->blue_mask_size = 8;
mode_info->blue_field_pos = 16;
mode_info->reserved_mask_size = 8;
mode_info->reserved_field_pos = 24;
break;
case GRUB_VIDEO_BLIT_FORMAT_RGB_888:
mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
mode_info->bpp = 24;
mode_info->bytes_per_pixel = 3;
mode_info->number_of_colors = 256;
mode_info->red_mask_size = 8;
mode_info->red_field_pos = 0;
mode_info->green_mask_size = 8;
mode_info->green_field_pos = 8;
mode_info->blue_mask_size = 8;
mode_info->blue_field_pos = 16;
mode_info->reserved_mask_size = 0;
mode_info->reserved_field_pos = 0;
break;
case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR:
mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
mode_info->bpp = 8;
mode_info->bytes_per_pixel = 1;
mode_info->number_of_colors = 256;
mode_info->red_mask_size = 0;
mode_info->red_field_pos = 0;
mode_info->green_mask_size = 0;
mode_info->green_field_pos = 0;
mode_info->blue_mask_size = 0;
mode_info->blue_field_pos = 0;
mode_info->reserved_mask_size = 0;
mode_info->reserved_field_pos = 0;
break;
default:
grub_free (*bitmap);
*bitmap = 0;
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"unsupported bitmap format");
}
mode_info->pitch = width * mode_info->bytes_per_pixel;
/* Calculate size needed for the data. */
size = (width * mode_info->bytes_per_pixel) * height;
(*bitmap)->data = grub_zalloc (size);
if (! (*bitmap)->data)
{
grub_free (*bitmap);
*bitmap = 0;
return grub_errno;
}
return GRUB_ERR_NONE;
}
/* Frees all resources allocated by bitmap. */
grub_err_t
grub_video_bitmap_destroy (struct grub_video_bitmap *bitmap)
{
if (! bitmap)
return GRUB_ERR_NONE;
grub_free (bitmap->data);
grub_free (bitmap);
return GRUB_ERR_NONE;
}
/* Match extension to filename. */
static int
match_extension (const char *filename, const char *ext)
{
int pos;
int ext_len;
pos = grub_strlen (filename);
ext_len = grub_strlen (ext);
if (! pos || ! ext_len || ext_len > pos)
return 0;
pos -= ext_len;
return grub_strcmp (filename + pos, ext) == 0;
}
/* Loads bitmap using registered bitmap readers. */
grub_err_t
grub_video_bitmap_load (struct grub_video_bitmap **bitmap,
const char *filename)
{
grub_video_bitmap_reader_t reader = bitmap_readers_list;
if (!bitmap)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
*bitmap = 0;
while (reader)
{
if (match_extension (filename, reader->extension))
return reader->reader (bitmap, filename);
reader = reader->next;
}
return grub_error(GRUB_ERR_BAD_FILE_TYPE, "unsupported bitmap format");
}
/* Return bitmap width. */
unsigned int
grub_video_bitmap_get_width (struct grub_video_bitmap *bitmap)
{
if (!bitmap)
return 0;
return bitmap->mode_info.width;
}
/* Return bitmap height. */
unsigned int
grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap)
{
if (!bitmap)
return 0;
return bitmap->mode_info.height;
}
/* Return mode info for bitmap. */
void grub_video_bitmap_get_mode_info (struct grub_video_bitmap *bitmap,
struct grub_video_mode_info *mode_info)
{
if (!bitmap)
return;
*mode_info = bitmap->mode_info;
}
/* Return pointer to bitmap's raw data. */
void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap)
{
if (!bitmap)
return 0;
return bitmap->data;
}
/* Initialize bitmap module. */
GRUB_MOD_INIT(bitmap)
{
}
/* Finalize bitmap module. */
GRUB_MOD_FINI(bitmap)
{
}

View file

@ -0,0 +1,308 @@
/* bitmap_scale.c - Bitmap scaling. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/video.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/types.h>
/* Prototypes for module-local functions. */
static grub_err_t scale_nn (struct grub_video_bitmap *dst,
struct grub_video_bitmap *src);
static grub_err_t scale_bilinear (struct grub_video_bitmap *dst,
struct grub_video_bitmap *src);
/* This function creates a new scaled version of the bitmap SRC. The new
bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm
is given by SCALE_METHOD. If an error is encountered, the return code is
not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or
it is destroyed before this function returns.
Supports only direct color modes which have components separated
into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
But because of this simplifying assumption, the implementation is
greatly simplified. */
grub_err_t
grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst,
int dst_width, int dst_height,
struct grub_video_bitmap *src,
enum grub_video_bitmap_scale_method
scale_method)
{
*dst = 0;
/* Verify the simplifying assumptions. */
if (src == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"null src bitmap in grub_video_bitmap_create_scaled");
if (src->mode_info.red_field_pos % 8 != 0
|| src->mode_info.green_field_pos % 8 != 0
|| src->mode_info.blue_field_pos % 8 != 0
|| src->mode_info.reserved_field_pos % 8 != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"src format not supported for scale");
if (src->mode_info.width == 0 || src->mode_info.height == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"source bitmap has a zero dimension");
if (dst_width <= 0 || dst_height <= 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"requested to scale to a size w/ a zero dimension");
if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"bitmap to scale has inconsistent Bpp and bpp");
/* Create the new bitmap. */
grub_err_t ret;
ret = grub_video_bitmap_create (dst, dst_width, dst_height,
src->mode_info.blit_format);
if (ret != GRUB_ERR_NONE)
return ret; /* Error. */
switch (scale_method)
{
case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST:
case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST:
ret = scale_nn (*dst, src);
break;
case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST:
case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR:
ret = scale_bilinear (*dst, src);
break;
default:
ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value");
break;
}
if (ret == GRUB_ERR_NONE)
{
/* Success: *dst is now a pointer to the scaled bitmap. */
return GRUB_ERR_NONE;
}
else
{
/* Destroy the bitmap and return the error code. */
grub_video_bitmap_destroy (*dst);
*dst = 0;
return ret;
}
}
/* Nearest neighbor bitmap scaling algorithm.
Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
dimensions of DST. This function uses the nearest neighbor algorithm to
interpolate the pixels.
Supports only direct color modes which have components separated
into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
But because of this simplifying assumption, the implementation is
greatly simplified. */
static grub_err_t
scale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
{
/* Verify the simplifying assumptions. */
if (dst == 0 || src == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn");
if (dst->mode_info.red_field_pos % 8 != 0
|| dst->mode_info.green_field_pos % 8 != 0
|| dst->mode_info.blue_field_pos % 8 != 0
|| dst->mode_info.reserved_field_pos % 8 != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
if (src->mode_info.red_field_pos % 8 != 0
|| src->mode_info.green_field_pos % 8 != 0
|| src->mode_info.blue_field_pos % 8 != 0
|| src->mode_info.reserved_field_pos % 8 != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
|| dst->mode_info.red_mask_size != src->mode_info.red_mask_size
|| dst->mode_info.green_field_pos != src->mode_info.green_field_pos
|| dst->mode_info.green_mask_size != src->mode_info.green_mask_size
|| dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
|| dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
|| dst->mode_info.reserved_field_pos !=
src->mode_info.reserved_field_pos
|| dst->mode_info.reserved_mask_size !=
src->mode_info.reserved_mask_size)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
if (dst->mode_info.width == 0 || dst->mode_info.height == 0
|| src->mode_info.width == 0 || src->mode_info.height == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
grub_uint8_t *ddata = dst->data;
grub_uint8_t *sdata = src->data;
int dw = dst->mode_info.width;
int dh = dst->mode_info.height;
int sw = src->mode_info.width;
int sh = src->mode_info.height;
int dstride = dst->mode_info.pitch;
int sstride = src->mode_info.pitch;
/* bytes_per_pixel is the same for both src and dst. */
int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
int dy;
for (dy = 0; dy < dh; dy++)
{
int dx;
for (dx = 0; dx < dw; dx++)
{
grub_uint8_t *dptr;
grub_uint8_t *sptr;
int sx;
int sy;
int comp;
/* Compute the source coordinate that the destination coordinate
maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
sx = sw * dx / dw;
sy = sh * dy / dh;
/* Get the address of the pixels in src and dst. */
dptr = ddata + dy * dstride + dx * bytes_per_pixel;
sptr = sdata + sy * sstride + sx * bytes_per_pixel;
/* Copy the pixel color value. */
for (comp = 0; comp < bytes_per_pixel; comp++)
dptr[comp] = sptr[comp];
}
}
return GRUB_ERR_NONE;
}
/* Bilinear interpolation image scaling algorithm.
Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
dimensions of DST. This function uses the bilinear interpolation algorithm
to interpolate the pixels.
Supports only direct color modes which have components separated
into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
But because of this simplifying assumption, the implementation is
greatly simplified. */
static grub_err_t
scale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src)
{
/* Verify the simplifying assumptions. */
if (dst == 0 || src == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func");
if (dst->mode_info.red_field_pos % 8 != 0
|| dst->mode_info.green_field_pos % 8 != 0
|| dst->mode_info.blue_field_pos % 8 != 0
|| dst->mode_info.reserved_field_pos % 8 != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported");
if (src->mode_info.red_field_pos % 8 != 0
|| src->mode_info.green_field_pos % 8 != 0
|| src->mode_info.blue_field_pos % 8 != 0
|| src->mode_info.reserved_field_pos % 8 != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported");
if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos
|| dst->mode_info.red_mask_size != src->mode_info.red_mask_size
|| dst->mode_info.green_field_pos != src->mode_info.green_field_pos
|| dst->mode_info.green_mask_size != src->mode_info.green_mask_size
|| dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos
|| dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size
|| dst->mode_info.reserved_field_pos !=
src->mode_info.reserved_field_pos
|| dst->mode_info.reserved_mask_size !=
src->mode_info.reserved_mask_size)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible");
if (dst->mode_info.width == 0 || dst->mode_info.height == 0
|| src->mode_info.width == 0 || src->mode_info.height == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension");
grub_uint8_t *ddata = dst->data;
grub_uint8_t *sdata = src->data;
int dw = dst->mode_info.width;
int dh = dst->mode_info.height;
int sw = src->mode_info.width;
int sh = src->mode_info.height;
int dstride = dst->mode_info.pitch;
int sstride = src->mode_info.pitch;
/* bytes_per_pixel is the same for both src and dst. */
int bytes_per_pixel = dst->mode_info.bytes_per_pixel;
int dy;
for (dy = 0; dy < dh; dy++)
{
int dx;
for (dx = 0; dx < dw; dx++)
{
grub_uint8_t *dptr;
grub_uint8_t *sptr;
int sx;
int sy;
int comp;
/* Compute the source coordinate that the destination coordinate
maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
sx = sw * dx / dw;
sy = sh * dy / dh;
/* Get the address of the pixels in src and dst. */
dptr = ddata + dy * dstride + dx * bytes_per_pixel;
sptr = sdata + sy * sstride + sx * bytes_per_pixel;
/* If we have enough space to do so, use bilinear interpolation.
Otherwise, fall back to nearest neighbor for this pixel. */
if (sx < sw - 1 && sy < sh - 1)
{
/* Do bilinear interpolation. */
/* Fixed-point .8 numbers representing the fraction of the
distance in the x (u) and y (v) direction within the
box of 4 pixels in the source. */
int u = (256 * sw * dx / dw) - (sx * 256);
int v = (256 * sh * dy / dh) - (sy * 256);
for (comp = 0; comp < bytes_per_pixel; comp++)
{
/* Get the component's values for the
four source corner pixels. */
grub_uint8_t f00 = sptr[comp];
grub_uint8_t f10 = sptr[comp + bytes_per_pixel];
grub_uint8_t f01 = sptr[comp + sstride];
grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel];
/* Do linear interpolations along the top and bottom
rows of the box. */
grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256;
grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256;
/* Interpolate vertically. */
grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256;
dptr[comp] = fxy;
}
}
else
{
/* Fall back to nearest neighbor interpolation. */
/* Copy the pixel color value. */
for (comp = 0; comp < bytes_per_pixel; comp++)
dptr[comp] = sptr[comp];
}
}
}
return GRUB_ERR_NONE;
}

399
grub-core/video/efi_gop.c Normal file
View file

@ -0,0 +1,399 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
#include <grub/video_fb.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/graphics_output.h>
static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
static struct grub_efi_gop *gop;
static unsigned old_mode;
static int restore_needed;
static struct
{
struct grub_video_mode_info mode_info;
struct grub_video_render_target *render_target;
grub_uint8_t *ptr;
} framebuffer;
static int
check_protocol (void)
{
gop = grub_efi_locate_protocol (&graphics_output_guid, 0);
if (gop)
return 1;
return 0;
}
static grub_err_t
grub_video_gop_init (void)
{
grub_memset (&framebuffer, 0, sizeof(framebuffer));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_gop_fini (void)
{
if (restore_needed)
{
efi_call_2 (gop->set_mode, gop, old_mode);
restore_needed = 0;
}
return grub_video_fb_fini ();
}
static int
grub_video_gop_get_bpp (struct grub_efi_gop_mode_info *in)
{
grub_uint32_t total_mask;
int i;
switch (in->pixel_format)
{
case GRUB_EFI_GOT_BGRA8:
case GRUB_EFI_GOT_RGBA8:
return 32;
case GRUB_EFI_GOT_BITMASK:
/* Check overlaps. */
if ((in->pixel_bitmask.r & in->pixel_bitmask.g)
|| (in->pixel_bitmask.r & in->pixel_bitmask.b)
|| (in->pixel_bitmask.g & in->pixel_bitmask.b)
|| (in->pixel_bitmask.r & in->pixel_bitmask.a)
|| (in->pixel_bitmask.g & in->pixel_bitmask.a)
|| (in->pixel_bitmask.b & in->pixel_bitmask.a))
return 0;
total_mask = in->pixel_bitmask.r | in->pixel_bitmask.g
| in->pixel_bitmask.b | in->pixel_bitmask.a;
for (i = 31; i >= 0; i--)
if (total_mask & (1 << i))
return i + 1;
/* Fall through. */
default:
return 0;
}
}
static void
grub_video_gop_get_bitmask (grub_uint32_t mask, unsigned int *mask_size,
unsigned int *field_pos)
{
int i;
int last_p;
for (i = 31; i >= 0; i--)
if (mask & (1 << i))
break;
if (i == -1)
{
*mask_size = *field_pos = 0;
return;
}
last_p = i;
for (; i >= 0; i--)
if (!(mask & (1 << i)))
break;
*field_pos = i + 1;
*mask_size = last_p - *field_pos + 1;
}
static grub_err_t
grub_video_gop_fill_mode_info (struct grub_efi_gop_mode_info *in,
struct grub_video_mode_info *out)
{
out->number_of_colors = 256;
out->width = in->width;
out->height = in->height;
out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
out->bpp = grub_video_gop_get_bpp (in);
out->bytes_per_pixel = out->bpp >> 3;
if (!out->bpp)
return grub_error (GRUB_ERR_IO, "unsupported video mode");
out->pitch = in->pixels_per_scanline * out->bytes_per_pixel;
switch (in->pixel_format)
{
case GRUB_EFI_GOT_RGBA8:
out->red_mask_size = 8;
out->red_field_pos = 0;
out->green_mask_size = 8;
out->green_field_pos = 8;
out->blue_mask_size = 8;
out->blue_field_pos = 16;
out->reserved_mask_size = 8;
out->reserved_field_pos = 24;
break;
case GRUB_EFI_GOT_BGRA8:
out->red_mask_size = 8;
out->red_field_pos = 16;
out->green_mask_size = 8;
out->green_field_pos = 8;
out->blue_mask_size = 8;
out->blue_field_pos = 0;
out->reserved_mask_size = 8;
out->reserved_field_pos = 24;
break;
case GRUB_EFI_GOT_BITMASK:
grub_video_gop_get_bitmask (in->pixel_bitmask.r, &out->red_mask_size,
&out->red_field_pos);
grub_video_gop_get_bitmask (in->pixel_bitmask.g, &out->green_mask_size,
&out->green_field_pos);
grub_video_gop_get_bitmask (in->pixel_bitmask.b, &out->blue_mask_size,
&out->blue_field_pos);
grub_video_gop_get_bitmask (in->pixel_bitmask.a, &out->reserved_mask_size,
&out->reserved_field_pos);
break;
default:
return grub_error (GRUB_ERR_IO, "unsupported video mode");
}
out->blit_format = grub_video_get_blit_format (out);
return GRUB_ERR_NONE;
}
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 depth;
struct grub_efi_gop_mode_info *info = NULL;
unsigned best_mode = 0;
grub_err_t err;
unsigned bpp;
int found = 0;
unsigned long long best_volume = 0;
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
/* Keep current mode if possible. */
if (gop->mode->info)
{
bpp = grub_video_gop_get_bpp (gop->mode->info);
if (bpp && ((width == gop->mode->info->width
&& height == gop->mode->info->height)
|| (width == 0 && height == 0))
&& (depth == bpp || depth == 0))
{
grub_dprintf ("video", "GOP: keeping mode %d\n", gop->mode->mode);
best_mode = gop->mode->mode;
found = 1;
}
}
if (!found)
{
unsigned mode;
grub_dprintf ("video", "GOP: %d modes detected\n", gop->mode->max_mode);
for (mode = 0; mode < gop->mode->max_mode; mode++)
{
grub_efi_uintn_t size;
grub_efi_status_t status;
status = efi_call_4 (gop->query_mode, gop, mode, &size, &info);
if (status)
{
info = 0;
break;
}
grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width,
info->height);
bpp = grub_video_gop_get_bpp (info);
if (!bpp)
{
grub_dprintf ("video", "GOP: mode %d: incompatible pixel mode\n",
mode);
continue;
}
grub_dprintf ("video", "GOP: mode %d: depth %d\n", mode, bpp);
if (!(((info->width == width && info->height == height)
|| (width == 0 && height == 0))
&& (bpp == depth || depth == 0)))
{
grub_dprintf ("video", "GOP: mode %d: rejected\n", mode);
continue;
}
if (best_volume < ((unsigned long long) info->width)
* ((unsigned long long) info->height)
* ((unsigned long long) bpp))
{
best_volume = ((unsigned long long) info->width)
* ((unsigned long long) info->height)
* ((unsigned long long) bpp);
best_mode = mode;
}
found = 1;
}
}
if (!found)
{
grub_dprintf ("video", "GOP: no mode found\n");
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
}
if (best_mode != gop->mode->mode)
{
if (!restore_needed)
{
old_mode = gop->mode->mode;
restore_needed = 1;
}
efi_call_2 (gop->set_mode, gop, best_mode);
}
info = gop->mode->info;
err = grub_video_gop_fill_mode_info (info, &framebuffer.mode_info);
if (err)
{
grub_dprintf ("video", "GOP: couldn't fill mode info\n");
return err;
}
framebuffer.ptr = (void *) (grub_addr_t) gop->mode->fb_base;
grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n",
framebuffer.ptr, framebuffer.mode_info.width,
framebuffer.mode_info.height, framebuffer.mode_info.bpp);
err = grub_video_fb_create_render_target_from_pointer
(&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr);
if (err)
{
grub_dprintf ("video", "GOP: Couldn't create FB target\n");
return err;
}
err = grub_video_fb_set_active_render_target (framebuffer.render_target);
if (err)
{
grub_dprintf ("video", "GOP: Couldn't set FB target\n");
return err;
}
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
if (err)
grub_dprintf ("video", "GOP: Couldn't set palette\n");
else
grub_dprintf ("video", "GOP: Success\n");
return err;
}
static grub_err_t
grub_video_gop_swap_buffers (void)
{
/* TODO: Implement buffer swapping. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_gop_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
target = framebuffer.render_target;
return grub_video_fb_set_active_render_target (target);
}
static grub_err_t
grub_video_gop_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuf)
{
grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
*framebuf = (char *) framebuffer.ptr;
grub_video_fb_fini ();
return GRUB_ERR_NONE;
}
static struct grub_video_adapter grub_video_gop_adapter =
{
.name = "EFI GOP driver",
.id = GRUB_VIDEO_DRIVER_EFI_GOP,
.init = grub_video_gop_init,
.fini = grub_video_gop_fini,
.setup = grub_video_gop_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_gop_get_info_and_fini,
.set_palette = grub_video_fb_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_gop_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_gop_set_active_render_target,
.get_active_render_target = grub_video_fb_get_active_render_target,
.next = 0
};
GRUB_MOD_INIT(efi_gop)
{
if (check_protocol ())
grub_video_register (&grub_video_gop_adapter);
}
GRUB_MOD_FINI(efi_gop)
{
if (restore_needed)
{
efi_call_2 (gop->set_mode, gop, old_mode);
restore_needed = 0;
}
if (gop)
grub_video_unregister (&grub_video_gop_adapter);
}

339
grub-core/video/efi_uga.c Normal file
View file

@ -0,0 +1,339 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
#include <grub/video_fb.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/uga_draw.h>
#include <grub/pci.h>
static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID;
static struct grub_efi_uga_draw_protocol *uga;
static grub_uint32_t uga_fb;
static grub_uint32_t uga_pitch;
static struct
{
struct grub_video_mode_info mode_info;
struct grub_video_render_target *render_target;
grub_uint8_t *ptr;
} framebuffer;
#define RGB_MASK 0xffffff
#define RGB_MAGIC 0x121314
#define LINE_MIN 800
#define LINE_MAX 4096
#define FBTEST_STEP (0x10000 >> 2)
#define FBTEST_COUNT 8
static int
find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len)
{
grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base;
int i;
for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP)
{
if ((*base & RGB_MASK) == RGB_MAGIC)
{
int j;
for (j = LINE_MIN; j <= LINE_MAX; j++)
{
if ((base[j] & RGB_MASK) == RGB_MAGIC)
{
*fb_base = (grub_uint32_t) (grub_target_addr_t) base;
*line_len = j << 2;
return 1;
}
}
break;
}
}
return 0;
}
static int
find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len)
{
int found = 0;
auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev,
grub_pci_id_t pciid);
int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev,
grub_pci_id_t pciid)
{
grub_pci_address_t addr;
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
if (grub_pci_read (addr) >> 24 == 0x3)
{
int i;
grub_dprintf ("fb", "Display controller: %d:%d.%d\nDevice id: %x\n",
grub_pci_get_bus (dev), grub_pci_get_device (dev),
grub_pci_get_function (dev), pciid);
addr += 8;
for (i = 0; i < 6; i++, addr += 4)
{
grub_uint32_t old_bar1, old_bar2, type;
grub_uint64_t base64;
old_bar1 = grub_pci_read (addr);
if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO))
continue;
type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK;
if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
{
if (i == 5)
break;
old_bar2 = grub_pci_read (addr + 4);
}
else
old_bar2 = 0;
base64 = old_bar2;
base64 <<= 32;
base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK);
grub_dprintf ("fb", "%s(%d): 0x%llx\n",
((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ?
"VMEM" : "MMIO"), i,
(unsigned long long) base64);
if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! found))
{
*fb_base = base64;
if (find_line_len (fb_base, line_len))
found++;
}
if (type == GRUB_PCI_ADDR_MEM_TYPE_64)
{
i++;
addr += 4;
}
}
}
return found;
}
grub_pci_iterate (find_card);
return found;
}
static int
check_protocol (void)
{
grub_efi_uga_draw_protocol_t *c;
c = grub_efi_locate_protocol (&uga_draw_guid, 0);
if (c)
{
grub_uint32_t width, height, depth, rate, pixel;
int ret;
if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate))
return 0;
grub_efi_set_text_mode (0);
pixel = RGB_MAGIC;
efi_call_10 (c->blt, c, (struct grub_efi_uga_pixel *) &pixel,
GRUB_EFI_UGA_VIDEO_FILL, 0, 0, 0, 0, 1, height, 0);
ret = find_framebuf (&uga_fb, &uga_pitch);
grub_efi_set_text_mode (1);
if (ret)
{
uga = c;
return 1;
}
}
return 0;
}
static grub_err_t
grub_video_uga_init (void)
{
grub_memset (&framebuffer, 0, sizeof(framebuffer));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_uga_fini (void)
{
return grub_video_fb_fini ();
}
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 depth;
int found = 0;
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
{
grub_uint32_t w;
grub_uint32_t h;
grub_uint32_t d;
grub_uint32_t r;
if ((! efi_call_5 (uga->get_mode, uga, &w, &h, &d, &r)) &&
((! width) || (width == w)) &&
((! height) || (height == h)) &&
((! depth) || (depth == d)))
{
framebuffer.mode_info.width = w;
framebuffer.mode_info.height = h;
framebuffer.mode_info.pitch = uga_pitch;
framebuffer.ptr = (grub_uint8_t *) (grub_target_addr_t) uga_fb;
found = 1;
}
}
if (found)
{
grub_err_t err;
framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
framebuffer.mode_info.bpp = 32;
framebuffer.mode_info.bytes_per_pixel = 4;
framebuffer.mode_info.number_of_colors = 256; /* TODO: fix me. */
framebuffer.mode_info.red_mask_size = 8;
framebuffer.mode_info.red_field_pos = 16;
framebuffer.mode_info.green_mask_size = 8;
framebuffer.mode_info.green_field_pos = 8;
framebuffer.mode_info.blue_mask_size = 8;
framebuffer.mode_info.blue_field_pos = 0;
framebuffer.mode_info.reserved_mask_size = 8;
framebuffer.mode_info.reserved_field_pos = 24;
framebuffer.mode_info.blit_format =
grub_video_get_blit_format (&framebuffer.mode_info);
err = grub_video_fb_create_render_target_from_pointer
(&framebuffer.render_target,
&framebuffer.mode_info,
framebuffer.ptr);
if (err)
return err;
err = grub_video_fb_set_active_render_target
(framebuffer.render_target);
if (err)
return err;
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
return err;
}
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
}
static grub_err_t
grub_video_uga_swap_buffers (void)
{
/* TODO: Implement buffer swapping. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_uga_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
target = framebuffer.render_target;
return grub_video_fb_set_active_render_target (target);
}
static grub_err_t
grub_video_uga_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuf)
{
grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
*framebuf = (char *) framebuffer.ptr;
grub_video_fb_fini ();
return GRUB_ERR_NONE;
}
static struct grub_video_adapter grub_video_uga_adapter =
{
.name = "EFI UGA driver",
.id = GRUB_VIDEO_DRIVER_EFI_UGA,
.init = grub_video_uga_init,
.fini = grub_video_uga_fini,
.setup = grub_video_uga_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_uga_get_info_and_fini,
.set_palette = grub_video_fb_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_uga_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_uga_set_active_render_target,
.get_active_render_target = grub_video_fb_get_active_render_target,
};
GRUB_MOD_INIT(efi_uga)
{
if (check_protocol ())
grub_video_register (&grub_video_uga_adapter);
}
GRUB_MOD_FINI(efi_uga)
{
if (uga)
grub_video_unregister (&grub_video_uga_adapter);
}

237
grub-core/video/emu/sdl.c Normal file
View file

@ -0,0 +1,237 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
#include <grub/video_fb.h>
#include <SDL/SDL.h>
static SDL_Surface *window = 0;
static struct grub_video_render_target *sdl_render_target;
static struct grub_video_mode_info mode_info;
static grub_err_t
grub_video_sdl_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data);
static grub_err_t
grub_video_sdl_init (void)
{
window = 0;
if (SDL_Init (SDL_INIT_VIDEO) < 0)
return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't init SDL: %s",
SDL_GetError ());
grub_memset (&mode_info, 0, sizeof (mode_info));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_sdl_fini (void)
{
SDL_Quit ();
window = 0;
grub_memset (&mode_info, 0, sizeof (mode_info));
return grub_video_fb_fini ();
}
static inline unsigned int
get_mask_size (grub_uint32_t mask)
{
unsigned i;
for (i = 0; mask > 1U << i; i++);
return i;
}
static grub_err_t
grub_video_sdl_setup (unsigned int width, unsigned int height,
unsigned int mode_type, unsigned int mode_mask)
{
int depth;
int flags = 0;
grub_err_t err;
/* Decode depth from mode_type. If it is zero, then autodetect. */
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
if (depth == 0)
depth = 32;
if (width == 0 && height == 0)
{
width = 800;
height = 600;
}
if ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)
|| !(mode_mask & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED))
flags |= SDL_DOUBLEBUF;
window = SDL_SetVideoMode (width, height, depth, flags | SDL_HWSURFACE);
if (! window)
window = SDL_SetVideoMode (width, height, depth, flags | SDL_SWSURFACE);
if (! window)
return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't open window: %s",
SDL_GetError ());
grub_memset (&sdl_render_target, 0, sizeof (sdl_render_target));
mode_info.width = window->w;
mode_info.height = window->h;
mode_info.mode_type = 0;
if (window->flags & SDL_DOUBLEBUF)
mode_info.mode_type
|= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
if (window->format->palette)
mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
else
mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_RGB;
mode_info.bpp = window->format->BitsPerPixel;
mode_info.bytes_per_pixel = window->format->BytesPerPixel;
mode_info.pitch = window->pitch;
/* In index color mode, number of colors. In RGB mode this is 256. */
if (window->format->palette)
mode_info.number_of_colors
= 1 << window->format->BitsPerPixel;
else
mode_info.number_of_colors = 256;
if (! window->format->palette)
{
mode_info.red_mask_size
= get_mask_size (window->format->Rmask >> window->format->Rshift);
mode_info.red_field_pos = window->format->Rshift;
mode_info.green_mask_size
= get_mask_size (window->format->Gmask >> window->format->Gshift);
mode_info.green_field_pos = window->format->Gshift;
mode_info.blue_mask_size
= get_mask_size (window->format->Bmask >> window->format->Bshift);
mode_info.blue_field_pos = window->format->Bshift;
mode_info.reserved_mask_size
= get_mask_size (window->format->Amask >> window->format->Ashift);
mode_info.reserved_field_pos = window->format->Ashift;
mode_info.blit_format
= grub_video_get_blit_format (&mode_info);
}
err = grub_video_fb_create_render_target_from_pointer (&sdl_render_target,
&mode_info,
window->pixels);
if (err)
return err;
/* Copy default palette to initialize emulated palette. */
grub_video_sdl_set_palette (0, (sizeof (grub_video_fbstd_colors)
/ sizeof (grub_video_fbstd_colors[0])),
grub_video_fbstd_colors);
/* Reset render target to SDL one. */
return grub_video_fb_set_active_render_target (sdl_render_target);
}
static grub_err_t
grub_video_sdl_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
unsigned i;
if (window->format->palette)
{
SDL_Color *tmp = grub_malloc (count * sizeof (tmp[0]));
for (i = 0; i < count; i++)
{
tmp[i].r = palette_data[i].r;
tmp[i].g = palette_data[i].g;
tmp[i].b = palette_data[i].b;
tmp[i].unused = palette_data[i].a;
}
SDL_SetColors (window, tmp, start, count);
grub_free (tmp);
}
return grub_video_fb_set_palette (start, count, palette_data);
}
static grub_err_t
grub_video_sdl_swap_buffers (void)
{
if (SDL_Flip (window) < 0)
return grub_error (GRUB_ERR_BAD_DEVICE, "couldn't swap buffers: %s",
SDL_GetError ());
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_sdl_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
return grub_video_fb_set_active_render_target (sdl_render_target);
return grub_video_fb_set_active_render_target (target);
}
static struct grub_video_adapter grub_video_sdl_adapter =
{
.name = "SDL Video Driver",
.init = grub_video_sdl_init,
.fini = grub_video_sdl_fini,
.setup = grub_video_sdl_setup,
.get_info = grub_video_fb_get_info,
.set_palette = grub_video_sdl_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_sdl_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_sdl_set_active_render_target,
.get_active_render_target = grub_video_fb_get_active_render_target,
.next = 0
};
GRUB_MOD_INIT(sdl)
{
grub_video_register (&grub_video_sdl_adapter);
}
GRUB_MOD_FINI(sdl)
{
grub_video_unregister (&grub_video_sdl_adapter);
}

1420
grub-core/video/fb/fbblit.c Normal file

File diff suppressed because it is too large Load diff

177
grub-core/video/fb/fbfill.c Normal file
View file

@ -0,0 +1,177 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/* 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.
- Every function in this code assumes that bounds checking has been done in
previous phase and they are opted out in here. */
#include <grub/video_fb.h>
#include <grub/fbfill.h>
#include <grub/fbutil.h>
#include <grub/types.h>
#include <grub/video.h>
/* Generic filler that works for every supported mode. */
void
grub_video_fbfill (struct grub_video_fbblit_info *dst,
grub_video_color_t color, int x, int y,
int width, int height)
{
int i;
int j;
for (j = 0; j < height; j++)
for (i = 0; i < width; i++)
set_pixel (dst, x + i, y + j, color);
}
/* Optimized filler for direct color 32 bit modes. It is assumed that color
is already mapped to destination format. */
void
grub_video_fbfill_direct32 (struct grub_video_fbblit_info *dst,
grub_video_color_t color, int x, int y,
int width, int height)
{
int i;
int j;
grub_uint32_t *dstptr;
grub_size_t rowskip;
/* Calculate the number of bytes to advance from the end of one line
to the beginning of the next line. */
rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width;
/* Get the start address. */
dstptr = (grub_uint32_t *) grub_video_fb_get_video_ptr (dst, x, y);
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
*dstptr++ = color;
/* Advance the dest pointer to the right location on the next line. */
dstptr = (grub_uint32_t *) (((char *) dstptr) + rowskip);
}
}
/* Optimized filler for direct color 24 bit modes. It is assumed that color
is already mapped to destination format. */
void
grub_video_fbfill_direct24 (struct grub_video_fbblit_info *dst,
grub_video_color_t color, int x, int y,
int width, int height)
{
int i;
int j;
grub_size_t rowskip;
grub_uint8_t *dstptr;
grub_uint8_t fill0 = (grub_uint8_t)((color >> 0) & 0xFF);
grub_uint8_t fill1 = (grub_uint8_t)((color >> 8) & 0xFF);
grub_uint8_t fill2 = (grub_uint8_t)((color >> 16) & 0xFF);
/* Calculate the number of bytes to advance from the end of one line
to the beginning of the next line. */
rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width;
/* Get the start address. */
dstptr = (grub_uint8_t *) grub_video_fb_get_video_ptr (dst, x, y);
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
*dstptr++ = fill0;
*dstptr++ = fill1;
*dstptr++ = fill2;
}
/* Advance the dest pointer to the right location on the next line. */
dstptr += rowskip;
}
}
/* Optimized filler for direct color 16 bit modes. It is assumed that color
is already mapped to destination format. */
void
grub_video_fbfill_direct16 (struct grub_video_fbblit_info *dst,
grub_video_color_t color, int x, int y,
int width, int height)
{
int i;
int j;
grub_size_t rowskip;
grub_uint8_t *dstptr;
grub_uint8_t fill0 = (grub_uint8_t)((color >> 0) & 0xFF);
grub_uint8_t fill1 = (grub_uint8_t)((color >> 8) & 0xFF);
/* Calculate the number of bytes to advance from the end of one line
to the beginning of the next line. */
rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width;
/* Get the start address. */
dstptr = (grub_uint8_t *) grub_video_fb_get_video_ptr (dst, x, y);
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
*dstptr++ = fill0;
*dstptr++ = fill1;
}
/* Advance the dest pointer to the right location on the next line. */
dstptr += rowskip;
}
}
/* Optimized filler for index color. It is assumed that color
is already mapped to destination format. */
void
grub_video_fbfill_direct8 (struct grub_video_fbblit_info *dst,
grub_video_color_t color, int x, int y,
int width, int height)
{
int i;
int j;
grub_size_t rowskip;
grub_uint8_t *dstptr;
grub_uint8_t fill = (grub_uint8_t)color & 0xFF;
/* Calculate the number of bytes to advance from the end of one line
to the beginning of the next line. */
rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width;
/* Get the start address. */
dstptr = (grub_uint8_t *) grub_video_fb_get_video_ptr (dst, x, y);
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
*dstptr++ = fill;
/* Advance the dest pointer to the right location on the next line. */
dstptr += rowskip;
}
}

178
grub-core/video/fb/fbutil.c Normal file
View file

@ -0,0 +1,178 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,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 <http://www.gnu.org/licenses/>.
*/
/* 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.
- Every function in this code assumes that bounds checking has been done in
previous phase and they are opted out in here. */
#include <grub/fbutil.h>
#include <grub/types.h>
#include <grub/video.h>
grub_uint8_t *
grub_video_fb_get_video_ptr (struct grub_video_fbblit_info *source,
unsigned int x, unsigned int y)
{
grub_uint8_t *ptr = 0;
switch (source->mode_info->bpp)
{
case 32:
ptr = source->data + y * source->mode_info->pitch + x * 4;
break;
case 24:
ptr = source->data + y * source->mode_info->pitch + x * 3;
break;
case 16:
case 15:
ptr = source->data + y * source->mode_info->pitch + x * 2;
break;
case 8:
ptr = source->data + y * source->mode_info->pitch + x;
break;
case 1:
/* For 1-bit bitmaps, addressing needs to be done at the bit level
and it doesn't make sense, in general, to ask for a pointer
to a particular pixel's data. */
break;
}
return ptr;
}
grub_video_color_t
get_pixel (struct grub_video_fbblit_info *source,
unsigned int x, unsigned int y)
{
grub_video_color_t color = 0;
switch (source->mode_info->bpp)
{
case 32:
color = *(grub_uint32_t *)grub_video_fb_get_video_ptr (source, x, y);
break;
case 24:
{
grub_uint8_t *ptr;
ptr = grub_video_fb_get_video_ptr (source, x, y);
color = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
}
break;
case 16:
case 15:
color = *(grub_uint16_t *)grub_video_fb_get_video_ptr (source, x, y);
break;
case 8:
color = *(grub_uint8_t *)grub_video_fb_get_video_ptr (source, x, y);
break;
case 1:
if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
{
int bit_index = y * source->mode_info->width + x;
grub_uint8_t *ptr = source->data + bit_index / 8;
int bit_pos = 7 - bit_index % 8;
color = (*ptr >> bit_pos) & 0x01;
}
break;
default:
break;
}
return color;
}
void
set_pixel (struct grub_video_fbblit_info *source,
unsigned int x, unsigned int y, grub_video_color_t color)
{
switch (source->mode_info->bpp)
{
case 32:
{
grub_uint32_t *ptr;
ptr = (grub_uint32_t *)grub_video_fb_get_video_ptr (source, x, y);
*ptr = color;
}
break;
case 24:
{
grub_uint8_t *ptr;
grub_uint8_t *colorptr = (grub_uint8_t *)&color;
ptr = grub_video_fb_get_video_ptr (source, x, y);
ptr[0] = colorptr[0];
ptr[1] = colorptr[1];
ptr[2] = colorptr[2];
}
break;
case 16:
case 15:
{
grub_uint16_t *ptr;
ptr = (grub_uint16_t *)grub_video_fb_get_video_ptr (source, x, y);
*ptr = (grub_uint16_t) (color & 0xFFFF);
}
break;
case 8:
{
grub_uint8_t *ptr;
ptr = (grub_uint8_t *)grub_video_fb_get_video_ptr (source, x, y);
*ptr = (grub_uint8_t) (color & 0xFF);
}
break;
case 1:
if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
{
int bit_index = y * source->mode_info->width + x;
grub_uint8_t *ptr = source->data + bit_index / 8;
int bit_pos = 7 - bit_index % 8;
*ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos);
}
break;
default:
break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,820 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/machine/memory.h>
#include <grub/machine/vga.h>
#include <grub/machine/vbe.h>
#include <grub/video_fb.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
static int vbe_detected = -1;
static struct grub_vbe_info_block controller_info;
static struct grub_vbe_mode_info_block active_vbe_mode_info;
/* Track last mode to support cards which fail on get_mode. */
static grub_uint32_t last_set_mode = 3;
static struct
{
struct grub_video_mode_info mode_info;
struct grub_video_render_target *front_target;
struct grub_video_render_target *back_target;
unsigned int bytes_per_scan_line;
unsigned int bytes_per_pixel;
grub_uint32_t active_vbe_mode;
grub_uint8_t *ptr;
int index_color_mode;
char *offscreen_buffer;
grub_size_t page_size; /* The size of a page in bytes. */
/* For page flipping strategy. */
int displayed_page; /* The page # that is the front buffer. */
int render_page; /* The page # that is the back buffer. */
/* Virtual functions. */
grub_video_fb_doublebuf_update_screen_t update_screen;
} framebuffer;
static grub_uint32_t initial_vbe_mode;
static grub_uint16_t *vbe_mode_list;
static void *
real2pm (grub_vbe_farptr_t ptr)
{
return (void *) ((((unsigned long) ptr & 0xFFFF0000) >> 12UL)
+ ((unsigned long) ptr & 0x0000FFFF));
}
grub_err_t
grub_vbe_probe (struct grub_vbe_info_block *info_block)
{
struct grub_vbe_info_block *vbe_ib;
grub_vbe_status_t status;
/* Clear caller's controller info block. */
if (info_block)
grub_memset (info_block, 0, sizeof (*info_block));
/* Do not probe more than one time, if not necessary. */
if (vbe_detected == -1 || info_block)
{
/* Clear old copy of controller info block. */
grub_memset (&controller_info, 0, sizeof (controller_info));
/* Mark VESA BIOS extension as undetected. */
vbe_detected = 0;
/* Use low memory scratch area as temporary storage
for VESA BIOS call. */
vbe_ib = (struct grub_vbe_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
/* Prepare info block. */
grub_memset (vbe_ib, 0, sizeof (*vbe_ib));
vbe_ib->signature[0] = 'V';
vbe_ib->signature[1] = 'B';
vbe_ib->signature[2] = 'E';
vbe_ib->signature[3] = '2';
/* Try to get controller info block. */
status = grub_vbe_bios_get_controller_info (vbe_ib);
if (status == GRUB_VBE_STATUS_OK)
{
/* Copy it for later usage. */
grub_memcpy (&controller_info, vbe_ib, sizeof (controller_info));
/* Mark VESA BIOS extension as detected. */
vbe_detected = 1;
}
}
if (! vbe_detected)
return grub_error (GRUB_ERR_BAD_DEVICE, "VESA BIOS Extension not found");
/* Make copy of controller info block to caller. */
if (info_block)
grub_memcpy (info_block, &controller_info, sizeof (*info_block));
return GRUB_ERR_NONE;
}
grub_err_t
grub_vbe_set_video_mode (grub_uint32_t vbe_mode,
struct grub_vbe_mode_info_block *vbe_mode_info)
{
grub_vbe_status_t status;
grub_uint32_t old_vbe_mode;
struct grub_vbe_mode_info_block new_vbe_mode_info;
grub_err_t err;
/* Make sure that VBE is supported. */
grub_vbe_probe (0);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* Try to get mode info. */
grub_vbe_get_video_mode_info (vbe_mode, &new_vbe_mode_info);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* For all VESA BIOS modes, force linear frame buffer. */
if (vbe_mode >= 0x100)
{
/* We only want linear frame buffer modes. */
vbe_mode |= 1 << 14;
/* Determine frame buffer pixel format. */
switch (new_vbe_mode_info.memory_model)
{
case GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL:
framebuffer.index_color_mode = 1;
break;
case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR:
framebuffer.index_color_mode = 0;
break;
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unsupported pixel format 0x%x",
new_vbe_mode_info.memory_model);
}
}
/* Get current mode. */
grub_vbe_get_video_mode (&old_vbe_mode);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* Try to set video mode. */
status = grub_vbe_bios_set_mode (vbe_mode, 0);
if (status != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot set VBE mode %x", vbe_mode);
last_set_mode = vbe_mode;
/* Save information for later usage. */
framebuffer.active_vbe_mode = vbe_mode;
grub_memcpy (&active_vbe_mode_info, &new_vbe_mode_info, sizeof (active_vbe_mode_info));
if (vbe_mode < 0x100)
{
/* If this is not a VESA mode, guess address. */
framebuffer.ptr = (grub_uint8_t *) GRUB_MEMORY_MACHINE_VGA_ADDR;
framebuffer.index_color_mode = 1;
}
else
{
framebuffer.ptr = (grub_uint8_t *) new_vbe_mode_info.phys_base_addr;
if (controller_info.version >= 0x300)
framebuffer.bytes_per_scan_line = new_vbe_mode_info.lin_bytes_per_scan_line;
else
framebuffer.bytes_per_scan_line = new_vbe_mode_info.bytes_per_scan_line;
}
/* Check whether mode is text mode or graphics mode. */
if (new_vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
{
/* Text mode. */
/* No special action needed for text mode as it is not supported for
graphical support. */
}
else
{
/* Graphics mode. */
/* Calculate bytes_per_pixel value. */
switch(new_vbe_mode_info.bits_per_pixel)
{
case 32: framebuffer.bytes_per_pixel = 4; break;
case 24: framebuffer.bytes_per_pixel = 3; break;
case 16: framebuffer.bytes_per_pixel = 2; break;
case 15: framebuffer.bytes_per_pixel = 2; break;
case 8: framebuffer.bytes_per_pixel = 1; break;
default:
grub_vbe_bios_set_mode (old_vbe_mode, 0);
last_set_mode = old_vbe_mode;
return grub_error (GRUB_ERR_BAD_DEVICE,
"cannot set VBE mode %x",
vbe_mode);
break;
}
/* If video mode is in indexed color, setup default VGA palette. */
if (framebuffer.index_color_mode)
{
struct grub_vbe_palette_data *palette
= (struct grub_vbe_palette_data *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
unsigned i;
/* Make sure that the BIOS can reach the palette. */
for (i = 0; i < GRUB_VIDEO_FBSTD_NUMCOLORS; i++)
{
palette[i].red = grub_video_fbstd_colors[i].r;
palette[i].green = grub_video_fbstd_colors[i].g;
palette[i].blue = grub_video_fbstd_colors[i].b;
palette[i].alignment = 0;
}
status = grub_vbe_bios_set_palette_data (GRUB_VIDEO_FBSTD_NUMCOLORS,
0, palette);
/* Just ignore the status. */
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
if (err)
return err;
}
}
/* Copy mode info for caller. */
if (vbe_mode_info)
grub_memcpy (vbe_mode_info, &new_vbe_mode_info, sizeof (*vbe_mode_info));
return GRUB_ERR_NONE;
}
grub_err_t
grub_vbe_get_video_mode (grub_uint32_t *mode)
{
grub_vbe_status_t status;
/* Make sure that VBE is supported. */
grub_vbe_probe (0);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* Try to query current mode from VESA BIOS. */
status = grub_vbe_bios_get_mode (mode);
/* XXX: ATI cards don't support get_mode. */
if (status != GRUB_VBE_STATUS_OK)
*mode = last_set_mode;
return GRUB_ERR_NONE;
}
grub_err_t
grub_vbe_get_video_mode_info (grub_uint32_t mode,
struct grub_vbe_mode_info_block *mode_info)
{
struct grub_vbe_mode_info_block *mi_tmp
= (struct grub_vbe_mode_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
grub_vbe_status_t status;
/* Make sure that VBE is supported. */
grub_vbe_probe (0);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* If mode is not VESA mode, skip mode info query. */
if (mode >= 0x100)
{
/* Try to get mode info from VESA BIOS. */
status = grub_vbe_bios_get_mode_info (mode, mi_tmp);
if (status != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_BAD_DEVICE,
"cannot get information on the mode %x", mode);
/* Make copy of mode info block. */
grub_memcpy (mode_info, mi_tmp, sizeof (*mode_info));
}
else
/* Just clear mode info block if it isn't a VESA mode. */
grub_memset (mode_info, 0, sizeof (*mode_info));
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_vbe_init (void)
{
grub_uint16_t *rm_vbe_mode_list;
grub_uint16_t *p;
grub_size_t vbe_mode_list_size;
struct grub_vbe_info_block info_block;
/* Check if there is adapter present.
Firmware note: There has been a report that some cards store video mode
list in temporary memory. So we must first use vbe probe to get
refreshed information to receive valid pointers and data, and then
copy this information to somewhere safe. */
grub_vbe_probe (&info_block);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* Copy modelist to local memory. */
p = rm_vbe_mode_list = real2pm (info_block.video_mode_ptr);
while(*p++ != 0xFFFF)
;
vbe_mode_list_size = (grub_addr_t) p - (grub_addr_t) rm_vbe_mode_list;
vbe_mode_list = grub_malloc (vbe_mode_list_size);
if (! vbe_mode_list)
return grub_errno;
grub_memcpy (vbe_mode_list, rm_vbe_mode_list, vbe_mode_list_size);
/* Adapter could be found, figure out initial video mode. */
grub_vbe_get_video_mode (&initial_vbe_mode);
if (grub_errno != GRUB_ERR_NONE)
{
/* Free allocated resources. */
grub_free (vbe_mode_list);
vbe_mode_list = NULL;
return grub_errno;
}
/* Reset frame buffer. */
grub_memset (&framebuffer, 0, sizeof(framebuffer));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_vbe_fini (void)
{
grub_vbe_status_t status;
grub_err_t err;
/* Restore old video mode. */
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;
last_set_mode = initial_vbe_mode;
/* TODO: Free any resources allocated by driver. */
grub_free (vbe_mode_list);
vbe_mode_list = NULL;
err = grub_video_fb_fini ();
grub_free (framebuffer.offscreen_buffer);
return err;
}
/*
Set framebuffer render target page and display the proper page, based on
`doublebuf_state.render_page' and `doublebuf_state.displayed_page',
respectively.
*/
static grub_err_t
doublebuf_pageflipping_commit (void)
{
/* Tell the video adapter to display the new front page. */
int display_start_line
= framebuffer.mode_info.height * framebuffer.displayed_page;
grub_vbe_status_t vbe_err =
grub_vbe_bios_set_display_start (0, display_start_line);
if (vbe_err != GRUB_VBE_STATUS_OK)
return grub_error (GRUB_ERR_IO, "couldn't commit pageflip");
return 0;
}
static grub_err_t
doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front
__attribute__ ((unused)),
struct grub_video_fbrender_target *back
__attribute__ ((unused)))
{
int new_displayed_page;
struct grub_video_fbrender_target *target;
grub_err_t err;
/* Swap the page numbers in the framebuffer struct. */
new_displayed_page = framebuffer.render_page;
framebuffer.render_page = framebuffer.displayed_page;
framebuffer.displayed_page = new_displayed_page;
err = doublebuf_pageflipping_commit ();
if (err)
{
/* Restore previous state. */
framebuffer.render_page = framebuffer.displayed_page;
framebuffer.displayed_page = new_displayed_page;
return err;
}
if (framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP)
grub_memcpy (framebuffer.ptr + framebuffer.render_page
* framebuffer.page_size, framebuffer.ptr
+ framebuffer.displayed_page * framebuffer.page_size,
framebuffer.page_size);
target = framebuffer.back_target;
framebuffer.back_target = framebuffer.front_target;
framebuffer.front_target = target;
err = grub_video_fb_get_active_render_target (&target);
if (err)
return err;
if (target == framebuffer.back_target)
err = grub_video_fb_set_active_render_target (framebuffer.front_target);
else if (target == framebuffer.front_target)
err = grub_video_fb_set_active_render_target (framebuffer.back_target);
return err;
}
static grub_err_t
doublebuf_pageflipping_init (void)
{
/* Get video RAM size in bytes. */
grub_size_t vram_size = controller_info.total_memory << 16;
grub_err_t err;
framebuffer.page_size =
framebuffer.mode_info.pitch * framebuffer.mode_info.height;
if (2 * framebuffer.page_size > vram_size)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"Not enough video memory for double buffering.");
framebuffer.displayed_page = 0;
framebuffer.render_page = 1;
framebuffer.update_screen = doublebuf_pageflipping_update_screen;
err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, &framebuffer.mode_info, framebuffer.ptr);
if (err)
return err;
err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, &framebuffer.mode_info, framebuffer.ptr + framebuffer.page_size);
if (err)
{
grub_video_fb_delete_render_target (framebuffer.front_target);
return err;
}
/* Set the framebuffer memory data pointer and display the right page. */
err = doublebuf_pageflipping_commit ();
if (err)
{
grub_video_fb_delete_render_target (framebuffer.front_target);
grub_video_fb_delete_render_target (framebuffer.back_target);
return err;
}
return GRUB_ERR_NONE;
}
/* Select the best double buffering mode available. */
static grub_err_t
double_buffering_init (unsigned int mode_type, unsigned int mode_mask)
{
grub_err_t err;
int updating_swap_needed;
updating_swap_needed
= grub_video_check_mode_flag (mode_type, mode_mask,
GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP, 0);
/* Do double buffering only if it's either requested or efficient. */
if (grub_video_check_mode_flag (mode_type, mode_mask,
GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
!updating_swap_needed))
{
framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
if (updating_swap_needed)
framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP;
err = doublebuf_pageflipping_init ();
if (!err)
return GRUB_ERR_NONE;
framebuffer.mode_info.mode_type
&= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
| GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
grub_errno = GRUB_ERR_NONE;
}
if (grub_video_check_mode_flag (mode_type, mode_mask,
GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
0))
{
framebuffer.mode_info.mode_type
|= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
| GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
err = grub_video_fb_doublebuf_blit_init (&framebuffer.front_target,
&framebuffer.back_target,
&framebuffer.update_screen,
framebuffer.mode_info,
framebuffer.ptr);
if (!err)
return GRUB_ERR_NONE;
framebuffer.mode_info.mode_type
&= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
| GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
grub_errno = GRUB_ERR_NONE;
}
/* Fall back to no double buffering. */
err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, &framebuffer.mode_info, framebuffer.ptr);
if (err)
return err;
framebuffer.back_target = framebuffer.front_target;
framebuffer.update_screen = 0;
framebuffer.mode_info.mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_vbe_setup (unsigned int width, unsigned int height,
unsigned int mode_type, unsigned int mode_mask)
{
grub_uint16_t *p;
struct grub_vbe_mode_info_block vbe_mode_info;
struct grub_vbe_mode_info_block best_vbe_mode_info;
grub_uint32_t best_vbe_mode = 0;
int depth;
/* Decode depth from mode_type. If it is zero, then autodetect. */
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
/* Walk thru mode list and try to find matching mode. */
for (p = vbe_mode_list; *p != 0xFFFF; p++)
{
grub_uint32_t vbe_mode = *p;
grub_vbe_get_video_mode_info (vbe_mode, &vbe_mode_info);
if (grub_errno != GRUB_ERR_NONE)
{
/* Could not retrieve mode info, retreat. */
grub_errno = GRUB_ERR_NONE;
break;
}
if ((vbe_mode_info.mode_attributes & 0x001) == 0)
/* If not available, skip it. */
continue;
if ((vbe_mode_info.mode_attributes & 0x008) == 0)
/* Monochrome is unusable. */
continue;
if ((vbe_mode_info.mode_attributes & 0x080) == 0)
/* We support only linear frame buffer modes. */
continue;
if ((vbe_mode_info.mode_attributes & 0x010) == 0)
/* We allow only graphical modes. */
continue;
if ((vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL)
&& (vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR))
/* Not compatible memory model. */
continue;
if (((vbe_mode_info.x_resolution != width)
|| (vbe_mode_info.y_resolution != height)) && width != 0 && height != 0)
/* Non matching resolution. */
continue;
/* Check if user requested RGB or index color mode. */
if ((mode_mask & GRUB_VIDEO_MODE_TYPE_COLOR_MASK) != 0)
{
unsigned my_mode_type = 0;
if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL)
my_mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR)
my_mode_type |= GRUB_VIDEO_MODE_TYPE_RGB;
if ((my_mode_type & mode_mask
& (GRUB_VIDEO_MODE_TYPE_RGB | GRUB_VIDEO_MODE_TYPE_INDEX_COLOR))
!= (mode_type & mode_mask
& (GRUB_VIDEO_MODE_TYPE_RGB
| GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)))
continue;
}
/* If there is a request for specific depth, ignore others. */
if ((depth != 0) && (vbe_mode_info.bits_per_pixel != depth))
continue;
/* Select mode with most of "volume" (size of framebuffer in bits). */
if (best_vbe_mode != 0)
if ((grub_uint64_t) vbe_mode_info.bits_per_pixel
* vbe_mode_info.x_resolution * vbe_mode_info.y_resolution
< (grub_uint64_t) best_vbe_mode_info.bits_per_pixel
* best_vbe_mode_info.x_resolution * best_vbe_mode_info.y_resolution)
continue;
/* Save so far best mode information for later use. */
best_vbe_mode = vbe_mode;
grub_memcpy (&best_vbe_mode_info, &vbe_mode_info, sizeof (vbe_mode_info));
}
/* Try to initialize best mode found. */
if (best_vbe_mode != 0)
{
grub_err_t err;
/* If this fails, then we have mode selection heuristics problem,
or adapter failure. */
/* grub_vbe_set_video_mode already sets active_vbe_mode_info. */
grub_vbe_set_video_mode (best_vbe_mode, NULL);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
/* Fill mode info details. */
framebuffer.mode_info.width = active_vbe_mode_info.x_resolution;
framebuffer.mode_info.height = active_vbe_mode_info.y_resolution;
if (framebuffer.index_color_mode)
framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
else
framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
framebuffer.mode_info.bpp = active_vbe_mode_info.bits_per_pixel;
framebuffer.mode_info.bytes_per_pixel = framebuffer.bytes_per_pixel;
framebuffer.mode_info.pitch = framebuffer.bytes_per_scan_line;
framebuffer.mode_info.number_of_colors = 256; /* TODO: fix me. */
framebuffer.mode_info.red_mask_size = active_vbe_mode_info.red_mask_size;
framebuffer.mode_info.red_field_pos = active_vbe_mode_info.red_field_position;
framebuffer.mode_info.green_mask_size = active_vbe_mode_info.green_mask_size;
framebuffer.mode_info.green_field_pos = active_vbe_mode_info.green_field_position;
framebuffer.mode_info.blue_mask_size = active_vbe_mode_info.blue_mask_size;
framebuffer.mode_info.blue_field_pos = active_vbe_mode_info.blue_field_position;
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.blit_format = grub_video_get_blit_format (&framebuffer.mode_info);
/* Set up double buffering and targets. */
err = double_buffering_init (mode_type, mode_mask);
if (err)
return err;
err = grub_video_fb_set_active_render_target (framebuffer.back_target);
if (err)
return err;
/* Copy default palette to initialize emulated palette. */
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
return err;
}
/* Couldn't found matching mode. */
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
}
static grub_err_t
grub_video_vbe_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
if (framebuffer.index_color_mode)
{
/* TODO: Implement setting indexed color mode palette to hardware. */
//status = grub_vbe_bios_set_palette_data (sizeof (vga_colors)
// / sizeof (struct grub_vbe_palette_data),
// 0,
// palette);
}
/* Then set color to emulated palette. */
return grub_video_fb_set_palette (start, count, palette_data);
}
static grub_err_t
grub_video_vbe_swap_buffers (void)
{
grub_err_t err;
if (!framebuffer.update_screen)
return GRUB_ERR_NONE;
err = framebuffer.update_screen (framebuffer.front_target,
framebuffer.back_target);
if (err)
return err;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_vbe_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
target = framebuffer.back_target;
return grub_video_fb_set_active_render_target (target);
}
static grub_err_t
grub_video_vbe_get_active_render_target (struct grub_video_render_target **target)
{
grub_err_t err;
err = grub_video_fb_get_active_render_target (target);
if (err)
return err;
if (*target == framebuffer.back_target)
*target = GRUB_VIDEO_RENDER_TARGET_DISPLAY;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuf)
{
grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
*framebuf = (char *) framebuffer.ptr
+ framebuffer.displayed_page * framebuffer.page_size;
grub_free (vbe_mode_list);
vbe_mode_list = NULL;
grub_video_fb_fini ();
grub_free (framebuffer.offscreen_buffer);
return GRUB_ERR_NONE;
}
static struct grub_video_adapter grub_video_vbe_adapter =
{
.name = "VESA BIOS Extension Video Driver",
.id = GRUB_VIDEO_DRIVER_VBE,
.init = grub_video_vbe_init,
.fini = grub_video_vbe_fini,
.setup = grub_video_vbe_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_vbe_get_info_and_fini,
.set_palette = grub_video_vbe_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_vbe_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_vbe_set_active_render_target,
.get_active_render_target = grub_video_vbe_get_active_render_target,
.next = 0
};
GRUB_MOD_INIT(video_i386_pc_vbe)
{
grub_video_register (&grub_video_vbe_adapter);
}
GRUB_MOD_FINI(video_i386_pc_vbe)
{
grub_video_unregister (&grub_video_vbe_adapter);
}

300
grub-core/video/ieee1275.c Normal file
View file

@ -0,0 +1,300 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
#include <grub/video_fb.h>
#include <grub/ieee1275/ieee1275.h>
/* Only 8-bit indexed color is supported for now. */
static unsigned old_width, old_height;
static int restore_needed;
static char *display;
static struct
{
struct grub_video_mode_info mode_info;
struct grub_video_render_target *render_target;
grub_uint8_t *ptr;
} framebuffer;
static grub_err_t
grub_video_ieee1275_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data);
static void
set_video_mode (unsigned width __attribute__ ((unused)),
unsigned height __attribute__ ((unused)))
{
/* TODO */
}
static void
find_display (void)
{
auto int hook (struct grub_ieee1275_devalias *alias);
int hook (struct grub_ieee1275_devalias *alias)
{
if (grub_strcmp (alias->type, "display") == 0)
{
grub_dprintf ("video", "Found display %s\n", alias->path);
display = grub_strdup (alias->path);
return 1;
}
return 0;
}
grub_ieee1275_devices_iterate (hook);
}
static grub_err_t
grub_video_ieee1275_init (void)
{
grub_memset (&framebuffer, 0, sizeof(framebuffer));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_ieee1275_fini (void)
{
if (restore_needed)
{
set_video_mode (old_width, old_height);
restore_needed = 0;
}
return grub_video_fb_fini ();
}
static grub_err_t
grub_video_ieee1275_fill_mode_info (grub_ieee1275_phandle_t dev,
struct grub_video_mode_info *out)
{
grub_uint32_t tmp;
grub_memset (out, 0, sizeof (*out));
if (grub_ieee1275_get_integer_property (dev, "width", &tmp,
sizeof (tmp), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width.");
out->width = tmp;
if (grub_ieee1275_get_integer_property (dev, "height", &tmp,
sizeof (tmp), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height.");
out->height = tmp;
if (grub_ieee1275_get_integer_property (dev, "linebytes", &tmp,
sizeof (tmp), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display pitch.");
out->pitch = tmp;
out->mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
out->bpp = 8;
out->bytes_per_pixel = 1;
out->number_of_colors = 256;
out->blit_format = grub_video_get_blit_format (out);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_ieee1275_setup (unsigned int width, unsigned int height,
unsigned int mode_type __attribute__ ((unused)),
unsigned int mode_mask __attribute__ ((unused)))
{
grub_uint32_t current_width, current_height, address;
grub_err_t err;
grub_ieee1275_phandle_t dev;
if (!display)
return grub_error (GRUB_ERR_IO, "Couldn't find display device.");
if (grub_ieee1275_finddevice (display, &dev))
return grub_error (GRUB_ERR_IO, "Couldn't open display device.");
if (grub_ieee1275_get_integer_property (dev, "width", &current_width,
sizeof (current_width), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width.");
if (grub_ieee1275_get_integer_property (dev, "height", &current_height,
sizeof (current_width), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height.");
if ((width == current_width && height == current_height)
|| (width == 0 && height == 0))
{
grub_dprintf ("video", "IEEE1275: keeping current mode %dx%d\n",
current_width, current_height);
}
else
{
grub_dprintf ("video", "IEEE1275: Setting mode %dx%d\n", width, height);
/* TODO. */
return grub_error (GRUB_ERR_IO, "can't set mode %dx%d", width, height);
}
err = grub_video_ieee1275_fill_mode_info (dev, &framebuffer.mode_info);
if (err)
{
grub_dprintf ("video", "IEEE1275: couldn't fill mode info\n");
return err;
}
if (grub_ieee1275_get_integer_property (dev, "address", (void *) &address,
sizeof (address), 0))
return grub_error (GRUB_ERR_IO, "Couldn't retrieve display address.");
/* For some reason sparc64 uses 32-bit pointer too. */
framebuffer.ptr = (void *) (grub_addr_t) address;
grub_video_ieee1275_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
grub_dprintf ("video", "IEEE1275: initialising FB @ %p %dx%dx%d\n",
framebuffer.ptr, framebuffer.mode_info.width,
framebuffer.mode_info.height, framebuffer.mode_info.bpp);
err = grub_video_fb_create_render_target_from_pointer
(&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr);
if (err)
{
grub_dprintf ("video", "IEEE1275: Couldn't create FB target\n");
return err;
}
err = grub_video_fb_set_active_render_target (framebuffer.render_target);
if (err)
{
grub_dprintf ("video", "IEEE1275: Couldn't set FB target\n");
return err;
}
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
if (err)
grub_dprintf ("video", "IEEE1275: Couldn't set palette\n");
else
grub_dprintf ("video", "IEEE1275: Success\n");
return err;
}
static grub_err_t
grub_video_ieee1275_swap_buffers (void)
{
/* TODO: Implement buffer swapping. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_ieee1275_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
target = framebuffer.render_target;
return grub_video_fb_set_active_render_target (target);
}
static grub_err_t
grub_video_ieee1275_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuf)
{
grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
*framebuf = (char *) framebuffer.ptr;
grub_video_fb_fini ();
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_ieee1275_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
grub_err_t err;
struct grub_video_palette_data fb_palette_data[256];
err = grub_video_fb_set_palette (start, count, palette_data);
if (err)
return err;
grub_video_fb_get_palette (0, 256, fb_palette_data);
/* TODO. */
return GRUB_ERR_NONE;
}
static struct grub_video_adapter grub_video_ieee1275_adapter =
{
.name = "IEEE1275 video driver",
.init = grub_video_ieee1275_init,
.fini = grub_video_ieee1275_fini,
.setup = grub_video_ieee1275_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_ieee1275_get_info_and_fini,
.set_palette = grub_video_ieee1275_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_ieee1275_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_ieee1275_set_active_render_target,
.get_active_render_target = grub_video_fb_get_active_render_target,
.next = 0
};
GRUB_MOD_INIT(ieee1275_fb)
{
find_display ();
if (display)
grub_video_register (&grub_video_ieee1275_adapter);
}
GRUB_MOD_FINI(ieee1275_fb)
{
if (restore_needed)
{
set_video_mode (old_width, old_height);
restore_needed = 0;
}
if (display)
grub_video_unregister (&grub_video_ieee1275_adapter);
grub_free (display);
}

View file

@ -0,0 +1,750 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/bitmap.h>
#include <grub/types.h>
#include <grub/normal.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/bufio.h>
/* Uncomment following define to enable JPEG debug. */
//#define JPEG_DEBUG
#define JPEG_ESC_CHAR 0xFF
#define JPEG_SAMPLING_1x1 0x11
#define JPEG_MARKER_SOI 0xd8
#define JPEG_MARKER_EOI 0xd9
#define JPEG_MARKER_DHT 0xc4
#define JPEG_MARKER_DQT 0xdb
#define JPEG_MARKER_SOF0 0xc0
#define JPEG_MARKER_SOS 0xda
#define SHIFT_BITS 8
#define CONST(x) ((int) ((x) * (1L << SHIFT_BITS) + 0.5))
#define JPEG_UNIT_SIZE 8
static const grub_uint8_t jpeg_zigzag_order[64] = {
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63
};
#ifdef JPEG_DEBUG
static grub_command_t cmd;
#endif
typedef int jpeg_data_unit_t[64];
struct grub_jpeg_data
{
grub_file_t file;
struct grub_video_bitmap **bitmap;
int image_width;
int image_height;
grub_uint8_t *huff_value[4];
int huff_offset[4][16];
int huff_maxval[4][16];
grub_uint8_t quan_table[2][64];
int comp_index[3][3];
jpeg_data_unit_t ydu[4];
jpeg_data_unit_t crdu;
jpeg_data_unit_t cbdu;
int vs, hs;
int dc_value[3];
int bit_mask, bit_save;
};
static grub_uint8_t
grub_jpeg_get_byte (struct grub_jpeg_data *data)
{
grub_uint8_t r;
r = 0;
grub_file_read (data->file, &r, 1);
return r;
}
static grub_uint16_t
grub_jpeg_get_word (struct grub_jpeg_data *data)
{
grub_uint16_t r;
r = 0;
grub_file_read (data->file, &r, sizeof (grub_uint16_t));
return grub_be_to_cpu16 (r);
}
static int
grub_jpeg_get_bit (struct grub_jpeg_data *data)
{
int ret;
if (data->bit_mask == 0)
{
data->bit_save = grub_jpeg_get_byte (data);
if (data->bit_save == JPEG_ESC_CHAR)
{
if (grub_jpeg_get_byte (data) != 0)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: invalid 0xFF in data stream");
return 0;
}
}
data->bit_mask = 0x80;
}
ret = ((data->bit_save & data->bit_mask) != 0);
data->bit_mask >>= 1;
return ret;
}
static int
grub_jpeg_get_number (struct grub_jpeg_data *data, int num)
{
int value, i, msb;
if (num == 0)
return 0;
msb = value = grub_jpeg_get_bit (data);
for (i = 1; i < num; i++)
value = (value << 1) + (grub_jpeg_get_bit (data) != 0);
if (!msb)
value += 1 - (1 << num);
return value;
}
static int
grub_jpeg_get_huff_code (struct grub_jpeg_data *data, int id)
{
int code, i;
code = 0;
for (i = 0; i < 16; i++)
{
code <<= 1;
if (grub_jpeg_get_bit (data))
code++;
if (code < data->huff_maxval[id][i])
return data->huff_value[id][code + data->huff_offset[id][i]];
}
grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: huffman decode fails");
return 0;
}
static grub_err_t
grub_jpeg_decode_huff_table (struct grub_jpeg_data *data)
{
int id, ac, i, n, base, ofs;
grub_uint32_t next_marker;
grub_uint8_t count[16];
next_marker = data->file->offset;
next_marker += grub_jpeg_get_word (data);
id = grub_jpeg_get_byte (data);
ac = (id >> 4);
id &= 0xF;
if (id > 1)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: too many huffman tables");
if (grub_file_read (data->file, &count, sizeof (count)) !=
sizeof (count))
return grub_errno;
n = 0;
for (i = 0; i < 16; i++)
n += count[i];
id += ac * 2;
data->huff_value[id] = grub_malloc (n);
if (grub_errno)
return grub_errno;
if (grub_file_read (data->file, data->huff_value[id], n) != n)
return grub_errno;
base = 0;
ofs = 0;
for (i = 0; i < 16; i++)
{
base += count[i];
ofs += count[i];
data->huff_maxval[id][i] = base;
data->huff_offset[id][i] = ofs - base;
base <<= 1;
}
if (data->file->offset != next_marker)
grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in huffman table");
return grub_errno;
}
static grub_err_t
grub_jpeg_decode_quan_table (struct grub_jpeg_data *data)
{
int id;
grub_uint32_t next_marker;
next_marker = data->file->offset;
next_marker += grub_jpeg_get_word (data);
id = grub_jpeg_get_byte (data);
if (id >= 0x10) /* Upper 4-bit is precision. */
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: only 8-bit precision is supported");
if (id > 1)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: too many quantization tables");
if (grub_file_read (data->file, &data->quan_table[id], 64) != 64)
return grub_errno;
if (data->file->offset != next_marker)
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: extra byte in quantization table");
return grub_errno;
}
static grub_err_t
grub_jpeg_decode_sof (struct grub_jpeg_data *data)
{
int i, cc;
grub_uint32_t next_marker;
next_marker = data->file->offset;
next_marker += grub_jpeg_get_word (data);
if (grub_jpeg_get_byte (data) != 8)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: only 8-bit precision is supported");
data->image_height = grub_jpeg_get_word (data);
data->image_width = grub_jpeg_get_word (data);
if ((!data->image_height) || (!data->image_width))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid image size");
cc = grub_jpeg_get_byte (data);
if (cc != 3)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: component count must be 3");
for (i = 0; i < cc; i++)
{
int id, ss;
id = grub_jpeg_get_byte (data) - 1;
if ((id < 0) || (id >= 3))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
ss = grub_jpeg_get_byte (data); /* Sampling factor. */
if (!id)
{
data->vs = ss & 0xF; /* Vertical sampling. */
data->hs = ss >> 4; /* Horizontal sampling. */
if ((data->vs > 2) || (data->hs > 2))
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: sampling method not supported");
}
else if (ss != JPEG_SAMPLING_1x1)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: sampling method not supported");
data->comp_index[id][0] = grub_jpeg_get_byte (data);
}
if (data->file->offset != next_marker)
grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sof");
return grub_errno;
}
static void
grub_jpeg_idct_transform (jpeg_data_unit_t du)
{
int *pd;
int i;
int t0, t1, t2, t3, t4, t5, t6, t7;
int v0, v1, v2, v3, v4;
pd = du;
for (i = 0; i < JPEG_UNIT_SIZE; i++, pd++)
{
if ((pd[JPEG_UNIT_SIZE * 1] | pd[JPEG_UNIT_SIZE * 2] |
pd[JPEG_UNIT_SIZE * 3] | pd[JPEG_UNIT_SIZE * 4] |
pd[JPEG_UNIT_SIZE * 5] | pd[JPEG_UNIT_SIZE * 6] |
pd[JPEG_UNIT_SIZE * 7]) == 0)
{
pd[JPEG_UNIT_SIZE * 0] <<= SHIFT_BITS;
pd[JPEG_UNIT_SIZE * 1] = pd[JPEG_UNIT_SIZE * 2]
= pd[JPEG_UNIT_SIZE * 3] = pd[JPEG_UNIT_SIZE * 4]
= pd[JPEG_UNIT_SIZE * 5] = pd[JPEG_UNIT_SIZE * 6]
= pd[JPEG_UNIT_SIZE * 7] = pd[JPEG_UNIT_SIZE * 0];
continue;
}
t0 = pd[JPEG_UNIT_SIZE * 0];
t1 = pd[JPEG_UNIT_SIZE * 2];
t2 = pd[JPEG_UNIT_SIZE * 4];
t3 = pd[JPEG_UNIT_SIZE * 6];
v4 = (t1 + t3) * CONST (0.541196100);
v0 = ((t0 + t2) << SHIFT_BITS);
v1 = ((t0 - t2) << SHIFT_BITS);
v2 = v4 - t3 * CONST (1.847759065);
v3 = v4 + t1 * CONST (0.765366865);
t0 = v0 + v3;
t3 = v0 - v3;
t1 = v1 + v2;
t2 = v1 - v2;
t4 = pd[JPEG_UNIT_SIZE * 7];
t5 = pd[JPEG_UNIT_SIZE * 5];
t6 = pd[JPEG_UNIT_SIZE * 3];
t7 = pd[JPEG_UNIT_SIZE * 1];
v0 = t4 + t7;
v1 = t5 + t6;
v2 = t4 + t6;
v3 = t5 + t7;
v4 = (v2 + v3) * CONST (1.175875602);
v0 *= CONST (0.899976223);
v1 *= CONST (2.562915447);
v2 = v2 * CONST (1.961570560) - v4;
v3 = v3 * CONST (0.390180644) - v4;
t4 = t4 * CONST (0.298631336) - v0 - v2;
t5 = t5 * CONST (2.053119869) - v1 - v3;
t6 = t6 * CONST (3.072711026) - v1 - v2;
t7 = t7 * CONST (1.501321110) - v0 - v3;
pd[JPEG_UNIT_SIZE * 0] = t0 + t7;
pd[JPEG_UNIT_SIZE * 7] = t0 - t7;
pd[JPEG_UNIT_SIZE * 1] = t1 + t6;
pd[JPEG_UNIT_SIZE * 6] = t1 - t6;
pd[JPEG_UNIT_SIZE * 2] = t2 + t5;
pd[JPEG_UNIT_SIZE * 5] = t2 - t5;
pd[JPEG_UNIT_SIZE * 3] = t3 + t4;
pd[JPEG_UNIT_SIZE * 4] = t3 - t4;
}
pd = du;
for (i = 0; i < JPEG_UNIT_SIZE; i++, pd += JPEG_UNIT_SIZE)
{
if ((pd[1] | pd[2] | pd[3] | pd[4] | pd[5] | pd[6] | pd[7]) == 0)
{
pd[0] >>= (SHIFT_BITS + 3);
pd[1] = pd[2] = pd[3] = pd[4] = pd[5] = pd[6] = pd[7] = pd[0];
continue;
}
v4 = (pd[2] + pd[6]) * CONST (0.541196100);
v0 = (pd[0] + pd[4]) << SHIFT_BITS;
v1 = (pd[0] - pd[4]) << SHIFT_BITS;
v2 = v4 - pd[6] * CONST (1.847759065);
v3 = v4 + pd[2] * CONST (0.765366865);
t0 = v0 + v3;
t3 = v0 - v3;
t1 = v1 + v2;
t2 = v1 - v2;
t4 = pd[7];
t5 = pd[5];
t6 = pd[3];
t7 = pd[1];
v0 = t4 + t7;
v1 = t5 + t6;
v2 = t4 + t6;
v3 = t5 + t7;
v4 = (v2 + v3) * CONST (1.175875602);
v0 *= CONST (0.899976223);
v1 *= CONST (2.562915447);
v2 = v2 * CONST (1.961570560) - v4;
v3 = v3 * CONST (0.390180644) - v4;
t4 = t4 * CONST (0.298631336) - v0 - v2;
t5 = t5 * CONST (2.053119869) - v1 - v3;
t6 = t6 * CONST (3.072711026) - v1 - v2;
t7 = t7 * CONST (1.501321110) - v0 - v3;
pd[0] = (t0 + t7) >> (SHIFT_BITS * 2 + 3);
pd[7] = (t0 - t7) >> (SHIFT_BITS * 2 + 3);
pd[1] = (t1 + t6) >> (SHIFT_BITS * 2 + 3);
pd[6] = (t1 - t6) >> (SHIFT_BITS * 2 + 3);
pd[2] = (t2 + t5) >> (SHIFT_BITS * 2 + 3);
pd[5] = (t2 - t5) >> (SHIFT_BITS * 2 + 3);
pd[3] = (t3 + t4) >> (SHIFT_BITS * 2 + 3);
pd[4] = (t3 - t4) >> (SHIFT_BITS * 2 + 3);
}
for (i = 0; i < JPEG_UNIT_SIZE * JPEG_UNIT_SIZE; i++)
{
du[i] += 128;
if (du[i] < 0)
du[i] = 0;
if (du[i] > 255)
du[i] = 255;
}
}
static void
grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
{
int pos, h1, h2, qt;
grub_memset (du, 0, sizeof (jpeg_data_unit_t));
qt = data->comp_index[id][0];
h1 = data->comp_index[id][1];
h2 = data->comp_index[id][2];
data->dc_value[id] +=
grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1));
du[0] = data->dc_value[id] * (int) data->quan_table[qt][0];
pos = 1;
while (pos < 64)
{
int num, val;
num = grub_jpeg_get_huff_code (data, h2);
if (!num)
break;
val = grub_jpeg_get_number (data, num & 0xF);
num >>= 4;
pos += num;
du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos];
pos++;
}
grub_jpeg_idct_transform (du);
}
static void
grub_jpeg_ycrcb_to_rgb (int yy, int cr, int cb, grub_uint8_t * rgb)
{
int dd;
cr -= 128;
cb -= 128;
/* Red */
dd = yy + ((cr * CONST (1.402)) >> SHIFT_BITS);
if (dd < 0)
dd = 0;
if (dd > 255)
dd = 255;
*(rgb++) = dd;
/* Green */
dd = yy - ((cb * CONST (0.34414) + cr * CONST (0.71414)) >> SHIFT_BITS);
if (dd < 0)
dd = 0;
if (dd > 255)
dd = 255;
*(rgb++) = dd;
/* Blue */
dd = yy + ((cb * CONST (1.772)) >> SHIFT_BITS);
if (dd < 0)
dd = 0;
if (dd > 255)
dd = 255;
*(rgb++) = dd;
}
static grub_err_t
grub_jpeg_decode_sos (struct grub_jpeg_data *data)
{
int i, cc, r1, c1, nr1, nc1, vb, hb;
grub_uint8_t *ptr1;
grub_uint32_t data_offset;
data_offset = data->file->offset;
data_offset += grub_jpeg_get_word (data);
cc = grub_jpeg_get_byte (data);
if (cc != 3)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: component count must be 3");
for (i = 0; i < cc; i++)
{
int id, ht;
id = grub_jpeg_get_byte (data) - 1;
if ((id < 0) || (id >= 3))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
ht = grub_jpeg_get_byte (data);
data->comp_index[id][1] = (ht >> 4);
data->comp_index[id][2] = (ht & 0xF) + 2;
}
grub_jpeg_get_byte (data); /* Skip 3 unused bytes. */
grub_jpeg_get_word (data);
if (data->file->offset != data_offset)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
if (grub_video_bitmap_create (data->bitmap, data->image_width,
data->image_height,
GRUB_VIDEO_BLIT_FORMAT_RGB_888))
return grub_errno;
data->bit_mask = 0x0;
vb = data->vs * 8;
hb = data->hs * 8;
nr1 = (data->image_height + vb - 1) / vb;
nc1 = (data->image_width + hb - 1) / hb;
ptr1 = (*data->bitmap)->data;
for (r1 = 0; r1 < nr1;
r1++, ptr1 += (vb * data->image_width - hb * nc1) * 3)
for (c1 = 0; c1 < nc1; c1++, ptr1 += hb * 3)
{
int r2, c2, nr2, nc2;
grub_uint8_t *ptr2;
for (r2 = 0; r2 < data->vs; r2++)
for (c2 = 0; c2 < data->hs; c2++)
grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
grub_jpeg_decode_du (data, 1, data->cbdu);
grub_jpeg_decode_du (data, 2, data->crdu);
if (grub_errno)
return grub_errno;
nr2 = (r1 == nr1 - 1) ? (data->image_height - r1 * vb) : vb;
nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb;
ptr2 = ptr1;
for (r2 = 0; r2 < nr2; r2++, ptr2 += (data->image_width - nc2) * 3)
for (c2 = 0; c2 < nc2; c2++, ptr2 += 3)
{
int i0, yy, cr, cb;
i0 = (r2 / data->vs) * 8 + (c2 / data->hs);
cr = data->crdu[i0];
cb = data->cbdu[i0];
yy =
data->ydu[(r2 / 8) * 2 + (c2 / 8)][(r2 % 8) * 8 + (c2 % 8)];
grub_jpeg_ycrcb_to_rgb (yy, cr, cb, ptr2);
}
}
return grub_errno;
}
static grub_uint8_t
grub_jpeg_get_marker (struct grub_jpeg_data *data)
{
grub_uint8_t r;
r = grub_jpeg_get_byte (data);
if (r != JPEG_ESC_CHAR)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid maker");
return 0;
}
return grub_jpeg_get_byte (data);
}
static grub_err_t
grub_jpeg_decode_jpeg (struct grub_jpeg_data *data)
{
if (grub_jpeg_get_marker (data) != JPEG_MARKER_SOI) /* Start Of Image. */
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid jpeg file");
while (grub_errno == 0)
{
grub_uint8_t marker;
marker = grub_jpeg_get_marker (data);
if (grub_errno)
break;
#ifdef JPEG_DEBUG
grub_printf ("jpeg marker: %x\n", marker);
#endif
switch (marker)
{
case JPEG_MARKER_DHT: /* Define Huffman Table. */
grub_jpeg_decode_huff_table (data);
break;
case JPEG_MARKER_DQT: /* Define Quantization Table. */
grub_jpeg_decode_quan_table (data);
break;
case JPEG_MARKER_SOF0: /* Start Of Frame 0. */
grub_jpeg_decode_sof (data);
break;
case JPEG_MARKER_SOS: /* Start Of Scan. */
grub_jpeg_decode_sos (data);
break;
case JPEG_MARKER_EOI: /* End Of Image. */
return grub_errno;
default: /* Skip unrecognized marker. */
{
grub_uint16_t sz;
sz = grub_jpeg_get_word (data);
if (grub_errno)
return (grub_errno);
grub_file_seek (data->file, data->file->offset + sz - 2);
}
}
}
return grub_errno;
}
static grub_err_t
grub_video_reader_jpeg (struct grub_video_bitmap **bitmap,
const char *filename)
{
grub_file_t file;
struct grub_jpeg_data *data;
file = grub_buffile_open (filename, 0);
if (!file)
return grub_errno;
data = grub_zalloc (sizeof (*data));
if (data != NULL)
{
int i;
data->file = file;
data->bitmap = bitmap;
grub_jpeg_decode_jpeg (data);
for (i = 0; i < 4; i++)
if (data->huff_value[i])
grub_free (data->huff_value[i]);
grub_free (data);
}
if (grub_errno != GRUB_ERR_NONE)
{
grub_video_bitmap_destroy (*bitmap);
*bitmap = 0;
}
grub_file_close (file);
return grub_errno;
}
#if defined(JPEG_DEBUG)
static grub_err_t
grub_cmd_jpegtest (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_video_bitmap *bitmap = 0;
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
grub_video_reader_jpeg (&bitmap, args[0]);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
grub_video_bitmap_destroy (bitmap);
return GRUB_ERR_NONE;
}
#endif
static struct grub_video_bitmap_reader jpg_reader = {
.extension = ".jpg",
.reader = grub_video_reader_jpeg,
.next = 0
};
static struct grub_video_bitmap_reader jpeg_reader = {
.extension = ".jpeg",
.reader = grub_video_reader_jpeg,
.next = 0
};
GRUB_MOD_INIT (jpeg)
{
grub_video_bitmap_reader_register (&jpg_reader);
grub_video_bitmap_reader_register (&jpeg_reader);
#if defined(JPEG_DEBUG)
cmd = grub_register_command ("jpegtest", grub_cmd_jpegtest,
"FILE",
"Tests loading of JPEG bitmap.");
#endif
}
GRUB_MOD_FINI (jpeg)
{
#if defined(JPEG_DEBUG)
grub_unregister_command (cmd);
#endif
grub_video_bitmap_reader_unregister (&jpeg_reader);
grub_video_bitmap_reader_unregister (&jpg_reader);
}

View file

@ -0,0 +1,913 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008,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 <http://www.gnu.org/licenses/>.
*/
#include <grub/bitmap.h>
#include <grub/types.h>
#include <grub/normal.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/bufio.h>
/* Uncomment following define to enable PNG debug. */
//#define PNG_DEBUG
#define PNG_COLOR_MASK_PALETTE 1
#define PNG_COLOR_MASK_COLOR 2
#define PNG_COLOR_MASK_ALPHA 4
#define PNG_COLOR_TYPE_GRAY 0
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
#define PNG_COLOR_TYPE_RGBA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
#define PNG_COLOR_TYPE_GRAYA (PNG_COLOR_MASK_ALPHA)
#define PNG_COMPRESSION_BASE 0
#define PNG_INTERLACE_NONE 0
#define PNG_INTERLACE_ADAM7 1
#define PNG_FILTER_TYPE_BASE 0
#define PNG_FILTER_VALUE_NONE 0
#define PNG_FILTER_VALUE_SUB 1
#define PNG_FILTER_VALUE_UP 2
#define PNG_FILTER_VALUE_AVG 3
#define PNG_FILTER_VALUE_PAETH 4
#define PNG_FILTER_VALUE_LAST 5
#define PNG_CHUNK_IHDR 0x49484452
#define PNG_CHUNK_IDAT 0x49444154
#define PNG_CHUNK_IEND 0x49454e44
#define Z_DEFLATED 8
#define Z_FLAG_DICT 32
#define INFLATE_STORED 0
#define INFLATE_FIXED 1
#define INFLATE_DYNAMIC 2
#define WSIZE 0x8000
#define DEFLATE_HCLEN_BASE 4
#define DEFLATE_HCLEN_MAX 19
#define DEFLATE_HLIT_BASE 257
#define DEFLATE_HLIT_MAX 288
#define DEFLATE_HDIST_BASE 1
#define DEFLATE_HDIST_MAX 30
#define DEFLATE_HUFF_LEN 16
#ifdef PNG_DEBUG
static grub_command_t cmd;
#endif
struct huff_table
{
int *values, *maxval, *offset;
int num_values, max_length;
};
struct grub_png_data
{
grub_file_t file;
struct grub_video_bitmap **bitmap;
int bit_count, bit_save;
grub_uint32_t next_offset;
int image_width, image_height, bpp, is_16bit, raw_bytes;
grub_uint8_t *image_data;
int inside_idat, idat_remain;
int code_values[DEFLATE_HLIT_MAX];
int code_maxval[DEFLATE_HUFF_LEN];
int code_offset[DEFLATE_HUFF_LEN];
int dist_values[DEFLATE_HDIST_MAX];
int dist_maxval[DEFLATE_HUFF_LEN];
int dist_offset[DEFLATE_HUFF_LEN];
struct huff_table code_table;
struct huff_table dist_table;
grub_uint8_t slide[WSIZE];
int wp;
grub_uint8_t *cur_rgb;
int cur_column, cur_filter, first_line;
};
static grub_uint32_t
grub_png_get_dword (struct grub_png_data *data)
{
grub_uint32_t r;
r = 0;
grub_file_read (data->file, &r, sizeof (grub_uint32_t));
return grub_be_to_cpu32 (r);
}
static grub_uint8_t
grub_png_get_byte (struct grub_png_data *data)
{
grub_uint8_t r;
if ((data->inside_idat) && (data->idat_remain == 0))
{
grub_uint32_t len, type;
do
{
/* Skip crc checksum. */
grub_png_get_dword (data);
if (data->file->offset != data->next_offset)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: chunk size error");
return 0;
}
len = grub_png_get_dword (data);
type = grub_png_get_dword (data);
if (type != PNG_CHUNK_IDAT)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: unexpected end of data");
return 0;
}
data->next_offset = data->file->offset + len + 4;
}
while (len == 0);
data->idat_remain = len;
}
r = 0;
grub_file_read (data->file, &r, 1);
if (data->inside_idat)
data->idat_remain--;
return r;
}
static int
grub_png_get_bits (struct grub_png_data *data, int num)
{
int code, shift;
if (data->bit_count == 0)
{
data->bit_save = grub_png_get_byte (data);
data->bit_count = 8;
}
code = 0;
shift = 0;
while (grub_errno == 0)
{
int n;
n = data->bit_count;
if (n > num)
n = num;
code += (int) (data->bit_save & ((1 << n) - 1)) << shift;
num -= n;
if (!num)
{
data->bit_count -= n;
data->bit_save >>= n;
break;
}
shift += n;
data->bit_save = grub_png_get_byte (data);
data->bit_count = 8;
}
return code;
}
static grub_err_t
grub_png_decode_image_header (struct grub_png_data *data)
{
int color_type;
int color_bits;
data->image_width = grub_png_get_dword (data);
data->image_height = grub_png_get_dword (data);
if ((!data->image_height) || (!data->image_width))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size");
color_bits = grub_png_get_byte (data);
if ((color_bits != 8) && (color_bits != 16))
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: bit depth must be 8 or 16");
data->is_16bit = (color_bits == 16);
color_type = grub_png_get_byte (data);
if (color_type == PNG_COLOR_TYPE_RGB)
{
if (grub_video_bitmap_create (data->bitmap, data->image_width,
data->image_height,
GRUB_VIDEO_BLIT_FORMAT_RGB_888))
return grub_errno;
data->bpp = 3;
}
else if (color_type == PNG_COLOR_TYPE_RGBA)
{
if (grub_video_bitmap_create (data->bitmap, data->image_width,
data->image_height,
GRUB_VIDEO_BLIT_FORMAT_RGBA_8888))
return grub_errno;
data->bpp = 4;
}
else
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: color type not supported");
if (data->is_16bit)
{
data->bpp <<= 1;
data->image_data = grub_malloc (data->image_height *
data->image_width * data->bpp);
if (grub_errno)
return grub_errno;
data->cur_rgb = data->image_data;
}
else
{
data->image_data = 0;
data->cur_rgb = (*data->bitmap)->data;
}
data->raw_bytes = data->image_height * (data->image_width + 1) * data->bpp;
data->cur_column = 0;
data->first_line = 1;
if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: compression method not supported");
if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: filter method not supported");
if (grub_png_get_byte (data) != PNG_INTERLACE_NONE)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: interlace method not supported");
/* Skip crc checksum. */
grub_png_get_dword (data);
return grub_errno;
}
/* Order of the bit length code lengths. */
static const grub_uint8_t bitorder[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
/* Copy lengths for literal codes 257..285. */
static const int cplens[] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
};
/* Extra bits for literal codes 257..285. */
static const grub_uint8_t cplext[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
}; /* 99==invalid */
/* Copy offsets for distance codes 0..29. */
static const int cpdist[] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577
};
/* Extra bits for distance codes. */
static const grub_uint8_t cpdext[] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13
};
static void
grub_png_init_huff_table (struct huff_table *ht, int cur_maxlen,
int *cur_values, int *cur_maxval, int *cur_offset)
{
ht->values = cur_values;
ht->maxval = cur_maxval;
ht->offset = cur_offset;
ht->num_values = 0;
ht->max_length = cur_maxlen;
grub_memset (cur_maxval, 0, sizeof (int) * cur_maxlen);
}
static void
grub_png_insert_huff_item (struct huff_table *ht, int code, int len)
{
int i, n;
if (len == 0)
return;
if (len > ht->max_length)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid code length");
return;
}
n = 0;
for (i = len; i < ht->max_length; i++)
n += ht->maxval[i];
for (i = 0; i < n; i++)
ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1];
ht->values[ht->num_values - n] = code;
ht->num_values++;
ht->maxval[len - 1]++;
}
static void
grub_png_build_huff_table (struct huff_table *ht)
{
int base, ofs, i;
base = 0;
ofs = 0;
for (i = 0; i < ht->max_length; i++)
{
base += ht->maxval[i];
ofs += ht->maxval[i];
ht->maxval[i] = base;
ht->offset[i] = ofs - base;
base <<= 1;
}
}
static int
grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht)
{
int code, i;
code = 0;
for (i = 0; i < ht->max_length; i++)
{
code = (code << 1) + grub_png_get_bits (data, 1);
if (code < ht->maxval[i])
return ht->values[code + ht->offset[i]];
}
return 0;
}
static grub_err_t
grub_png_init_fixed_block (struct grub_png_data *data)
{
int i;
grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN,
data->code_values, data->code_maxval,
data->code_offset);
for (i = 0; i < 144; i++)
grub_png_insert_huff_item (&data->code_table, i, 8);
for (; i < 256; i++)
grub_png_insert_huff_item (&data->code_table, i, 9);
for (; i < 280; i++)
grub_png_insert_huff_item (&data->code_table, i, 7);
for (; i < DEFLATE_HLIT_MAX; i++)
grub_png_insert_huff_item (&data->code_table, i, 8);
grub_png_build_huff_table (&data->code_table);
grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN,
data->dist_values, data->dist_maxval,
data->dist_offset);
for (i = 0; i < DEFLATE_HDIST_MAX; i++)
grub_png_insert_huff_item (&data->dist_table, i, 5);
grub_png_build_huff_table (&data->dist_table);
return grub_errno;
}
static grub_err_t
grub_png_init_dynamic_block (struct grub_png_data *data)
{
int nl, nd, nb, i, prev;
struct huff_table cl;
int cl_values[sizeof (bitorder)];
int cl_maxval[8];
int cl_offset[8];
grub_uint8_t lens[DEFLATE_HCLEN_MAX];
nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5);
nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5);
nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4);
if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) ||
(nb > DEFLATE_HCLEN_MAX))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: too much data");
grub_png_init_huff_table (&cl, 8, cl_values, cl_maxval, cl_offset);
for (i = 0; i < nb; i++)
lens[bitorder[i]] = grub_png_get_bits (data, 3);
for (; i < DEFLATE_HCLEN_MAX; i++)
lens[bitorder[i]] = 0;
for (i = 0; i < DEFLATE_HCLEN_MAX; i++)
grub_png_insert_huff_item (&cl, i, lens[i]);
grub_png_build_huff_table (&cl);
grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN,
data->code_values, data->code_maxval,
data->code_offset);
grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN,
data->dist_values, data->dist_maxval,
data->dist_offset);
prev = 0;
for (i = 0; i < nl + nd; i++)
{
int n, code;
struct huff_table *ht;
if (grub_errno)
return grub_errno;
if (i < nl)
{
ht = &data->code_table;
code = i;
}
else
{
ht = &data->dist_table;
code = i - nl;
}
n = grub_png_get_huff_code (data, &cl);
if (n < 16)
{
grub_png_insert_huff_item (ht, code, n);
prev = n;
}
else if (n == 16)
{
int c;
c = 3 + grub_png_get_bits (data, 2);
while (c > 0)
{
grub_png_insert_huff_item (ht, code++, prev);
i++;
c--;
}
i--;
}
else if (n == 17)
i += 3 + grub_png_get_bits (data, 3) - 1;
else
i += 11 + grub_png_get_bits (data, 7) - 1;
}
grub_png_build_huff_table (&data->code_table);
grub_png_build_huff_table (&data->dist_table);
return grub_errno;
}
static grub_err_t
grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n)
{
int row_bytes;
if (--data->raw_bytes < 0)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflown");
if (data->cur_column == 0)
{
if (n >= PNG_FILTER_VALUE_LAST)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid filter value");
data->cur_filter = n;
}
else
*(data->cur_rgb++) = n;
data->cur_column++;
row_bytes = data->image_width * data->bpp;
if (data->cur_column == row_bytes + 1)
{
grub_uint8_t *blank_line = NULL;
grub_uint8_t *cur = data->cur_rgb - row_bytes;
grub_uint8_t *left = cur;
grub_uint8_t *up;
if (data->first_line)
{
blank_line = grub_zalloc (row_bytes);
if (blank_line == NULL)
return grub_errno;
up = blank_line;
}
else
up = cur - row_bytes;
switch (data->cur_filter)
{
case PNG_FILTER_VALUE_SUB:
{
int i;
cur += data->bpp;
for (i = data->bpp; i < row_bytes; i++, cur++, left++)
*cur += *left;
break;
}
case PNG_FILTER_VALUE_UP:
{
int i;
for (i = 0; i < row_bytes; i++, cur++, up++)
*cur += *up;
break;
}
case PNG_FILTER_VALUE_AVG:
{
int i;
for (i = 0; i < data->bpp; i++, cur++, up++)
*cur += *up >> 1;
for (; i < row_bytes; i++, cur++, up++, left++)
*cur += ((int) *up + (int) *left) >> 1;
break;
}
case PNG_FILTER_VALUE_PAETH:
{
int i;
grub_uint8_t *upper_left = up;
for (i = 0; i < data->bpp; i++, cur++, up++)
*cur += *up;
for (; i < row_bytes; i++, cur++, up++, left++, upper_left++)
{
int a, b, c, pa, pb, pc;
a = *left;
b = *up;
c = *upper_left;
pa = b - c;
pb = a - c;
pc = pa + pb;
if (pa < 0)
pa = -pa;
if (pb < 0)
pb = -pb;
if (pc < 0)
pc = -pc;
*cur += ((pa <= pb) && (pa <= pc)) ? a : (pb <= pc) ? b : c;
}
}
}
if (blank_line)
grub_free (blank_line);
data->cur_column = 0;
data->first_line = 0;
}
return grub_errno;
}
static grub_err_t
grub_png_read_dynamic_block (struct grub_png_data *data)
{
while (grub_errno == 0)
{
int n;
n = grub_png_get_huff_code (data, &data->code_table);
if (n < 256)
{
data->slide[data->wp] = n;
grub_png_output_byte (data, n);
data->wp++;
if (data->wp >= WSIZE)
data->wp = 0;
}
else if (n == 256)
break;
else
{
int len, dist, pos;
n -= 257;
len = cplens[n];
if (cplext[n])
len += grub_png_get_bits (data, cplext[n]);
n = grub_png_get_huff_code (data, &data->dist_table);
dist = cpdist[n];
if (cpdext[n])
dist += grub_png_get_bits (data, cpdext[n]);
pos = data->wp - dist;
if (pos < 0)
pos += WSIZE;
while (len > 0)
{
data->slide[data->wp] = data->slide[pos];
grub_png_output_byte (data, data->slide[data->wp]);
data->wp++;
if (data->wp >= WSIZE)
data->wp = 0;
pos++;
if (pos >= WSIZE)
pos = 0;
len--;
}
}
}
return grub_errno;
}
static grub_err_t
grub_png_decode_image_data (struct grub_png_data *data)
{
grub_uint8_t cmf, flg;
int final;
cmf = grub_png_get_byte (data);
flg = grub_png_get_byte (data);
if ((cmf & 0xF) != Z_DEFLATED)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: only support deflate compression method");
if (flg & Z_FLAG_DICT)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: dictionary not supported");
do
{
int block_type;
final = grub_png_get_bits (data, 1);
block_type = grub_png_get_bits (data, 2);
switch (block_type)
{
case INFLATE_STORED:
{
grub_uint16_t i, len;
data->bit_count = 0;
len = grub_png_get_byte (data);
len += ((grub_uint16_t) grub_png_get_byte (data)) << 8;
/* Skip NLEN field. */
grub_png_get_byte (data);
grub_png_get_byte (data);
for (i = 0; i < len; i++)
grub_png_output_byte (data, grub_png_get_byte (data));
break;
}
case INFLATE_FIXED:
grub_png_init_fixed_block (data);
grub_png_read_dynamic_block (data);
break;
case INFLATE_DYNAMIC:
grub_png_init_dynamic_block (data);
grub_png_read_dynamic_block (data);
break;
default:
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: unknown block type");
}
}
while ((!final) && (grub_errno == 0));
/* Skip adler checksum. */
grub_png_get_dword (data);
/* Skip crc checksum. */
grub_png_get_dword (data);
return grub_errno;
}
static const grub_uint8_t png_magic[8] =
{ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
static void
grub_png_convert_image (struct grub_png_data *data)
{
int i;
grub_uint8_t *d1, *d2;
d1 = (*data->bitmap)->data;
d2 = data->image_data + 1;
/* Only copy the upper 8 bit. */
for (i = 0; i < (data->image_width * data->image_height * data->bpp >> 1);
i++, d1++, d2+=2)
*d1 = *d2;
}
static grub_err_t
grub_png_decode_png (struct grub_png_data *data)
{
grub_uint8_t magic[8];
if (grub_file_read (data->file, &magic[0], 8) != 8)
return grub_errno;
if (grub_memcmp (magic, png_magic, sizeof (png_magic)))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: not a png file");
while (1)
{
grub_uint32_t len, type;
len = grub_png_get_dword (data);
type = grub_png_get_dword (data);
data->next_offset = data->file->offset + len + 4;
switch (type)
{
case PNG_CHUNK_IHDR:
grub_png_decode_image_header (data);
break;
case PNG_CHUNK_IDAT:
data->inside_idat = 1;
data->idat_remain = len;
data->bit_count = 0;
grub_png_decode_image_data (data);
data->inside_idat = 0;
break;
case PNG_CHUNK_IEND:
if (data->is_16bit)
grub_png_convert_image (data);
return grub_errno;
default:
grub_file_seek (data->file, data->file->offset + len + 4);
}
if (grub_errno)
break;
if (data->file->offset != data->next_offset)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"png: chunk size error");
}
return grub_errno;
}
static grub_err_t
grub_video_reader_png (struct grub_video_bitmap **bitmap,
const char *filename)
{
grub_file_t file;
struct grub_png_data *data;
file = grub_buffile_open (filename, 0);
if (!file)
return grub_errno;
data = grub_zalloc (sizeof (*data));
if (data != NULL)
{
data->file = file;
data->bitmap = bitmap;
grub_png_decode_png (data);
grub_free (data->image_data);
grub_free (data);
}
if (grub_errno != GRUB_ERR_NONE)
{
grub_video_bitmap_destroy (*bitmap);
*bitmap = 0;
}
grub_file_close (file);
return grub_errno;
}
#if defined(PNG_DEBUG)
static grub_err_t
grub_cmd_pngtest (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_video_bitmap *bitmap = 0;
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
grub_video_reader_png (&bitmap, args[0]);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
grub_video_bitmap_destroy (bitmap);
return GRUB_ERR_NONE;
}
#endif
static struct grub_video_bitmap_reader png_reader = {
.extension = ".png",
.reader = grub_video_reader_png,
.next = 0
};
GRUB_MOD_INIT (png)
{
grub_video_bitmap_reader_register (&png_reader);
#if defined(PNG_DEBUG)
cmd = grub_register_command ("pngtest", grub_cmd_pngtest,
"FILE",
"Tests loading of PNG bitmap.");
#endif
}
GRUB_MOD_FINI (png)
{
#if defined(PNG_DEBUG)
grub_unregister_command (cmd);
#endif
grub_video_bitmap_reader_unregister (&png_reader);
}

View file

@ -0,0 +1,495 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/bitmap.h>
#include <grub/types.h>
#include <grub/normal.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/bufio.h>
/* Uncomment following define to enable TGA debug. */
//#define TGA_DEBUG
#if defined(TGA_DEBUG)
#define dump_int_field(x) grub_printf( #x " = %d (0x%04x)\n", x, x);
static grub_command_t cmd;
#endif
enum
{
GRUB_TGA_IMAGE_TYPE_NONE = 0,
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR = 1,
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR = 2,
GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE = 3,
GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR = 9,
GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR = 10,
GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE = 11,
};
enum
{
GRUB_TGA_COLOR_MAP_TYPE_NONE = 0,
GRUB_TGA_COLOR_MAP_TYPE_INCLUDED = 1
};
enum
{
GRUB_TGA_IMAGE_ORIGIN_RIGHT = 0x10,
GRUB_TGA_IMAGE_ORIGIN_TOP = 0x20
};
struct grub_tga_header
{
grub_uint8_t id_length;
grub_uint8_t color_map_type;
grub_uint8_t image_type;
/* Color Map Specification. */
grub_uint16_t color_map_first_index;
grub_uint16_t color_map_length;
grub_uint8_t color_map_bpp;
/* Image Specification. */
grub_uint16_t image_x_origin;
grub_uint16_t image_y_origin;
grub_uint16_t image_width;
grub_uint16_t image_height;
grub_uint8_t image_bpp;
grub_uint8_t image_descriptor;
} __attribute__ ((packed));
static grub_err_t
tga_load_truecolor_rle_R8G8B8 (struct grub_video_bitmap *bitmap,
struct grub_tga_header *header,
grub_file_t file)
{
unsigned int x;
unsigned int y;
grub_uint8_t type;
grub_uint8_t *ptr;
grub_uint8_t tmp[4]; /* Size should be max_bpp / 8. */
grub_uint8_t bytes_per_pixel;
bytes_per_pixel = header->image_bpp / 8;
for (y = 0; y < header->image_height; y++)
{
ptr = bitmap->data;
if ((header->image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
ptr += y * bitmap->mode_info.pitch;
else
ptr += (header->image_height - 1 - y) * bitmap->mode_info.pitch;
for (x = 0; x < header->image_width;)
{
if (grub_file_read (file, &type, sizeof (type)) != sizeof(type))
return grub_errno;
if (type & 0x80)
{
/* RLE-encoded packet. */
type &= 0x7f;
type++;
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
while (type)
{
if (x < header->image_width)
{
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr += 3;
}
type--;
x++;
}
}
else
{
/* RAW-encoded packet. */
type++;
while (type)
{
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
if (x < header->image_width)
{
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr += 3;
}
type--;
x++;
}
}
}
}
return GRUB_ERR_NONE;
}
static grub_err_t
tga_load_truecolor_rle_R8G8B8A8 (struct grub_video_bitmap *bitmap,
struct grub_tga_header *header,
grub_file_t file)
{
unsigned int x;
unsigned int y;
grub_uint8_t type;
grub_uint8_t *ptr;
grub_uint8_t tmp[4]; /* Size should be max_bpp / 8. */
grub_uint8_t bytes_per_pixel;
bytes_per_pixel = header->image_bpp / 8;
for (y = 0; y < header->image_height; y++)
{
ptr = bitmap->data;
if ((header->image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
ptr += y * bitmap->mode_info.pitch;
else
ptr += (header->image_height - 1 - y) * bitmap->mode_info.pitch;
for (x = 0; x < header->image_width;)
{
if (grub_file_read (file, &type, sizeof (type)) != sizeof(type))
return grub_errno;
if (type & 0x80)
{
/* RLE-encoded packet. */
type &= 0x7f;
type++;
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
while (type)
{
if (x < header->image_width)
{
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr[3] = tmp[3];
ptr += 4;
}
type--;
x++;
}
}
else
{
/* RAW-encoded packet. */
type++;
while (type)
{
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
if (x < header->image_width)
{
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr[3] = tmp[3];
ptr += 4;
}
type--;
x++;
}
}
}
}
return GRUB_ERR_NONE;
}
static grub_err_t
tga_load_truecolor_R8G8B8 (struct grub_video_bitmap *bitmap,
struct grub_tga_header *header,
grub_file_t file)
{
unsigned int x;
unsigned int y;
grub_uint8_t *ptr;
grub_uint8_t tmp[4]; /* Size should be max_bpp / 8. */
grub_uint8_t bytes_per_pixel;
bytes_per_pixel = header->image_bpp / 8;
for (y = 0; y < header->image_height; y++)
{
ptr = bitmap->data;
if ((header->image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
ptr += y * bitmap->mode_info.pitch;
else
ptr += (header->image_height - 1 - y) * bitmap->mode_info.pitch;
for (x = 0; x < header->image_width; x++)
{
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr += 3;
}
}
return GRUB_ERR_NONE;
}
static grub_err_t
tga_load_truecolor_R8G8B8A8 (struct grub_video_bitmap *bitmap,
struct grub_tga_header *header,
grub_file_t file)
{
unsigned int x;
unsigned int y;
grub_uint8_t *ptr;
grub_uint8_t tmp[4]; /* Size should be max_bpp / 8. */
grub_uint8_t bytes_per_pixel;
bytes_per_pixel = header->image_bpp / 8;
for (y = 0; y < header->image_height; y++)
{
ptr = bitmap->data;
if ((header->image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0)
ptr += y * bitmap->mode_info.pitch;
else
ptr += (header->image_height - 1 - y) * bitmap->mode_info.pitch;
for (x = 0; x < header->image_width; x++)
{
if (grub_file_read (file, &tmp[0], bytes_per_pixel)
!= bytes_per_pixel)
return grub_errno;
ptr[0] = tmp[2];
ptr[1] = tmp[1];
ptr[2] = tmp[0];
ptr[3] = tmp[3];
ptr += 4;
}
}
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_reader_tga (struct grub_video_bitmap **bitmap,
const char *filename)
{
grub_file_t file;
grub_ssize_t pos;
struct grub_tga_header header;
int has_alpha;
file = grub_buffile_open (filename, 0);
if (! file)
return grub_errno;
/* TGA Specification states that we SHOULD start by reading
ID from end of file, but we really don't care about that as we are
not going to support developer area & extensions at this point. */
/* Read TGA header from beginning of file. */
if (grub_file_read (file, &header, sizeof (header))
!= sizeof (header))
{
grub_file_close (file);
return grub_errno;
}
/* Skip ID field. */
pos = grub_file_tell (file);
pos += header.id_length;
grub_file_seek (file, pos);
if (grub_errno != GRUB_ERR_NONE)
{
grub_file_close (file);
return grub_errno;
}
#if defined(TGA_DEBUG)
grub_printf("tga: header\n");
dump_int_field(header.id_length);
dump_int_field(header.color_map_type);
dump_int_field(header.image_type);
dump_int_field(header.color_map_first_index);
dump_int_field(header.color_map_length);
dump_int_field(header.color_map_bpp);
dump_int_field(header.image_x_origin);
dump_int_field(header.image_y_origin);
dump_int_field(header.image_width);
dump_int_field(header.image_height);
dump_int_field(header.image_bpp);
dump_int_field(header.image_descriptor);
#endif
/* Check that bitmap encoding is supported. */
switch (header.image_type)
{
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR:
case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR:
break;
default:
grub_file_close (file);
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"unsupported bitmap format (unknown encoding)");
}
/* Check that bitmap depth is supported. */
switch (header.image_bpp)
{
case 24:
has_alpha = 0;
break;
case 32:
has_alpha = 1;
break;
default:
grub_file_close (file);
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"unsupported bitmap format (bpp=%d)",
header.image_bpp);
}
/* Allocate bitmap. If there is alpha information store it too. */
if (has_alpha)
{
grub_video_bitmap_create (bitmap, header.image_width,
header.image_height,
GRUB_VIDEO_BLIT_FORMAT_RGBA_8888);
if (grub_errno != GRUB_ERR_NONE)
{
grub_file_close (file);
return grub_errno;
}
/* Load bitmap data. */
switch (header.image_type)
{
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR:
tga_load_truecolor_R8G8B8A8 (*bitmap, &header, file);
break;
case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR:
tga_load_truecolor_rle_R8G8B8A8 (*bitmap, &header, file);
break;
}
}
else
{
grub_video_bitmap_create (bitmap, header.image_width,
header.image_height,
GRUB_VIDEO_BLIT_FORMAT_RGB_888);
if (grub_errno != GRUB_ERR_NONE)
{
grub_file_close (file);
return grub_errno;
}
/* Load bitmap data. */
switch (header.image_type)
{
case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR:
tga_load_truecolor_R8G8B8 (*bitmap, &header, file);
break;
case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR:
tga_load_truecolor_rle_R8G8B8 (*bitmap, &header, file);
break;
}
}
/* If there was a loading problem, destroy bitmap. */
if (grub_errno != GRUB_ERR_NONE)
{
grub_video_bitmap_destroy (*bitmap);
*bitmap = 0;
}
grub_file_close (file);
return grub_errno;
}
#if defined(TGA_DEBUG)
static grub_err_t
grub_cmd_tgatest (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_video_bitmap *bitmap = 0;
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
grub_video_reader_tga (&bitmap, args[0]);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
grub_video_bitmap_destroy (bitmap);
return GRUB_ERR_NONE;
}
#endif
static struct grub_video_bitmap_reader tga_reader = {
.extension = ".tga",
.reader = grub_video_reader_tga,
.next = 0
};
GRUB_MOD_INIT(tga)
{
grub_video_bitmap_reader_register (&tga_reader);
#if defined(TGA_DEBUG)
cmd = grub_register_command ("tgatest", grub_cmd_tgatest,
"FILE", "Tests loading of TGA bitmap.");
#endif
}
GRUB_MOD_FINI(tga)
{
#if defined(TGA_DEBUG)
grub_unregister_command (cmd);
#endif
grub_video_bitmap_reader_unregister (&tga_reader);
}

230
grub-core/video/sm712.c Normal file
View file

@ -0,0 +1,230 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#define grub_video_render_target grub_video_fbrender_target
#include <grub/err.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/video.h>
#include <grub/video_fb.h>
#include <grub/pci.h>
static struct
{
struct grub_video_mode_info mode_info;
struct grub_video_render_target *render_target;
unsigned int bytes_per_scan_line;
unsigned int bytes_per_pixel;
grub_uint8_t *ptr;
int index_color_mode;
int mapped;
grub_uint32_t base;
grub_pci_device_t dev;
} framebuffer;
static grub_err_t
grub_video_sm712_video_init (void)
{
/* Reset frame buffer. */
grub_memset (&framebuffer, 0, sizeof(framebuffer));
return grub_video_fb_init ();
}
static grub_err_t
grub_video_sm712_video_fini (void)
{
if (framebuffer.mapped)
grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr,
1024 * 600 * 2);
return grub_video_fb_fini ();
}
static grub_err_t
grub_video_sm712_setup (unsigned int width, unsigned int height,
unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused)))
{
int depth;
grub_err_t err;
int found = 0;
auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)));
int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)))
{
grub_pci_address_t addr;
grub_uint32_t class;
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
class = grub_pci_read (addr);
if (((class >> 16) & 0xffff) != 0x0300 || pciid != 0x0712126f)
return 0;
found = 1;
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
framebuffer.base = grub_pci_read (addr);
framebuffer.dev = dev;
return 1;
}
/* Decode depth from mode_type. If it is zero, then autodetect. */
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
if ((width != 1024 && width != 0) || (height != 600 && height != 0)
|| (depth != 16 && depth != 0))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"Only 1024x600x16 is supported");
grub_pci_iterate (find_card);
if (!found)
return grub_error (GRUB_ERR_IO, "Couldn't find graphics card");
if (found && framebuffer.base == 0)
{
/* FIXME: change framebuffer base */
}
/* Fill mode info details. */
framebuffer.mode_info.width = 1024;
framebuffer.mode_info.height = 600;
framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
framebuffer.mode_info.bpp = 16;
framebuffer.mode_info.bytes_per_pixel = 2;
framebuffer.mode_info.pitch = 1024 * 2;
framebuffer.mode_info.number_of_colors = 256;
framebuffer.mode_info.red_mask_size = 5;
framebuffer.mode_info.red_field_pos = 11;
framebuffer.mode_info.green_mask_size = 6;
framebuffer.mode_info.green_field_pos = 5;
framebuffer.mode_info.blue_mask_size = 5;
framebuffer.mode_info.blue_field_pos = 0;
framebuffer.mode_info.reserved_mask_size = 0;
framebuffer.mode_info.reserved_field_pos = 0;
framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info);
/* We can safely discard volatile attribute. */
framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev,
framebuffer.base,
1024 * 600 * 2);
framebuffer.mapped = 1;
err = grub_video_fb_create_render_target_from_pointer (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr);
if (err)
return err;
err = grub_video_fb_set_active_render_target (framebuffer.render_target);
if (err)
return err;
/* Copy default palette to initialize emulated palette. */
err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
grub_video_fbstd_colors);
return err;
}
static grub_err_t
grub_video_sm712_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
if (framebuffer.index_color_mode)
{
/* TODO: Implement setting indexed color mode palette to hardware. */
}
/* Then set color to emulated palette. */
return grub_video_fb_set_palette (start, count, palette_data);
}
static grub_err_t
grub_video_sm712_swap_buffers (void)
{
/* TODO: Implement buffer swapping. */
return GRUB_ERR_NONE;
}
static grub_err_t
grub_video_sm712_set_active_render_target (struct grub_video_render_target *target)
{
if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
target = framebuffer.render_target;
return grub_video_fb_set_active_render_target (target);
}
static grub_err_t
grub_video_sm712_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuf)
{
grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
*framebuf = (char *) framebuffer.ptr;
grub_video_fb_fini ();
return GRUB_ERR_NONE;
}
static struct grub_video_adapter grub_video_sm712_adapter =
{
.name = "SM712 Video Driver",
.id = GRUB_VIDEO_DRIVER_SM712,
.init = grub_video_sm712_video_init,
.fini = grub_video_sm712_video_fini,
.setup = grub_video_sm712_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_sm712_get_info_and_fini,
.set_palette = grub_video_sm712_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
.get_viewport = grub_video_fb_get_viewport,
.map_color = grub_video_fb_map_color,
.map_rgb = grub_video_fb_map_rgb,
.map_rgba = grub_video_fb_map_rgba,
.unmap_color = grub_video_fb_unmap_color,
.fill_rect = grub_video_fb_fill_rect,
.blit_bitmap = grub_video_fb_blit_bitmap,
.blit_render_target = grub_video_fb_blit_render_target,
.scroll = grub_video_fb_scroll,
.swap_buffers = grub_video_sm712_swap_buffers,
.create_render_target = grub_video_fb_create_render_target,
.delete_render_target = grub_video_fb_delete_render_target,
.set_active_render_target = grub_video_sm712_set_active_render_target,
.get_active_render_target = grub_video_fb_get_active_render_target,
.next = 0
};
GRUB_MOD_INIT(video_sm712)
{
grub_video_register (&grub_video_sm712_adapter);
}
GRUB_MOD_FINI(video_sm712)
{
grub_video_unregister (&grub_video_sm712_adapter);
}

706
grub-core/video/video.c Normal file
View file

@ -0,0 +1,706 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008,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 <http://www.gnu.org/licenses/>.
*/
#include <grub/video.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
/* The list of video adapters registered to system. */
static grub_video_adapter_t grub_video_adapter_list;
/* Active video adapter. */
static grub_video_adapter_t grub_video_adapter_active;
/* Register video driver. */
void
grub_video_register (grub_video_adapter_t adapter)
{
adapter->next = grub_video_adapter_list;
grub_video_adapter_list = adapter;
}
/* Unregister video driver. */
void
grub_video_unregister (grub_video_adapter_t adapter)
{
grub_video_adapter_t *p, q;
for (p = &grub_video_adapter_list, q = *p; q; p = &(q->next), q = q->next)
if (q == adapter)
{
*p = q->next;
break;
}
}
/* Iterate thru all registered video drivers. */
void
grub_video_iterate (int (*hook) (grub_video_adapter_t adapter))
{
grub_video_adapter_t p;
for (p = grub_video_adapter_list; p; p = p->next)
if (hook (p))
break;
}
/* Restore back to initial mode (where applicable). */
grub_err_t
grub_video_restore (void)
{
if (grub_video_adapter_active)
{
grub_video_adapter_active->fini ();
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
grub_video_adapter_active = 0;
}
return GRUB_ERR_NONE;
}
/* Get information about active video mode. */
grub_err_t
grub_video_get_info (struct grub_video_mode_info *mode_info)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
/* If mode_info is NULL just report that video adapter is active. */
if (! mode_info)
{
grub_errno = GRUB_ERR_NONE;
return grub_errno;
}
return grub_video_adapter_active->get_info (mode_info);
}
grub_video_driver_id_t
grub_video_get_driver_id (void)
{
if (! grub_video_adapter_active)
return GRUB_VIDEO_DRIVER_NONE;
return grub_video_adapter_active->id;
}
/* Get information about active video mode. */
grub_err_t
grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info,
void **framebuffer)
{
grub_err_t err;
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
err = grub_video_adapter_active->get_info_and_fini (mode_info, framebuffer);
if (err)
return err;
grub_video_adapter_active = 0;
return GRUB_ERR_NONE;
}
/* Determine optimized blitting formation for specified video mode info. */
enum grub_video_blit_format
grub_video_get_blit_format (struct grub_video_mode_info *mode_info)
{
/* Check if we have any known 32 bit modes. */
if (mode_info->bpp == 32)
{
if ((mode_info->red_mask_size == 8)
&& (mode_info->red_field_pos == 16)
&& (mode_info->green_mask_size == 8)
&& (mode_info->green_field_pos == 8)
&& (mode_info->blue_mask_size == 8)
&& (mode_info->blue_field_pos == 0))
{
return GRUB_VIDEO_BLIT_FORMAT_BGRA_8888;
}
else if ((mode_info->red_mask_size == 8)
&& (mode_info->red_field_pos == 0)
&& (mode_info->green_mask_size == 8)
&& (mode_info->green_field_pos == 8)
&& (mode_info->blue_mask_size == 8)
&& (mode_info->blue_field_pos == 16))
{
return GRUB_VIDEO_BLIT_FORMAT_RGBA_8888;
}
}
/* Check if we have any known 24 bit modes. */
else if (mode_info->bpp == 24)
{
if ((mode_info->red_mask_size == 8)
&& (mode_info->red_field_pos == 16)
&& (mode_info->green_mask_size == 8)
&& (mode_info->green_field_pos == 8)
&& (mode_info->blue_mask_size == 8)
&& (mode_info->blue_field_pos == 0))
{
return GRUB_VIDEO_BLIT_FORMAT_BGR_888;
}
else if ((mode_info->red_mask_size == 8)
&& (mode_info->red_field_pos == 0)
&& (mode_info->green_mask_size == 8)
&& (mode_info->green_field_pos == 8)
&& (mode_info->blue_mask_size == 8)
&& (mode_info->blue_field_pos == 16))
{
return GRUB_VIDEO_BLIT_FORMAT_RGB_888;
}
}
/* Check if we have any known 16 bit modes. */
else if (mode_info->bpp == 16)
{
if ((mode_info->red_mask_size == 5)
&& (mode_info->red_field_pos == 11)
&& (mode_info->green_mask_size == 6)
&& (mode_info->green_field_pos == 5)
&& (mode_info->blue_mask_size == 5)
&& (mode_info->blue_field_pos == 0))
{
return GRUB_VIDEO_BLIT_FORMAT_BGR_565;
}
else if ((mode_info->red_mask_size == 5)
&& (mode_info->red_field_pos == 0)
&& (mode_info->green_mask_size == 6)
&& (mode_info->green_field_pos == 5)
&& (mode_info->blue_mask_size == 5)
&& (mode_info->blue_field_pos == 11))
{
return GRUB_VIDEO_BLIT_FORMAT_RGB_565;
}
}
else if (mode_info->bpp == 1)
return GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
/* Backup route. Unknown format. */
/* If there are more than 8 bits per color, assume RGB(A) mode. */
if (mode_info->bpp > 8)
{
if (mode_info->reserved_mask_size > 0)
{
return GRUB_VIDEO_BLIT_FORMAT_RGBA;
}
else
{
return GRUB_VIDEO_BLIT_FORMAT_RGB;
}
}
/* Assume as indexcolor mode. */
return GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR;
}
/* Set new indexed color palette entries. */
grub_err_t
grub_video_set_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->set_palette (start, count, palette_data);
}
/* Get indexed color palette entries. */
grub_err_t
grub_video_get_palette (unsigned int start, unsigned int count,
struct grub_video_palette_data *palette_data)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->get_palette (start, count, palette_data);
}
/* Set viewport dimensions. */
grub_err_t
grub_video_set_viewport (unsigned int x, unsigned int y,
unsigned int width, unsigned int height)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
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)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->get_viewport (x, y, width, height);
}
/* Map color name to adapter specific color. */
grub_video_color_t
grub_video_map_color (grub_uint32_t color_name)
{
if (! grub_video_adapter_active)
return 0;
return grub_video_adapter_active->map_color (color_name);
}
/* Map RGB value to adapter specific color. */
grub_video_color_t
grub_video_map_rgb (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue)
{
if (! grub_video_adapter_active)
return 0;
return grub_video_adapter_active->map_rgb (red, green, blue);
}
/* Map RGBA value to adapter specific color. */
grub_video_color_t
grub_video_map_rgba (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue,
grub_uint8_t alpha)
{
if (! grub_video_adapter_active)
return 0;
return grub_video_adapter_active->map_rgba (red, green, blue, alpha);
}
/* Unmap video color back to RGBA components. */
grub_err_t
grub_video_unmap_color (grub_video_color_t color, grub_uint8_t *red,
grub_uint8_t *green, grub_uint8_t *blue,
grub_uint8_t *alpha)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->unmap_color (color,
red,
green,
blue,
alpha);
}
/* 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)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->fill_rect (color, x, y, width, height);
}
/* Blit bitmap to screen. */
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)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->blit_bitmap (bitmap, oper, x, y,
offset_x, offset_y,
width, height);
}
/* Blit render target to active render target. */
grub_err_t
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)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->blit_render_target (target, oper, x, y,
offset_x, offset_y,
width, height);
}
/* Scroll viewport and fill new areas with specified color. */
grub_err_t
grub_video_scroll (grub_video_color_t color, int dx, int dy)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->scroll (color, dx, dy);
}
/* Swap buffers (swap active render target). */
grub_err_t
grub_video_swap_buffers (void)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->swap_buffers ();
}
/* Create new render target. */
grub_err_t
grub_video_create_render_target (struct grub_video_render_target **result,
unsigned int width, unsigned int height,
unsigned int mode_type)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->create_render_target (result,
width, height,
mode_type);
}
/* Delete render target. */
grub_err_t
grub_video_delete_render_target (struct grub_video_render_target *target)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->delete_render_target (target);
}
/* Set active render target. */
grub_err_t
grub_video_set_active_render_target (struct grub_video_render_target *target)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->set_active_render_target (target);
}
/* Get active render target. */
grub_err_t
grub_video_get_active_render_target (struct grub_video_render_target **target)
{
if (! grub_video_adapter_active)
return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
return grub_video_adapter_active->get_active_render_target (target);
}
/* Parse <width>x<height>[x<depth>]*/
static grub_err_t
parse_modespec (const char *current_mode, int *width, int *height, int *depth)
{
const char *value;
const char *param = current_mode;
*width = *height = *depth = -1;
if (grub_strcmp (param, "auto") == 0)
{
*width = *height = 0;
return GRUB_ERR_NONE;
}
/* Find width value. */
value = param;
param = grub_strchr(param, 'x');
if (param == NULL)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Invalid mode: %s\n",
current_mode);
param++;
*width = grub_strtoul (value, 0, 0);
if (grub_errno != GRUB_ERR_NONE)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Invalid mode: %s\n",
current_mode);
/* Find height value. */
value = param;
param = grub_strchr(param, 'x');
if (param == NULL)
{
*height = grub_strtoul (value, 0, 0);
if (grub_errno != GRUB_ERR_NONE)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Invalid mode: %s\n",
current_mode);
}
else
{
/* We have optional color depth value. */
param++;
*height = grub_strtoul (value, 0, 0);
if (grub_errno != GRUB_ERR_NONE)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Invalid mode: %s\n",
current_mode);
/* Convert color depth value. */
value = param;
*depth = grub_strtoul (value, 0, 0);
if (grub_errno != GRUB_ERR_NONE)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Invalid mode: %s\n",
current_mode);
}
return GRUB_ERR_NONE;
}
grub_err_t
grub_video_set_mode (const char *modestring,
unsigned int modemask,
unsigned int modevalue)
{
char *tmp;
char *next_mode;
char *current_mode;
char *modevar;
modevalue &= modemask;
/* Take copy of env.var. as we don't want to modify that. */
modevar = grub_strdup (modestring);
/* Initialize next mode. */
next_mode = modevar;
if (! modevar)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate space for local modevar copy");
if (grub_memcmp (next_mode, "keep", sizeof ("keep")) == 0
|| grub_memcmp (next_mode, "keep,", sizeof ("keep,") - 1) == 0
|| grub_memcmp (next_mode, "keep;", sizeof ("keep;") - 1) == 0)
{
int suitable = 1;
grub_err_t err;
if (grub_video_adapter_active)
{
struct grub_video_mode_info mode_info;
grub_memset (&mode_info, 0, sizeof (mode_info));
err = grub_video_get_info (&mode_info);
if (err)
{
suitable = 0;
grub_errno = GRUB_ERR_NONE;
}
if ((mode_info.mode_type & modemask) != modevalue)
suitable = 0;
}
else if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) != 0)
&& ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) == 0))
suitable = 0;
if (suitable)
{
grub_free (modevar);
return GRUB_ERR_NONE;
}
next_mode += sizeof ("keep") - 1;
if (! *next_mode)
{
grub_free (modevar);
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"no suitable mode found");
}
/* Skip separator. */
next_mode++;
}
/* De-activate last set video adapter. */
if (grub_video_adapter_active)
{
/* Finalize adapter. */
grub_video_adapter_active->fini ();
if (grub_errno != GRUB_ERR_NONE)
grub_errno = GRUB_ERR_NONE;
/* Mark active adapter as not set. */
grub_video_adapter_active = 0;
}
/* Loop until all modes has been tested out. */
while (next_mode != NULL)
{
int width = -1;
int height = -1;
int depth = -1;
grub_err_t err;
unsigned int flags = modevalue;
unsigned int flagmask = modemask;
/* Use last next_mode as current mode. */
tmp = next_mode;
/* Save position of next mode and separate modes. */
for (; *next_mode; next_mode++)
if (*next_mode == ',' || *next_mode == ';')
break;
if (*next_mode)
{
*next_mode = 0;
next_mode++;
}
else
next_mode = 0;
/* Skip whitespace. */
while (grub_isspace (*tmp))
tmp++;
/* Initialize token holders. */
current_mode = tmp;
/* XXX: we assume that we're in pure text mode if
no video mode is initialized. Is it always true? */
if (grub_strcmp (current_mode, "text") == 0)
{
struct grub_video_mode_info mode_info;
grub_memset (&mode_info, 0, sizeof (mode_info));
if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) == 0)
|| ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) != 0))
{
/* Valid mode found from adapter, and it has been activated.
Specify it as active adapter. */
grub_video_adapter_active = NULL;
/* Free memory. */
grub_free (modevar);
return GRUB_ERR_NONE;
}
}
err = parse_modespec (current_mode, &width, &height, &depth);
if (err)
{
/* Free memory before returning. */
grub_free (modevar);
return err;
}
/* Try out video mode. */
/* If user requested specific depth check if this depth is supported. */
if (depth != -1 && (flagmask & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
&&
(((flags & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
!= ((depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
& GRUB_VIDEO_MODE_TYPE_DEPTH_MASK))))
continue;
if (depth != -1)
{
flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
& GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
flagmask |= GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
}
/* Try to initialize requested mode. Ignore any errors. */
grub_video_adapter_t p;
/* Loop thru all possible video adapter trying to find requested mode. */
for (p = grub_video_adapter_list; p; p = p->next)
{
struct grub_video_mode_info mode_info;
grub_memset (&mode_info, 0, sizeof (mode_info));
/* Try to initialize adapter, if it fails, skip to next adapter. */
err = p->init ();
if (err != GRUB_ERR_NONE)
{
grub_errno = GRUB_ERR_NONE;
continue;
}
/* Try to initialize video mode. */
err = p->setup (width, height, flags, flagmask);
if (err != GRUB_ERR_NONE)
{
p->fini ();
grub_errno = GRUB_ERR_NONE;
continue;
}
err = p->get_info (&mode_info);
if (err != GRUB_ERR_NONE)
{
p->fini ();
grub_errno = GRUB_ERR_NONE;
continue;
}
flags = mode_info.mode_type & ~GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
flags |= (mode_info.bpp << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
& GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
/* Check that mode is suitable for upper layer. */
if ((flags & GRUB_VIDEO_MODE_TYPE_PURE_TEXT)
? (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) != 0)
&& ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) == 0))
: ((flags & modemask) != modevalue))
{
p->fini ();
grub_errno = GRUB_ERR_NONE;
continue;
}
/* Valid mode found from adapter, and it has been activated.
Specify it as active adapter. */
grub_video_adapter_active = p;
/* Free memory. */
grub_free (modevar);
return GRUB_ERR_NONE;
}
}
/* Free memory. */
grub_free (modevar);
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"no suitable mode found");
}
/* Initialize Video API module. */
GRUB_MOD_INIT(video)
{
}
/* Finalize Video API module. */
GRUB_MOD_FINI(video)
{
}