diff options
Diffstat (limited to 'grub-core/video/readers/tga.c')
-rw-r--r-- | grub-core/video/readers/tga.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/grub-core/video/readers/tga.c b/grub-core/video/readers/tga.c new file mode 100644 index 0000000..7cb9d1d --- /dev/null +++ b/grub-core/video/readers/tga.c @@ -0,0 +1,511 @@ +/* + * 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> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Uncomment following define to enable TGA debug. */ +//#define TGA_DEBUG + +#define dump_int_field(x) grub_dprintf ("tga", #x " = %d (0x%04x)\n", x, x); +#if defined(TGA_DEBUG) +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; +} GRUB_PACKED; + +struct tga_data +{ + struct grub_tga_header hdr; + int uses_rle; + int pktlen; + int bpp; + int is_rle; + grub_uint8_t pixel[4]; + grub_uint8_t palette[256][3]; + struct grub_video_bitmap *bitmap; + grub_file_t file; + unsigned image_width; + unsigned image_height; +}; + +static grub_err_t +fetch_pixel (struct tga_data *data) +{ + if (!data->uses_rle) + { + if (grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + return GRUB_ERR_NONE; + } + if (!data->pktlen) + { + grub_uint8_t type; + if (grub_file_read (data->file, &type, sizeof (type)) != sizeof(type)) + return grub_errno; + data->is_rle = !!(type & 0x80); + data->pktlen = (type & 0x7f) + 1; + if (data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + } + if (!data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + data->pktlen--; + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_palette (struct tga_data *data) +{ + grub_size_t len = grub_le_to_cpu32 (data->hdr.color_map_length) * 3; + + if (len > sizeof (data->palette)) + len = sizeof (data->palette); + + if (grub_file_read (data->file, &data->palette, len) + != (grub_ssize_t) len) + return grub_errno; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + { + grub_size_t i; + for (i = 0; 3 * i < len; i++) + { + grub_uint8_t t; + t = data->palette[i][0]; + data->palette[i][0] = data->palette[i][2]; + data->palette[i][2] = t; + } + } +#endif + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_index_color (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + + ptr[0] = data->palette[data->pixel[0]][0]; + ptr[1] = data->palette[data->pixel[0]][1]; + ptr[2] = data->palette[data->pixel[0]][2]; + + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_grayscale (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[0]; + ptr[2] = data->pixel[0]; + + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_truecolor_R8G8B8 (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[2]; +#else + ptr[0] = data->pixel[2]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[0]; +#endif + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_truecolor_R8G8B8A8 (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[2]; + ptr[3] = data->pixel[3]; +#else + ptr[0] = data->pixel[2]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[0]; + ptr[3] = data->pixel[3]; +#endif + + ptr += 4; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_reader_tga (struct grub_video_bitmap **bitmap, + const char *filename) +{ + grub_ssize_t pos; + struct tga_data data; + + grub_memset (&data, 0, sizeof (data)); + + data.file = grub_buffile_open (filename, GRUB_FILE_TYPE_PIXMAP, 0); + if (! data.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 (data.file, &data.hdr, sizeof (data.hdr)) + != sizeof (data.hdr)) + { + grub_file_close (data.file); + return grub_errno; + } + + /* Skip ID field. */ + pos = grub_file_tell (data.file); + pos += data.hdr.id_length; + grub_file_seek (data.file, pos); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + grub_dprintf("tga", "tga: header\n"); + dump_int_field(data.hdr.id_length); + dump_int_field(data.hdr.color_map_type); + dump_int_field(data.hdr.image_type); + dump_int_field(data.hdr.color_map_first_index); + dump_int_field(data.hdr.color_map_length); + dump_int_field(data.hdr.color_map_bpp); + dump_int_field(data.hdr.image_x_origin); + dump_int_field(data.hdr.image_y_origin); + dump_int_field(data.hdr.image_width); + dump_int_field(data.hdr.image_height); + dump_int_field(data.hdr.image_bpp); + dump_int_field(data.hdr.image_descriptor); + + data.image_width = grub_le_to_cpu16 (data.hdr.image_width); + data.image_height = grub_le_to_cpu16 (data.hdr.image_height); + + /* Check that bitmap encoding is supported. */ + switch (data.hdr.image_type) + { + case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR: + data.uses_rle = 1; + break; + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR: + data.uses_rle = 0; + break; + + default: + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (unknown encoding %d)", data.hdr.image_type); + } + + data.bpp = data.hdr.image_bpp / 8; + + /* Check that bitmap depth is supported. */ + switch (data.hdr.image_type) + { + case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE: + if (data.hdr.image_bpp != 8) + { + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_grayscale (&data); + break; + + case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR: + if (data.hdr.image_bpp != 8 + || data.hdr.color_map_bpp != 24 + || data.hdr.color_map_first_index != 0) + { + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_palette (&data); + tga_load_index_color (&data); + break; + + case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR: + switch (data.hdr.image_bpp) + { + case 24: + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_truecolor_R8G8B8 (&data); + break; + + case 32: + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_truecolor_R8G8B8A8 (&data); + break; + + default: + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + } + + /* If there was a loading problem, destroy bitmap. */ + if (grub_errno != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (*bitmap); + *bitmap = 0; + } + + grub_file_close (data.file); + return grub_errno; +} + +#if defined(TGA_DEBUG) +static grub_err_t +grub_cmd_tgatest (grub_command_t cmd_d __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); +} |