| /* |
| * 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> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| /* Uncomment following define to enable JPEG debug. */ |
| //#define JPEG_DEBUG |
| |
| #define JPEG_ESC_CHAR 0xFF |
| |
| #define JPEG_SAMPLING_1x1 0x11 |
| |
| enum |
| { |
| JPEG_MARKER_SOF0 = 0xc0, |
| JPEG_MARKER_DHT = 0xc4, |
| JPEG_MARKER_SOI = 0xd8, |
| JPEG_MARKER_EOI = 0xd9, |
| JPEG_MARKER_RST0 = 0xd0, |
| JPEG_MARKER_RST1 = 0xd1, |
| JPEG_MARKER_RST2 = 0xd2, |
| JPEG_MARKER_RST3 = 0xd3, |
| JPEG_MARKER_RST4 = 0xd4, |
| JPEG_MARKER_RST5 = 0xd5, |
| JPEG_MARKER_RST6 = 0xd6, |
| JPEG_MARKER_RST7 = 0xd7, |
| JPEG_MARKER_SOS = 0xda, |
| JPEG_MARKER_DQT = 0xdb, |
| JPEG_MARKER_DRI = 0xdd, |
| }; |
| |
| #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; |
| grub_uint8_t *bitmap_ptr; |
| |
| unsigned image_width; |
| unsigned 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; |
| |
| unsigned log_vs, log_hs; |
| int dri; |
| unsigned r1; |
| |
| int dc_value[3]; |
| |
| int color_components; |
| |
| 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; |
| unsigned i; |
| |
| code = 0; |
| for (i = 0; i < ARRAY_SIZE (data->huff_maxval[id]); 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, n, base, ofs; |
| grub_uint32_t next_marker; |
| grub_uint8_t count[16]; |
| unsigned i; |
| |
| next_marker = data->file->offset; |
| next_marker += grub_jpeg_get_word (data); |
| |
| while (data->file->offset + sizeof (count) + 1 <= next_marker) |
| { |
| id = grub_jpeg_get_byte (data); |
| ac = (id >> 4) & 1; |
| 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 < ARRAY_SIZE (count); 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 < ARRAY_SIZE (count); 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); |
| |
| while (data->file->offset + sizeof (data->quan_table[id]) + 1 |
| <= next_marker) |
| { |
| 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], |
| sizeof (data->quan_table[id])) |
| != sizeof (data->quan_table[id])) |
| 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 != 1 && cc != 3) |
| return grub_error (GRUB_ERR_BAD_FILE_TYPE, |
| "jpeg: component count must be 1 or 3"); |
| data->color_components = cc; |
| |
| 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) |
| { |
| grub_uint8_t vs, hs; |
| vs = ss & 0xF; /* Vertical sampling. */ |
| hs = ss >> 4; /* Horizontal sampling. */ |
| if ((vs > 2) || (hs > 2) || (vs == 0) || (hs == 0)) |
| return grub_error (GRUB_ERR_BAD_FILE_TYPE, |
| "jpeg: sampling method not supported"); |
| data->log_vs = (vs == 2); |
| data->log_hs = (hs == 2); |
| } |
| 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 grub_err_t |
| grub_jpeg_decode_dri (struct grub_jpeg_data *data) |
| { |
| if (grub_jpeg_get_word (data) != 4) |
| return grub_error (GRUB_ERR_BAD_FILE_TYPE, |
| "jpeg: DRI marker length must be 4"); |
| |
| data->dri = grub_jpeg_get_word (data); |
| |
| 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 h1, h2, qt; |
| unsigned pos; |
| |
| 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 < ARRAY_SIZE (data->quan_table[qt])) |
| { |
| 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; |
| #ifdef GRUB_CPU_WORDS_BIGENDIAN |
| rgb[2] = dd; |
| #else |
| *(rgb++) = dd; |
| #endif |
| |
| /* Green */ |
| dd = yy - ((cb * CONST (0.34414) + cr * CONST (0.71414)) >> SHIFT_BITS); |
| if (dd < 0) |
| dd = 0; |
| if (dd > 255) |
| dd = 255; |
| #ifdef GRUB_CPU_WORDS_BIGENDIAN |
| rgb[1] = dd; |
| #else |
| *(rgb++) = dd; |
| #endif |
| |
| /* Blue */ |
| dd = yy + ((cb * CONST (1.772)) >> SHIFT_BITS); |
| if (dd < 0) |
| dd = 0; |
| if (dd > 255) |
| dd = 255; |
| #ifdef GRUB_CPU_WORDS_BIGENDIAN |
| rgb[0] = dd; |
| rgb += 3; |
| #else |
| *(rgb++) = dd; |
| #endif |
| } |
| |
| static grub_err_t |
| grub_jpeg_decode_sos (struct grub_jpeg_data *data) |
| { |
| int i, cc; |
| 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 && cc != 1) |
| return grub_error (GRUB_ERR_BAD_FILE_TYPE, |
| "jpeg: component count must be 1 or 3"); |
| data->color_components = cc; |
| |
| 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->bitmap_ptr = (*data->bitmap)->data; |
| return GRUB_ERR_NONE; |
| } |
| |
| static grub_err_t |
| grub_jpeg_decode_data (struct grub_jpeg_data *data) |
| { |
| unsigned c1, vb, hb, nr1, nc1; |
| int rst = data->dri; |
| |
| vb = 8 << data->log_vs; |
| hb = 8 << data->log_hs; |
| nr1 = (data->image_height + vb - 1) >> (3 + data->log_vs); |
| nc1 = (data->image_width + hb - 1) >> (3 + data->log_hs); |
| |
| for (; data->r1 < nr1 && (!data->dri || rst); |
| data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) |
| for (c1 = 0; c1 < nc1 && (!data->dri || rst); |
| c1++, rst--, data->bitmap_ptr += hb * 3) |
| { |
| unsigned r2, c2, nr2, nc2; |
| grub_uint8_t *ptr2; |
| |
| for (r2 = 0; r2 < (1U << data->log_vs); r2++) |
| for (c2 = 0; c2 < (1U << data->log_hs); c2++) |
| grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); |
| |
| if (data->color_components >= 3) |
| { |
| grub_jpeg_decode_du (data, 1, data->cbdu); |
| grub_jpeg_decode_du (data, 2, data->crdu); |
| } |
| |
| if (grub_errno) |
| return grub_errno; |
| |
| nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb; |
| nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb; |
| |
| ptr2 = data->bitmap_ptr; |
| for (r2 = 0; r2 < nr2; r2++, ptr2 += (data->image_width - nc2) * 3) |
| for (c2 = 0; c2 < nc2; c2++, ptr2 += 3) |
| { |
| unsigned i0; |
| int yy; |
| |
| i0 = (r2 >> data->log_vs) * 8 + (c2 >> data->log_hs); |
| yy = data->ydu[(r2 / 8) * 2 + (c2 / 8)][(r2 % 8) * 8 + (c2 % 8)]; |
| |
| if (data->color_components >= 3) |
| { |
| int cr, cb; |
| cr = data->crdu[i0]; |
| cb = data->cbdu[i0]; |
| grub_jpeg_ycrcb_to_rgb (yy, cr, cb, ptr2); |
| } |
| else |
| { |
| ptr2[0] = yy; |
| ptr2[1] = yy; |
| ptr2[2] = yy; |
| } |
| } |
| } |
| |
| return grub_errno; |
| } |
| |
| static void |
| grub_jpeg_reset (struct grub_jpeg_data *data) |
| { |
| data->bit_mask = 0x0; |
| |
| data->dc_value[0] = 0; |
| data->dc_value[1] = 0; |
| data->dc_value[2] = 0; |
| } |
| |
| 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; |
| |
| grub_dprintf ("jpeg", "jpeg marker: %x\n", marker); |
| |
| 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_DRI: /* Define Restart Interval. */ |
| grub_jpeg_decode_dri (data); |
| break; |
| case JPEG_MARKER_SOS: /* Start Of Scan. */ |
| if (grub_jpeg_decode_sos (data)) |
| break; |
| /* FALLTHROUGH */ |
| case JPEG_MARKER_RST0: /* Restart. */ |
| case JPEG_MARKER_RST1: |
| case JPEG_MARKER_RST2: |
| case JPEG_MARKER_RST3: |
| case JPEG_MARKER_RST4: |
| case JPEG_MARKER_RST5: |
| case JPEG_MARKER_RST6: |
| case JPEG_MARKER_RST7: |
| grub_jpeg_decode_data (data); |
| grub_jpeg_reset (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, GRUB_FILE_TYPE_PIXMAP, 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++) |
| 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 cmdd __attribute__ ((unused)), |
| int argc, char **args) |
| { |
| struct grub_video_bitmap *bitmap = 0; |
| |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| 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); |
| } |