[librptexture] ICO: Select the best icon image.

First, the image size is checked. If either width or height are larger
than the previously-selected image, this image is picked. Otherwise,
if the size is identical (both width and height), and bitcount is
higher, then the image is selected.

TODO: Better non-square icon handling? (Use "largest area"?)
This commit is contained in:
David Korth 2025-06-07 21:50:37 -04:00
parent fbdeae25a5
commit 1caf44d15e
2 changed files with 108 additions and 6 deletions

View File

@ -220,11 +220,95 @@ int ICOPrivate::loadIconDirectory_Win3(void)
}
}
// NOTE: Picking the first icon for now.
// TODO: Pick the largest, then highest color depth.
pBestIcon = &iconDirectory[0];
pIconHeader = &iconBitmapHeaders[0];
return 0;
// Go through the icon bitmap headers and figure out the "best" one.
unsigned int width_best = 0, height_best = 0, bitcount_best = 0;
for (unsigned int i = 0; i < count; i++) {
// Get the width, height, and color depth from this bitmap header.
const IconBitmapHeader_t *const p = &iconBitmapHeaders[i];
unsigned int width, height, bitcount;
switch (p->size) {
default:
// Not supported...
continue;
case BITMAPCOREHEADER_SIZE:
if (le32_to_cpu(p->bch.bcPlanes) > 1) {
// Cannot handle planar bitmaps.
continue;
}
width = le16_to_cpu(p->bch.bcWidth);
height = le16_to_cpu(p->bch.bcHeight) / 2;
bitcount = le16_to_cpu(p->bch.bcBitCount);
break;
case BITMAPINFOHEADER_SIZE:
case BITMAPV2INFOHEADER_SIZE:
case BITMAPV3INFOHEADER_SIZE:
case BITMAPV4HEADER_SIZE:
case BITMAPV5HEADER_SIZE:
if (le32_to_cpu(p->bih.biPlanes) > 1) {
// Cannot handle planar bitmaps.
continue;
}
width = le32_to_cpu(p->bih.biWidth);
height = le32_to_cpu(p->bih.biHeight) / 2;
bitcount = le16_to_cpu(p->bih.biBitCount);
break;
case 0x474E5089: // "\x89PNG"
switch (p->png.ihdr.data.color_type) {
default:
// Not supported...
continue;
case PNG_COLOR_TYPE_PALETTE:
bitcount = p->png.ihdr.data.bit_depth;
break;
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_RGB:
// Handling as if it's RGB.
bitcount = p->png.ihdr.data.bit_depth * 3;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
case PNG_COLOR_TYPE_RGB_ALPHA:
// Handling as if it's ARGB.
bitcount = p->png.ihdr.data.bit_depth * 4;
break;
}
width = be32_to_cpu(p->png.ihdr.data.width);
height = be32_to_cpu(p->png.ihdr.data.height);
break;
}
// Check if the image is larger.
// TODO: Non-square icon handling.
bool icon_is_better = false;
if (width > width_best || height > height_best) {
// Image is larger.
icon_is_better = true;
} else if (width == width_best && height == height_best) {
// Image is the same size.
if (bitcount > bitcount_best) {
// Color depth is higher.
icon_is_better = true;
}
}
if (icon_is_better) {
// This icon is better.
pBestIcon = &iconDirectory[i];
pIconHeader = p;
width_best = width;
height_best = height;
bitcount_best = bitcount;
}
}
return (pBestIcon) ? 0 : -ENOENT;
}
/**

View File

@ -153,6 +153,22 @@ typedef enum {
/** PNG chunks (TODO: Combine with librpbase/tests/bmp.h?) **/
/* These describe the color_type field in png_info. */
/* color type masks */
#define PNG_COLOR_MASK_PALETTE 1
#define PNG_COLOR_MASK_COLOR 2
#define PNG_COLOR_MASK_ALPHA 4
/* color types. Note that not all combinations are legal */
#define PNG_COLOR_TYPE_GRAY 0
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
/* aliases */
#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA
#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA
/**
* PNG IHDR chunk
*/
@ -169,7 +185,9 @@ typedef struct RP_PACKED _PNG_IHDR_t {
ASSERT_STRUCT(PNG_IHDR_t, 13);
#pragma pack()
// PNG IHDR struct, with length, name, and CRC32.
/**
* PNG IHDR struct, with length, name, and CRC32.
*/
#pragma pack(1)
typedef struct RP_PACKED _PNG_IHDR_full_t {
uint32_t chunk_size; // BE32