diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 0a72e09ad9..8f99f52a5f 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -100,6 +100,65 @@ TEST_CASE(test_gif) EXPECT(frame.duration == 400); } +TEST_CASE(test_gif_without_global_color_table) +{ + Array 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) { auto file = MUST(Core::MappedFile::map(TEST_INPUT("png/buggie.png"sv))); diff --git a/Userland/Libraries/LibGfx/ImageFormats/GIFLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/GIFLoader.cpp index 22e0f085a3..22dbdf6140 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/GIFLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/GIFLoader.cpp @@ -240,20 +240,26 @@ static ErrorOr load_header_and_logical_screen(GIFLoadingContext& context) context.logical_screen.width = TRY(context.stream.read_value>()); context.logical_screen.height = TRY(context.stream.read_value>()); - auto gcm_info = TRY(context.stream.read_value()); + auto packed_fields = TRY(context.stream.read_value()); context.background_color_index = TRY(context.stream.read_value()); [[maybe_unused]] auto pixel_aspect_ratio = TRY(context.stream.read_value()); - u8 bits_per_pixel = (gcm_info & 7) + 1; - int color_map_entry_count = 1; - for (int i = 0; i < bits_per_pixel; ++i) - color_map_entry_count *= 2; + // Global Color Table; if the flag is set, the Global Color Table will + // immediately follow the Logical Screen Descriptor. + bool global_color_table_flag = packed_fields & 0x80; - for (int i = 0; i < color_map_entry_count; ++i) { - u8 r = TRY(context.stream.read_value()); - u8 g = TRY(context.stream.read_value()); - u8 b = TRY(context.stream.read_value()); - context.logical_screen.color_map[i] = { r, g, b }; + if (global_color_table_flag) { + u8 bits_per_pixel = (packed_fields & 7) + 1; + int color_map_entry_count = 1; + for (int i = 0; i < bits_per_pixel; ++i) + color_map_entry_count *= 2; + + for (int i = 0; i < color_map_entry_count; ++i) { + u8 r = TRY(context.stream.read_value()); + u8 g = TRY(context.stream.read_value()); + u8 b = TRY(context.stream.read_value()); + context.logical_screen.color_map[i] = { r, g, b }; + } } return {};