Alec Brown a85714545f video/readers: Add artificial limit to image dimensions
In grub-core/video/readers/jpeg.c, the height and width of a JPEG image don't
have an upper limit for how big the JPEG image can be. In Coverity, this is
getting flagged as an untrusted loop bound. This issue can also seen in PNG and
TGA format images as well but Coverity isn't flagging it. To prevent this, the
constant IMAGE_HW_MAX_PX is being added to include/grub/bitmap.h, which has
a value of 16384, to act as an artificial limit and restrict the height and
width of images. This value was picked as it is double the current max
resolution size, which is 8K.

Fixes: CID 292450

Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2022-10-27 20:10:18 +02:00

958 lines
23 KiB
C

/*
* 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>
#include <grub/safemath.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;
grub_ssize_t bytes_read;
r = 0;
bytes_read = grub_file_read (data->file, &r, 1);
if (bytes_read != 1)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: unexpected end of data");
return 0;
}
return r;
}
static grub_uint16_t
grub_jpeg_get_word (struct grub_jpeg_data *data)
{
grub_uint16_t r;
grub_ssize_t bytes_read;
r = 0;
bytes_read = grub_file_read (data->file, &r, sizeof (grub_uint16_t));
if (bytes_read != sizeof (grub_uint16_t))
{
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: unexpected end of data");
return 0;
}
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 (grub_errno != GRUB_ERR_NONE) {
grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: file read error");
return 0;
}
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;
}
if (grub_errno != GRUB_ERR_NONE)
{
grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: file read error");
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 && grub_errno == GRUB_ERR_NONE; 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);
if (next_marker > data->file->size)
{
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: invalid huffman table");
}
while (data->file->offset + sizeof (count) + 1 <= next_marker)
{
id = grub_jpeg_get_byte (data);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
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;
if (data->huff_value[id] != NULL)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: attempt to reallocate huffman table");
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);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
if (next_marker > data->file->size)
{
/* Should never be set beyond the size of the file. */
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid next reference");
}
while (data->file->offset + sizeof (data->quan_table[id]) + 1
<= next_marker)
{
id = grub_jpeg_get_byte (data);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
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_errno != GRUB_ERR_NONE)
return grub_errno;
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);
grub_dprintf ("jpeg", "image height: %d\n", data->image_height);
grub_dprintf ("jpeg", "image width: %d\n", data->image_width);
if ((!data->image_height) || (!data->image_width) ||
(data->image_height > IMAGE_HW_MAX_PX) || (data->image_width > IMAGE_HW_MAX_PX))
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 (grub_errno != GRUB_ERR_NONE)
return grub_errno;
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->comp_index[id][0] > 1)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: too many quantization tables");
}
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 grub_err_t
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));
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
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;
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
if (pos >= ARRAY_SIZE (jpeg_zigzag_order))
{
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: invalid position in zigzag order!?");
}
du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos];
pos++;
}
grub_jpeg_idct_transform (du);
return GRUB_ERR_NONE;
}
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 (grub_errno != GRUB_ERR_NONE)
return grub_errno;
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");
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
ht = grub_jpeg_get_byte (data);
data->comp_index[id][1] = (ht >> 4);
data->comp_index[id][2] = (ht & 0xF) + 2;
if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) ||
(data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index");
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
}
grub_jpeg_get_byte (data); /* Skip 3 unused bytes. */
grub_jpeg_get_word (data);
if (grub_errno != GRUB_ERR_NONE)
return grub_errno;
if (data->file->offset != data_offset)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
if (*data->bitmap)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: too many start of scan blocks");
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;
unsigned stride_a, stride_b, stride;
int rst = data->dri;
grub_err_t err = GRUB_ERR_NONE;
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);
if (data->bitmap_ptr == NULL)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: attempted to decode data before start of stream");
if (grub_mul(vb, data->image_width, &stride_a) ||
grub_mul(hb, nc1, &stride_b) ||
grub_sub(stride_a, stride_b, &stride))
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
"jpeg: cannot decode image with these dimensions");
for (; data->r1 < nr1 && (!data->dri || rst);
data->r1++, data->bitmap_ptr += stride * 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++)
{
err = grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
if (err != GRUB_ERR_NONE)
return err;
}
if (data->color_components >= 3)
{
err = grub_jpeg_decode_du (data, 1, data->cbdu);
if (err != GRUB_ERR_NONE)
return err;
err = grub_jpeg_decode_du (data, 2, data->crdu);
if (err != GRUB_ERR_NONE)
return err;
}
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);
}