1

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.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
  • You might convert `QPixmap` to a `HICON` handle (http://doc.qt.io/qt-5/qtwin.html#toHICON) and save it to an ICO file (https://stackoverflow.com/questions/2289894/how-can-i-save-hicon-to-an-ico-file). – vahancho Jan 21 '19 at 11:12
  • Thanks @vahancho, but how can I combine the HICONs? This method will give me one HICON per QPixmap (therefore one file per image), not a single .ico file (I'm updating the question to be cleared on that) – cbuchart Jan 21 '19 at 11:14
  • As far as I know there is no builtin support for such conversion in Qt. Here are listed supported image formats (for writing): http://doc.qt.io/qt-5/qimagewriter.html#supportedImageFormats . – vahancho Jan 21 '19 at 11:19
  • Thanks again @vahancho! Based on that I've implemented a writer and published it as an answer – cbuchart Jan 21 '19 at 12:02

1 Answers1

1

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.

cbuchart
  • 10,847
  • 9
  • 53
  • 93