mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-07 16:40:59 +00:00
LibGfx/GIF: Only parse global color table if header flag is set
This fixes an issue where GIF images without a global color table would have the first segment incorrectly interpreted as color table data. Makes many more screenshots appear on https://virtuallyfun.com/ :^)
This commit is contained in:
parent
a1d669fe63
commit
182a2b0c3a
|
@ -100,6 +100,65 @@ TEST_CASE(test_gif)
|
||||||
EXPECT(frame.duration == 400);
|
EXPECT(frame.duration == 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_gif_without_global_color_table)
|
||||||
|
{
|
||||||
|
Array<u8, 35> gif_data {
|
||||||
|
// Header (6 bytes): "GIF89a"
|
||||||
|
0x47,
|
||||||
|
0x49,
|
||||||
|
0x46,
|
||||||
|
0x38,
|
||||||
|
0x39,
|
||||||
|
0x61,
|
||||||
|
|
||||||
|
// Logical Screen Descriptor (7 bytes)
|
||||||
|
0x01,
|
||||||
|
0x00, // Width (1)
|
||||||
|
0x01,
|
||||||
|
0x00, // Height (1)
|
||||||
|
0x00, // Packed fields (NOTE: the MSB here is the Global Color Table flag!)
|
||||||
|
0x00, // Background Color Index
|
||||||
|
0x00, // Pixel Aspect Ratio
|
||||||
|
|
||||||
|
// Image Descriptor (10 bytes)
|
||||||
|
0x2C,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x80,
|
||||||
|
|
||||||
|
// Local Color Table (6 bytes: 2 colors, 3 bytes per color)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00, // Color 1: Black (RGB: 0, 0, 0)
|
||||||
|
0xff,
|
||||||
|
0x00,
|
||||||
|
0x00, // Color 2: Red (RGB: 255, 0, 0)
|
||||||
|
|
||||||
|
// Image Data (8 bytes)
|
||||||
|
0x02, // LZW Minimum Code Size
|
||||||
|
0x02, // Data Sub-block size (2 bytes)
|
||||||
|
0x4C,
|
||||||
|
0x01, // Image Data
|
||||||
|
0x00, // Data Sub-block Terminator
|
||||||
|
|
||||||
|
// Trailer (1 byte)
|
||||||
|
0x3B,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::GIFImageDecoderPlugin::create(gif_data));
|
||||||
|
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||||
|
auto frame = TRY_OR_FAIL(plugin_decoder->frame(0));
|
||||||
|
EXPECT(frame.image);
|
||||||
|
EXPECT_EQ(frame.image->size(), Gfx::IntSize(1, 1));
|
||||||
|
EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color::NamedColor::Red);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE(test_not_ico)
|
TEST_CASE(test_not_ico)
|
||||||
{
|
{
|
||||||
auto file = MUST(Core::MappedFile::map(TEST_INPUT("png/buggie.png"sv)));
|
auto file = MUST(Core::MappedFile::map(TEST_INPUT("png/buggie.png"sv)));
|
||||||
|
|
|
@ -240,20 +240,26 @@ static ErrorOr<void> load_header_and_logical_screen(GIFLoadingContext& context)
|
||||||
context.logical_screen.width = TRY(context.stream.read_value<LittleEndian<u16>>());
|
context.logical_screen.width = TRY(context.stream.read_value<LittleEndian<u16>>());
|
||||||
context.logical_screen.height = TRY(context.stream.read_value<LittleEndian<u16>>());
|
context.logical_screen.height = TRY(context.stream.read_value<LittleEndian<u16>>());
|
||||||
|
|
||||||
auto gcm_info = TRY(context.stream.read_value<u8>());
|
auto packed_fields = TRY(context.stream.read_value<u8>());
|
||||||
context.background_color_index = TRY(context.stream.read_value<u8>());
|
context.background_color_index = TRY(context.stream.read_value<u8>());
|
||||||
[[maybe_unused]] auto pixel_aspect_ratio = TRY(context.stream.read_value<u8>());
|
[[maybe_unused]] auto pixel_aspect_ratio = TRY(context.stream.read_value<u8>());
|
||||||
|
|
||||||
u8 bits_per_pixel = (gcm_info & 7) + 1;
|
// Global Color Table; if the flag is set, the Global Color Table will
|
||||||
int color_map_entry_count = 1;
|
// immediately follow the Logical Screen Descriptor.
|
||||||
for (int i = 0; i < bits_per_pixel; ++i)
|
bool global_color_table_flag = packed_fields & 0x80;
|
||||||
color_map_entry_count *= 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < color_map_entry_count; ++i) {
|
if (global_color_table_flag) {
|
||||||
u8 r = TRY(context.stream.read_value<u8>());
|
u8 bits_per_pixel = (packed_fields & 7) + 1;
|
||||||
u8 g = TRY(context.stream.read_value<u8>());
|
int color_map_entry_count = 1;
|
||||||
u8 b = TRY(context.stream.read_value<u8>());
|
for (int i = 0; i < bits_per_pixel; ++i)
|
||||||
context.logical_screen.color_map[i] = { r, g, b };
|
color_map_entry_count *= 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < color_map_entry_count; ++i) {
|
||||||
|
u8 r = TRY(context.stream.read_value<u8>());
|
||||||
|
u8 g = TRY(context.stream.read_value<u8>());
|
||||||
|
u8 b = TRY(context.stream.read_value<u8>());
|
||||||
|
context.logical_screen.color_map[i] = { r, g, b };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
Loading…
Reference in a new issue