mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 17:52:26 -05:00
LibGfx: Add support for uncompressed/classic ICO files
This add support to the ICO file decoder for loading uncompressed bitmap icon files. It is limited to 32-bit color icons, no 4/8/16 bit or paletted formats yet. This is in addition to the PNG compressed ICO format support added previously. With this we can now load the favicons for github, google and twitter.
This commit is contained in:
parent
140581d0c3
commit
b1e3f728a1
Notes:
sideshowbarker
2024-07-19 05:28:44 +09:00
Author: https://github.com/MegabytePhreak Commit: https://github.com/SerenityOS/serenity/commit/b1e3f728a14 Pull-request: https://github.com/SerenityOS/serenity/pull/2565 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/nico
1 changed files with 134 additions and 2 deletions
|
@ -29,6 +29,7 @@
|
|||
#include <AK/LexicalPath.h>
|
||||
#include <AK/MappedFile.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/ICOLoader.h>
|
||||
#include <LibGfx/PNGLoader.h>
|
||||
#include <LibM/math.h>
|
||||
|
@ -59,6 +60,40 @@ struct ICONDIRENTRY {
|
|||
};
|
||||
static_assert(sizeof(ICONDIRENTRY) == 16);
|
||||
|
||||
struct [[gnu::packed]] BMPFILEHEADER
|
||||
{
|
||||
u8 signature[2];
|
||||
u32 size;
|
||||
u16 reserved1;
|
||||
u16 reserved2;
|
||||
u32 offset;
|
||||
};
|
||||
static_assert(sizeof(BMPFILEHEADER) == 14);
|
||||
|
||||
struct BITMAPINFOHEADER {
|
||||
u32 size;
|
||||
i32 width;
|
||||
i32 height;
|
||||
u16 planes;
|
||||
u16 bpp;
|
||||
u32 compression;
|
||||
u32 size_image;
|
||||
u32 vres;
|
||||
u32 hres;
|
||||
u32 palette_size;
|
||||
u32 important_colors;
|
||||
};
|
||||
static_assert(sizeof(BITMAPINFOHEADER) == 40);
|
||||
|
||||
struct [[gnu::packed]] BMP_ARGB
|
||||
{
|
||||
u8 b;
|
||||
u8 g;
|
||||
u8 r;
|
||||
u8 a;
|
||||
};
|
||||
static_assert(sizeof(BMP_ARGB) == 4);
|
||||
|
||||
struct ImageDescriptor {
|
||||
u16 width;
|
||||
u16 height;
|
||||
|
@ -162,6 +197,98 @@ static bool load_ico_directory(ICOLoadingContext& context)
|
|||
#endif
|
||||
context.images.append(desc);
|
||||
}
|
||||
context.state = ICOLoadingContext::State::DirectoryDecoded;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_ico_bmp(ICOLoadingContext& context, ImageDescriptor& desc)
|
||||
{
|
||||
BITMAPINFOHEADER info;
|
||||
if (desc.size < sizeof(info))
|
||||
return false;
|
||||
|
||||
memcpy(&info, context.data + desc.offset, sizeof(info));
|
||||
if (info.size != sizeof(info)) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: info size: %u, expected: %lu\n", info.size, sizeof(info));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.width < 0) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: width %d < 0\n", info.width);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
bool topdown = false;
|
||||
if (info.height < 0) {
|
||||
topdown = true;
|
||||
info.height = -info.height;
|
||||
}
|
||||
|
||||
if (info.planes != 1) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: planes: %d != 1", info.planes);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.bpp != 32) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: unsupported bpp: %u\n", info.bpp);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: width: %d height: %d direction: %s bpp: %d size_image: %u\n",
|
||||
info.width, info.height, topdown ? "TopDown" : "BottomUp", info.bpp, info.size_image);
|
||||
#endif
|
||||
|
||||
if (info.compression != 0 || info.palette_size != 0 || info.important_colors != 0) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: following fields must be 0: compression: %u palette_size: %u important_colors: %u\n",
|
||||
info.compression, info.palette_size, info.important_colors);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.width != desc.width || info.height != 2 * desc.height) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: size mismatch: ico %dx%d, bmp %dx%d\n",
|
||||
desc.width, desc.height, info.width, info.height);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mask is 1bpp, and each row must be 4-byte aligned
|
||||
size_t mask_row_len = align_up_to(align_up_to(desc.width, 8)/8, 4);
|
||||
size_t required_len = desc.height * (desc.width * sizeof(BMP_ARGB) + mask_row_len);
|
||||
size_t available_len = desc.size - sizeof(info);
|
||||
if (required_len > available_len) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bmp: required_len: %lu > available_len: %lu\n",
|
||||
required_len, available_len);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
desc.bitmap = Bitmap::create_purgeable(BitmapFormat::RGBA32, { desc.width, desc.height });
|
||||
Bitmap& bitmap = *desc.bitmap;
|
||||
const u8* image_base = context.data + desc.offset + sizeof(info);
|
||||
const BMP_ARGB* data_base = (const BMP_ARGB*)image_base;
|
||||
const u8* mask_base = image_base + desc.width * desc.height * sizeof(BMP_ARGB);
|
||||
for (int y = 0; y < desc.height; y++) {
|
||||
const u8* row_mask = mask_base + mask_row_len * y;
|
||||
const BMP_ARGB* row_data = data_base + desc.width * y;
|
||||
for (int x = 0; x < desc.width; x++) {
|
||||
u8 mask = !!(row_mask[x / 8] & (0x80 >> (x % 8)));
|
||||
BMP_ARGB data = row_data[x];
|
||||
bitmap.set_pixel(x, topdown ? y : desc.height - y - 1,
|
||||
Color(data.r, data.g, data.b, mask ? 0 : data.a));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -191,8 +318,13 @@ static bool load_ico_bitmap(ICOLoadingContext& context, size_t index)
|
|||
}
|
||||
return true;
|
||||
} else {
|
||||
dbg() << "load_ico_bitmap: image index: " << index << " must be BMP encoded, not implemented\n";
|
||||
return false;
|
||||
if (!load_ico_bmp(context, desc)) {
|
||||
#ifdef ICO_DEBUG
|
||||
printf("load_ico_bitmap: failed to load BMP encoded image index: %lu\n", index);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue