How to Save Hicon to an .Ico File

Save HICON as a png

I'm missing the 'copy' from your code:

Graphics* g = Graphics::FromImage(copy);

Looking at the images it appears that the device context you're drawing the icon to doesn't have 32-bit colors (alpha channel is missing).

Try creating a DC like this:

HDC hDC = CreateCompatibleDC(NULL);

and then select a 32-bit colored (empty) bitmap into that dc. After that you can draw the icon and save it.

Save a list of QPixmaps to .ico file

It seems that there is no built in support in Qt for writing ICO files, so here I'm publishing a code snippet to generate one from a list of pixmaps. Hope it may be useful for somebody else.

template<typename T>
void write(QFile& f, const T t)
{
f.write((const char*)&t, sizeof(t));
}

bool savePixmapsToICO(const QList<QPixmap>& pixmaps, const QString& path)
{
static_assert(sizeof(short) == 2, "short int is not 2 bytes");
static_assert(sizeof(int) == 4, "int is not 4 bytes");

QFile f(path);
if (!f.open(QFile::OpenModeFlag::WriteOnly)) return false;

// Header
write<short>(f, 0);
write<short>(f, 1);
write<short>(f, pixmaps.count());

// Compute size of individual images
QList<int> images_size;
for (int ii = 0; ii < pixmaps.count(); ++ii) {
QTemporaryFile temp;
temp.setAutoRemove(true);
if (!temp.open()) return false;

const auto& pixmap = pixmaps[ii];
pixmap.save(&temp, "PNG");

temp.close();

images_size.push_back(QFileInfo(temp).size());
}

// Images directory
constexpr unsigned int entry_size = sizeof(char) + sizeof(char) + sizeof(char) + sizeof(char) + sizeof(short) + sizeof(short) + sizeof(unsigned int) + sizeof(unsigned int);
static_assert(entry_size == 16, "wrong entry size");

unsigned int offset = 3 * sizeof(short) + pixmaps.count() * entry_size;
for (int ii = 0; ii < pixmaps.count(); ++ii) {
const auto& pixmap = pixmaps[ii];
if (pixmap.width() > 256 || pixmap.height() > 256) continue;

write<char>(f, pixmap.width() == 256 ? 0 : pixmap.width());
write<char>(f, pixmap.height() == 256 ? 0 : pixmap.height());
write<char>(f, 0); // palette size
write<char>(f, 0); // reserved
write<short>(f, 1); // color planes
write<short>(f, pixmap.depth()); // bits-per-pixel
write<unsigned int>(f, images_size[ii]); // size of image in bytes
write<unsigned int>(f, offset); // offset
offset += images_size[ii];
}

for (int ii = 0; ii < pixmaps.count(); ++ii) {
const auto& pixmap = pixmaps[ii];
if (pixmap.width() > 256 || pixmap.height() > 256) continue;
pixmap.save(&f, "PNG");
}

return true;
}

Code also available in GitHub.

Load HICON from the buffer (*.ico file)

I found a solution. Actually after a bit of research it turned out that the code, which I placed inside the sample, is indeed correct.

There is a bug in WinAPI function LookupIconIdFromDirectoryEx(). I've noticed that for some icons I can get the correct icon and set it up, but for others it fails either on the later stage CreateIconFromResourceEx(), or earlier on LookupIconIdFromDirectoryEx(). I've noticed that sometimes the function fails to find the icon even though the icon is inside a file. Sometimes the function returned the same value for the different icons inside an icon file.

I made several rounds of tests and parsed the format of each icon file myself based on the format definition. Then I compared the actual offsets to the values returned by LookupIconIdFromDirectoryEx().

Let's say we have 2 icons: A and B.

The A icon in my case contained 5 images, the entries inside the ICO file were placed in the following order:

  1. 256x256 PNG
  2. 128x128 BMP
  3. 64x64 BMP
  4. 32x32 BMP
  5. 16x16 BMP

The B icon contained 7 images, they were placed in the following order:

  1. 16x16 BMP
  2. 32x32 BMP
  3. 48x48 BMP
  4. 64x64 BMP
  5. 128x128 BMP
  6. 256x256 BMP

The results of the LookupIconIdFromDirectoryEx() for each of the icons can be found below.

Icon A:

  1. 86
  2. 9640
  3. 9640
  4. 9640
  5. 9640

Icon B:

  1. 102
  2. 1230
  3. 5494
  4. 15134
  5. NOT FOUND (function failed and returned 0)
  6. NOT FOUND (function failed and returned 0)
  7. NOT FOUND (function failed and returned 0)

I parsed the actual format, according to the definition in wikipedia (the tables below are contain the icon entries, each row is a separate entry, each column is a field for this entry) for both icon files.

The actual layout of A is:

W     H     *    *    *   **     SIZE     OFFSET
------------------------------------------------
0 0 0 0 1 32 43253 86
128 128 0 0 1 32 67624 43339
48 48 0 0 1 32 9640 110963
32 32 0 0 1 32 4264 120603
16 16 0 0 1 32 1128 124867

The actual layout of B is:

W     H     *    *    *   **     SIZE     OFFSET
------------------------------------------------
16 16 0 0 0 32 1128 102
32 32 0 0 0 32 4264 1230
48 48 0 0 0 32 9640 5494
64 64 0 0 0 32 16936 15134
128 128 0 0 0 32 67624 32070
0 0 0 0 0 32 270376 99694

So you can clearly see, that in case of A only the offset for the first image was idenfied correct, offsets for other images were incorrect and equal to the ... size of the 3rd image (??), maybe just a coincidence.

In case of the second image all offsets were correct until we reached 128x128 image, which was not even found.

