I'm interested in creating a single .ico file (with transparency) from a list of QPixmap
images (with sizes 16x16, 32x32, 48x48...). I haven't seen any related method in Qt's documentation: QPixmap
, QImage
, QIcon
(which is for storing images for UI states, not related to the file format)...
Does Qt has such functionality? How can I save such file? May be mixing with Windows API?
PS: A low-level solution would be to directly write the .ico file, but I'm more interested in not reinventing the wheel if possible.
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.