MethylBlue
  1. Filelight
  2. Codeine
  3. Wocka
  4. Blog
  5. Detritus
  6. Home
RSS

HICON to QPixmap

I found it necessary to convert an Windows HICON to a QPixmap so I could actually use it with Qt. Qt is great but lacks this for some reason. Here you go:

#define WIN32_MEAN_AND_LEAN //necessary to avoid compiler errors
#include <windows.h>

namespace Win32
{
	static
	QPixmap pixmap( const HICON &icon, bool alpha = true )

	{
		try {
			ICONINFO info;
			bool const b = ::GetIconInfo( icon, &info );

			QPixmap pixmap = alpha
					? fancyPants( info )
					: QPixmap::fromWinHBITMAP( info.hbmColor, QPixmap::NoAlpha );

			// gah Win32 is annoying!
			::DeleteObject( info.hbmColor );
			::DeleteObject( info.hbmMask );

			::DestroyIcon( icon );

			return pixmap;

		}
		catch (...)
		{
			return QPixmap();

		}
	}

	QPixmap
	associatedIcon( const QString &path )

	{
		// performance tuned using:
		//http://www.codeguru.com/Cpp/COM-Tech/shell/article.php/c4511/

		SHFILEINFO file_info;
		::SHGetFileInfo(

				(wchar_t*)path.utf16(),
				FILE_ATTRIBUTE_NORMAL,
				&file_info,

				sizeof(SHFILEINFO),
				SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_LARGEICON );

		return pixmap( file_info.hIcon );
	}

	static QPixmap
	fancyPants( ICONINFO const &icon_info )
	{
		int result;

		HBITMAP h_bitmap = icon_info.hbmColor;

		/// adapted from qpixmap_win.cpp so we can have a _non_ premultiplied alpha
		/// conversion and also apply the icon mask to bitmaps with no alpha channel
		/// remaining comments are Trolltech originals


	////// get dimensions
		BITMAP bitmap;
		memset( &bitmap, 0, sizeof(BITMAP) );

		result = GetObjectW( h_bitmap, sizeof(BITMAP), &bitmap );

		if (!result)
			return QPixmap();

		int const w = bitmap.bmWidth;
		int const h = bitmap.bmHeight;

	//////
		BITMAPINFO info;
		memset( &info, 0, sizeof(info) );

		info.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
		info.bmiHeader.biWidth       = w;

		info.bmiHeader.biHeight      = -h;
		info.bmiHeader.biPlanes      = 1;

		info.bmiHeader.biBitCount    = 32;
		info.bmiHeader.biCompression = BI_RGB;

		info.bmiHeader.biSizeImage   = w * h * 4;

		// Get bitmap bits
		uchar *data = (uchar*)qMalloc( info.bmiHeader.biSizeImage );

		result = GetDIBits( qt_win_display_dc(), h_bitmap, 0, h, data, &info, DIB_RGB_COLORS );

		QPixmap p;
		if (result) {
			// test for a completely invisible image
			// we need to do this because there is apparently no way to determine

			// if an icon's bitmaps have alpha channels or not. If they don't the
			// alpha bits are set to 0 by default and the icon becomes invisible :(
			// so we do this long check. I've investigated everything, the bitmaps
			// don't seem to carry the BITMAPV4HEADER as they should, that would tell
			// us what we want to know if it was there, but apparently MS are SHIT SHIT
			const int N = info.bmiHeader.biSizeImage;

			int x;
			for (x = 3; x < N; x += 4)

				if (data[x] != 0)
					break;

			if (x < N)
				p = QPixmap::fromImage( QImage( data, w, h, QImage::Format_ARGB32 ) );

			else {
				QImage image( data, w, h, QImage::Format_RGB32 );

				QImage mask = image.createHeuristicMask();
				mask.invertPixels(); //prolly efficient as is a 1bpp bitmap really

				image.setAlphaChannel( mask );
				p = QPixmap::fromImage( image );

			}

			// force the pixmap to make a deep copy of the image data
			// otherwise `delete data` will corrupt the pixmap
			QPixmap copy = p;
			copy.detach();

			p = copy;
		}

		qFree( data );

		return p;
	}
}

That took me a while to figure out. So enjoy the fruits of my labour!

4 Responses

  1. Hi,

    Thanks for the post! This method works great except for one thing: for old-style icons with 1-bpp transparency, the black color goes completely transparent, giving the icons a weird “cropped” look.

    For example, in Windows XP, the icon for .BAT files has a black border on the bottom and right. This border goes completely transparent at some point during the conversion to QPixmap. This is more noticeable for me, because I am using SHGFI_SMALLICON, so a sizeable chunk of some icons just goes “missing”!

    Any idea how to fix it? Thanks!

    Daniel Identicon Icon Daniel
  2. Diid you try both pixmap( foo, true ) and pixmap( foo, false ), as one handles transparency and the other doesn’t. I can’t remember exactly all the details as I wrote this a while ago. I recall I had to add the bool as there was no way to detect this I could determine.

    Max Howell Identicon Icon Max Howell
  3. Yeah, I tried both alpha settings, plus messed around a bit without much success. Luckily, it seems Qt is now doing the hard work for us in Qt 4.3. Check out the new convertHIconToPixmap() function in src\gui\image\qpixmap_win.cpp from the latest Qt snapshot. It includes all the necessary craziness to detect alpha and whatnot. Not sure when Qt 4.3 will be out, but you can just copy+paste that function and use it in an older Qt 4 program.

    Daniel Identicon Icon Daniel
  4. [...] Re: Retrieving a file’s icon yep forgot that. i prefer this solution: http://www.methylblue.com/blog/hicon-to-qpixmap/ [...]

    Retrieving a file's icon - Qt Centre Forum

Leave a Reply