LibGfx: Select largest ICO image before bpp tie-break

When choosing the best image from an ICO file, prefer the entry with the
largest pixel area first, and only use bits-per-pixel as a tie-breaker.
This had regressed in commit b10fe7c136.

This fixes ICO files like Discord's favicon, where 16x16, 32x32, 48x48,
and 256x256 entries all advertise the same bpp. We were previously just
choosing whichever we saw first, which happened to be the 16x16 icon.
This commit is contained in:
Timothy Flynn 2026-05-27 20:04:28 -04:00 committed by Jelle Raaijmakers
parent d341ed5448
commit b7c4cd511f
3 changed files with 19 additions and 9 deletions

View file

@ -5,6 +5,7 @@
*/
#include <AK/Debug.h>
#include <AK/Enumerate.h>
#include <AK/MemoryStream.h>
#include <AK/Types.h>
#include <LibGfx/ImageFormats/BMPLoader.h>
@ -120,19 +121,19 @@ static ErrorOr<ICOImageDescriptor> decode_ico_direntry(Stream& stream, IconType
static size_t find_largest_image(ICOLoadingContext const& context)
{
size_t max_area = 0;
size_t index = 0;
size_t largest_index = 0;
u16 max_bits_per_pixel = 0;
for (auto const& desc : context.images) {
if (static_cast<size_t>(desc.width) * static_cast<size_t>(desc.height) >= max_area) {
if (desc.bits_per_pixel > max_bits_per_pixel) {
max_area = desc.width * desc.height;
largest_index = index;
max_bits_per_pixel = desc.bits_per_pixel;
}
for (auto const& [index, desc] : enumerate(context.images)) {
auto area = static_cast<size_t>(desc.width) * static_cast<size_t>(desc.height);
if (area > max_area || (area == max_area && desc.bits_per_pixel > max_bits_per_pixel)) {
max_area = area;
largest_index = index;
max_bits_per_pixel = desc.bits_per_pixel;
}
++index;
}
return largest_index;
}

View file

@ -121,6 +121,15 @@ TEST_CASE(test_ico_malformed_frame)
}
}
TEST_CASE(test_ico_selects_largest_image_with_same_bpp)
{
auto input = TEST_INPUT("ico/multiple-sizes-with-same-bpp.ico"sv);
auto file = TRY_OR_FAIL(Core::MappedFile::map(input));
auto plugin_decoder = TRY_OR_FAIL(Gfx::ICOImageDecoderPlugin::create(file->bytes()));
EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(256, 256));
}
TEST_CASE(test_cur)
{
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("cur/cursor.cur"sv)));

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B