[librptexture] ICO: Save the icon image header and get sizes for BITMAPCOREHEADER and PNG.

iconHeader is now saved in the private class instead of being loaded
in loadImage_Win3().

The dimensions are now set correctly when an icon's "best" image is
in PNG format.

ico_structs.h: Add PNG chunks.
This commit is contained in:
David Korth 2025-06-07 21:15:50 -04:00
parent f6e5229b72
commit d01201a184
2 changed files with 92 additions and 26 deletions

View File

@ -69,12 +69,27 @@ public:
ICONHEADER win3;
} icoHeader;
// Icon directory (Win3.x)
/** Win3.x icon stuff **/
// Icon directory
// NOTE: *Not* byteswapped.
rp::uvector<ICONDIRENTRY> iconDirectory;
// "Best" icon in the icon directory
// NOTE: *Not* byteswapped.
const ICONDIRENTRY *pBestIcon;
// Icon bitmap header
union {
uint32_t size;
BITMAPCOREHEADER bch;
BITMAPINFOHEADER bih;
struct {
uint8_t magic[8];
PNG_IHDR_full_t ihdr;
} png;
} iconHeader;
// Decoded image
rp_image_ptr img;
@ -184,6 +199,15 @@ int ICOPrivate::loadIconDirectory_Win3(void)
// NOTE: Picking the first icon for now.
// TODO: Pick the largest, then highest color depth.
pBestIcon = &iconDirectory[0];
// Load the icon image header.
unsigned int addr = le32_to_cpu(pBestIcon->dwImageOffset);
size = file->seekAndRead(addr, &iconHeader, sizeof(iconHeader));
if (size != sizeof(iconHeader)) {
// Seek and/or read error.
return -EIO;
}
return 0;
}
@ -227,25 +251,12 @@ rp_image_const_ptr ICOPrivate::loadImage_Win1(void)
*/
rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
{
// Get the icon image header.
// May be BITMAPCOREHEADER or BITMAPINFOHEADER.
// Icon image header was already loaded by loadIconDirectory_Win3().
// TODO: Verify dwBytesInRes.
union {
uint32_t size;
BITMAPCOREHEADER bch;
BITMAPINFOHEADER bih;
} header;
unsigned int addr = le32_to_cpu(pBestIcon->dwImageOffset);
size_t size = file->seekAndRead(addr, &header, sizeof(header));
if (size != sizeof(header)) {
// Seek and/or read error.
return {};
}
// Check the header size.
const unsigned int header_size = le32_to_cpu(header.size);
addr += header_size;
const unsigned int header_size = le32_to_cpu(iconHeader.size);
unsigned int addr = le32_to_cpu(pBestIcon->dwImageOffset) + header_size;
switch (header_size) {
default:
// Not supported...
@ -272,11 +283,13 @@ rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
// and the bottom is the monochrome mask.
// NOTE 2: If height > 0, the entire bitmap is upside-down.
const BITMAPINFOHEADER *bih = &iconHeader.bih;
// Make sure width and height are valid.
// Height cannot be 0 or an odd number.
// NOTE: Negative height is allowed for "right-side up".
const unsigned int width = le32_to_cpu((unsigned int)(header.bih.biWidth));
const int orig_height = static_cast<int>(le32_to_cpu(header.bih.biHeight));
const unsigned int width = le32_to_cpu((unsigned int)(bih->biWidth));
const int orig_height = static_cast<int>(le32_to_cpu(bih->biHeight));
const bool is_upside_down = (orig_height > 0);
const unsigned int height = abs(orig_height);
const unsigned int half_height = height / 2;
@ -287,14 +300,14 @@ rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
// Only supporting 16-color images for now.
// TODO: Handle BI_BITFIELDS?
if (le32_to_cpu(header.bih.biPlanes) > 1) {
if (le32_to_cpu(bih->biPlanes) > 1) {
// Cannot handle planar bitmaps.
return {};
}
// Row must be 32-bit aligned.
// FIXME: Including for 24-bit images?
const unsigned int bitcount = le32_to_cpu(header.bih.biBitCount);
const unsigned int bitcount = le32_to_cpu(bih->biBitCount);
unsigned int stride = width;
switch (bitcount) {
default:
@ -333,7 +346,7 @@ rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
const unsigned int palette_count = (1U << bitcount);
const size_t palette_size = palette_count * sizeof(uint32_t);
pal_data.resize(palette_count);
size = file->seekAndRead(addr, pal_data.data(), palette_size);
size_t size = file->seekAndRead(addr, pal_data.data(), palette_size);
if (size != palette_size) {
// Seek and/or read error.
return {};
@ -354,7 +367,7 @@ rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
size_t biSizeImage = icon_size + mask_size;
rp::uvector<uint8_t> img_data;
img_data.resize(biSizeImage);
size = file->seekAndRead(addr, img_data.data(), biSizeImage);
size_t size = file->seekAndRead(addr, img_data.data(), biSizeImage);
if (size != biSizeImage) {
// Seek and/or read error.
return {};
@ -389,7 +402,7 @@ rp_image_const_ptr ICOPrivate::loadImage_Win3(void)
case 32: {
// 32-bit ARGB
const uint32_t biCompression = le32_to_cpu(header.bih.biCompression);
const uint32_t biCompression = le32_to_cpu(bih->biCompression);
if (biCompression != BI_RGB) {
// FIXME: BI_BITFIELDS is not supported right now.
return {};
@ -658,8 +671,32 @@ ICO::ICO(const IRpFilePtr &file)
return;
}
d->dimensions[0] = d->pBestIcon->bWidth;
d->dimensions[1] = d->pBestIcon->bHeight;
switch (d->iconHeader.size) {
default:
// Not supported...
d->file.reset();
return;
case BITMAPCOREHEADER_SIZE:
d->dimensions[0] = le16_to_cpu(d->iconHeader.bch.bcWidth);
d->dimensions[1] = le16_to_cpu(d->iconHeader.bch.bcHeight);
break;
case BITMAPINFOHEADER_SIZE:
case BITMAPV2INFOHEADER_SIZE:
case BITMAPV3INFOHEADER_SIZE:
case BITMAPV4HEADER_SIZE:
case BITMAPV5HEADER_SIZE:
d->dimensions[0] = le32_to_cpu(d->iconHeader.bih.biWidth);
d->dimensions[1] = le32_to_cpu(d->iconHeader.bih.biHeight);
break;
case 0x474E5089: // "\x89PNG"
// TODO: Verify more IHDR fields?
d->dimensions[0] = be32_to_cpu(d->iconHeader.png.ihdr.data.width);
d->dimensions[1] = be32_to_cpu(d->iconHeader.png.ihdr.data.height);
break;
}
break;
}

View File

@ -151,6 +151,35 @@ typedef enum {
BI_PNG = 5U,
} BMP_Compression_e;
/** PNG chunks (TODO: Combine with librpbase/tests/bmp.h?) **/
/**
* PNG IHDR chunk
*/
#pragma pack(1)
typedef struct RP_PACKED _PNG_IHDR_t {
uint32_t width; // BE32
uint32_t height; // BE32
uint8_t bit_depth;
uint8_t color_type;
uint8_t compression_method;
uint8_t filter_method;
uint8_t interlace_method;
} PNG_IHDR_t;
ASSERT_STRUCT(PNG_IHDR_t, 13);
#pragma pack()
// PNG IHDR struct, with length, name, and CRC32.
#pragma pack(1)
typedef struct RP_PACKED _PNG_IHDR_full_t {
uint32_t chunk_size; // BE32
char chunk_name[4]; // "IHDR"
PNG_IHDR_t data;
uint32_t crc32;
} PNG_IHDR_full_t;
ASSERT_STRUCT(PNG_IHDR_full_t, 25);
#pragma pack()
#ifdef __cplusplus
}
#endif