The common thing between those 2 cases is that the function started to behave strange after parsing 128x128 icon, and here is an interesting thing - look on the size of 128x128 icon and compare it to the size of the other images. In both cases the size of the 128x128 image does not fit in 2 bytes. Right after parsing the icon entry in which the size was bigger than 2 bytes, the function behavior is undefined. Judging from this data I can assume that somewhere in the code they expect that the size of the icon cannot be bigger than 2 bytes. In case if the size is bigger, the behavior is undefined.

I used ImageMagick to reassemble a new icon which does not have such image inside and now the function works correct in all cases.

So I can definitely say that there is a bug inside LookupIconIdFromDirectoryEx() implementation.

UPD. Icons can be found here: A icon, B icon.

create HICON from memory on windows

If you want to do this without using GDI+ nor WIC then you have to do some parsing yourself because the format of a .ICO file is not exactly the same as a icon resource in a .EXE/.DLL so you cannot use the resource based icon functions. The official binary spec can be found here and there is a great blog series about it here.

If your .ico file has more than one image in it then you must loop over the icon directories until you find a image dimension you are happy with. You can then call CreateIcon to create a HICON.

winapi c++ HICON

Use an .rc file to add the .ico file to your executable's resources. Then when calling LoadImage(), get rid of the LR_LOADFROMFILE flag and specify the ID of your resource in the lpszName parameter instead of a filename. For example:

MY_ICON ICON "icon.ico"

.

case WM_CREATE:
{
...
hIcon = (HICON) LoadImage(GetModuleHandle(NULL), "MY_ICON", IMAGE_ICON, 32, 32, 0);
...
}

Saving icon resources (not including the duplicate ones) to disk

The icon file should start with icon header ICONDIR, not BITMAPFILEHEADER. See Icon reference

The bytes obtained from FindResource could contain PNG data, not bitmap. It's best to avoid converting between icon and bitmap.

After initializing and writing the icon header, you can directly write the bytes obtained from FindResource, to avoid any conversion.

EnumResourceNames(... RT_ICON ...) will enumerate the icons as separate, therefore each destination file contains one icon.

EnumResourceNames(... RT_GROUP_ICON ...) will enumerate the icon groups. You can go through the groups and add all the icons in to one file.

It's not clear what is your criteria for finding duplicates. With RT_ICON enumeration, you get the same icon in different sizes and color depth, they are not exact duplicates. You can choose certain sizes, example 32x32 size only. To find exact duplicates, you can calculate the hash of the bytes from resources, then compare the hash values.

#include <string>
#include <fstream>
#include <Windows.h>

#pragma pack(push, 2)
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
} ICONDIRENTRY, *LPICONDIRENTRY;

typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
ICONDIRENTRY idEntries[1];
} ICONDIR, *LPICONDIR;
#pragma pack(pop)

BOOL CALLBACK EnumIcons(HMODULE hmodule, LPCTSTR type, LPTSTR lpszName, LONG_PTR ptr)
{
if(!ptr)
return FALSE;
std::string path = (const char*)ptr;

HRSRC hRes = FindResource(hmodule, lpszName, type);
if(!hRes)
return TRUE;

DWORD size = SizeofResource(hmodule, hRes);
HGLOBAL hg = LoadResource(hmodule, hRes);
BYTE* bytes = (BYTE*)LockResource(hg);

HICON hicon = CreateIconFromResource(bytes, size, TRUE, 0x00030000);
if(!hicon)
return TRUE;

if (IS_INTRESOURCE(lpszName))
path += std::to_string((int)lpszName);
else
path += lpszName;
path += std::string(".ico");
std::ofstream fout(path, std::ios::binary);
if(!fout)
return TRUE;

ICONINFOEX ii = { sizeof(ii) };
GetIconInfoEx(hicon, &ii);

ICONDIR hdr = { 0 };
hdr.idType = 1;
hdr.idCount = 1;

BITMAP bm;
GetObject(ii.hbmColor, sizeof(bm), &bm);

hdr.idEntries[0].bWidth = (BYTE)bm.bmWidth;
hdr.idEntries[0].bHeight = (BYTE)bm.bmHeight;
hdr.idEntries[0].wPlanes = bm.bmPlanes;
hdr.idEntries[0].wBitCount = bm.bmBitsPixel;
hdr.idEntries[0].dwBytesInRes = size;
hdr.idEntries[0].dwImageOffset = sizeof(ICONDIR);

fout.write((char*)&hdr, hdr.idEntries[0].dwImageOffset);
fout.write((char*)bytes, size);

DestroyIcon(hicon);
return TRUE;
}

int main()
{
std::string modulepath = "file.exe";
const char *dir = "c:\\test\\";
HMODULE hmodule = LoadLibraryEx(modulepath.c_str(), NULL,
LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if(hmodule)
EnumResourceNames(hmodule, RT_ICON, (ENUMRESNAMEPROC)EnumIcons, (LONG_PTR)dir);
return 0;
}

ps, you don't need VirtualAlloc to allocate memory for your own process. You can use malloc, or new for C++. Better yet, use std::string and its methods.

Invert .ico file in Python

You can utilize Pillow, invert the image and save it to a temporary file which will auto-delete after the context manager (with block) exits

# pip install pillow
from PIL import Image, ImageOps
from tempfile import NamedTemporaryFile

with NamedTemporaryFile() as file, Image.open("image.ico") as img:
old_mode = img.mode # loaded as RGBA!

rgb = img.convert("RGB")
inverted = ImageOps.invert(rgb)
inverted = inverted.convert(old_mode)

inverted.save(file, format="ICO", quality=100)
file.seek(0) # rewind so it can be read again
print(file.name)
# /tmp/tmpjo7pa_r6
# load/read icon here, probably self.get_hicon(file.name)
# file is automatically deleted here


Related Topics



Leave a reply



Submit