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

HICON to QPixmap

December 29th, 2006

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 Comments »

Printing a QWidget

December 29th, 2006

For the benefit of Google:

QWidget *w = qtreeview->viewport();

QPrinter printer;
printer.setPageSize( QPrinter::A4 );

QPainter::setRedirected( w, &printer );
QPaintEvent e( w->rect() );
QApplication::sendEvent( w, &e );
QPainter::restoreRedirected( w );

Respond »