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>
958 lines
23 KiB
C
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);
|
|
}
|