Gdi+ and XP Icons -- A Solution

Hollow <[email protected]> Sun, 08 May 2005 22:30:47 -0400
Newsgroups gmane.comp.windows.shell-shocked.devel
Message-ID <000001c5543f$1d624720$790aa8c0@hollow>
I've been working with Gdi+ recently and is poor handling of XP icons has
been by far the most irksome aspect of it.  I got to thinking about this
today, and came up with the following idea.

At its most basic level, compositing a pixel with an alpha channel onto a
pixel with an alpha channel works as follows:

	C = bB + (1-b)aA

Where C is the composite color obtained by placing a pixel with color B and
alpha b over a pixel with color A and alpha a.  (This equation is valid on
the range [0, 1], and is applied to each channel of the color.)

Working with an icon one has access to B and C, and can chose A and a.  This
is adequate information to back calculate b.  Selecting A and a as 255 and
255 and plugging into the equation results in:

	b = (C-1)/(B-1)  

This back calculation is carried out by the extremely rough code at the end.


The question this leaves me with is the following, why doesn't Gdi+ do this
and why couldn't I find this on Google?  Is there a better way--why
shouldn't I do this?

Currently my code to draw an icon onto a Gdiplus::Bitmap is horrendous, but
it works.  This seems like an ideal replacement to me.

Eric 



// CODE -----------------------------------------
#include <iostream>
#include <windows.h>
#include <math.h>
#include <gdiplus.h>
using namespace std;


bool GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
	using namespace	Gdiplus;
	UINT num = 0;		// number of	image encoders
	UINT size =	0;		// size of the image	encoder
array in bytes

	ImageCodecInfo* pImageCodecInfo = NULL;

	GetImageEncodersSize(&num, &size);
	if(size == 0)
		return false; // no codecs installed.

	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL)
		return false;  // out of memory

	GetImageEncoders(num, size, pImageCodecInfo);

	for(UINT j = 0; j < num; ++j)
	{
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
		{
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return true;	// Success
		}	
	}

	free(pImageCodecInfo);
	return false;  // codec doesn't exist
}

int main()
{
	class UsingGdiplus
	{
	public:
		UsingGdiplus()
		{
			Gdiplus::GdiplusStartupInput gpsi;
			Gdiplus::GdiplusStartup(&token, &gpsi, NULL);
		}
		~UsingGdiplus() { Gdiplus::GdiplusShutdown(token); }
	private:
		ULONG_PTR token;
	} ugp;

	HICON ico = 0;
	if(ExtractIconEx("c:\\windows\\system32\\shell32.dll",3, &ico, 0, 1)
!= 1)
	{
		cout << "extraction failed" << endl;
		exit(0);
	}
			
	BITMAPINFOHEADER bmpInfo32;
	bmpInfo32.biSize	= sizeof(BITMAPINFOHEADER);
	bmpInfo32.biWidth	= 32;
	bmpInfo32.biHeight	= 32;
	bmpInfo32.biPlanes	= 1;
	bmpInfo32.biBitCount	= 24;
	bmpInfo32.biCompression	= BI_RGB;
	bmpInfo32.biSizeImage	= 0;
	bmpInfo32.biXPelsPerMeter	= 0;
	bmpInfo32.biYPelsPerMeter	= 0;
	bmpInfo32.biClrUsed	= 0;
	bmpInfo32.biClrImportant	= 0;

	DWORD* bits;

	HDC dc = CreateCompatibleDC(0);
	HBITMAP bmp = CreateDIBSection(dc, (BITMAPINFO*)&bmpInfo32,
DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
	if(!bmp)
	{
		cout << "creating dibsection failed" << endl;
		DeleteDC(dc);
		DestroyIcon(ico);
		exit(0);
	}
	HBITMAP hbOld = (HBITMAP)SelectObject(dc,bmp);
	
	HBRUSH brush = CreateSolidBrush(RGB(0xff,0xff,0xff));
	DrawIconEx(dc, 0, 0, ico, 0, 0, 0, brush, DI_NORMAL);
	DeleteObject(brush);
	
	Gdiplus::Bitmap gpb(ico);
	DestroyIcon(ico);
	
	for(int i = 0; i < 32; ++i)
	{
		for(int j = 0; j < 32; ++j)
		{
			Gdiplus::Color gpc;
			gpb.GetPixel(i,j,&gpc);
			COLORREF B = gpc.ToCOLORREF();
			COLORREF C = GetPixel(dc,i,j);
			if(B != C && C != RGB(0xff,0xff,0xff))
			{
				double Br = (double)(GetRValue(B)-255);
				double Bg = (double)(GetGValue(B)-255);
				double Bb = (double)(GetBValue(B)-255);
				double Cr = (double)(GetRValue(C)-255);
				double Cg = (double)(GetGValue(C)-255);
				double Cb = (double)(GetBValue(C)-255);

				int alpha =  (int)((floor(255*Cr/Br) +
floor(255*Cg/Bg) + floor(255*Cb/Bb))/3.0);
				gpb.SetPixel(i,j, Gdiplus::Color(alpha,
gpc.GetR(), gpc.GetG(), gpc.GetB()));
			}
		}
	}
	bmp = (HBITMAP)SelectObject(dc, hbOld);
	DeleteObject(bmp);
	DeleteDC(dc);

	CLSID pngClsid;
	GetEncoderClsid(L"image/png", &pngClsid);
	gpb.Save(L"g:\\fixed.png", &pngClsid);
	return 0;
}


__________________________________________________
Subscription options and archive:
http://www.freelists.org/list/shell-coding