• Welcome to TechPowerUp Forums, Guest! Please check out our forum guidelines for info related to our community.

A bit of help GDI drawing in win32

streetfighter 2

New Member
Joined
Jul 26, 2010
Messages
1,655 (0.33/day)
Location
Philly
It's a nice coincidence that I should fall into a GDI trap just when ctrain is showing off his skills.

So I'm trying to create a tray icon (16x16) that contains whatever one or two digit number I want to throw at it. My requirements are that the icon appear as only plain text with a transparent background. My code works, but leaks like it was earning money by the byte :laugh:.

Here's the code:
Code:
HICON CreateIntIcon(int temp)
{
    HDC hMemDC, hdc, hdcMem, hdcMem2;
    DWORD dwWidth, dwHeight;
    HBITMAP hBitmap, hbmMask;
    BITMAP bm;
    void *lpBits;
    DWORD x,y;
    HICON hAlphaCursor = NULL;
    TCHAR buf[3]; 
    _stprintf(buf,"%d",temp);
    int size = _tcslen(buf);
	
    dwWidth  = 16;  // width of cursor
    dwHeight = 16;  // height of cursor

    hdc = GetDC(hDlg);

    hBitmap = CreateCompatibleBitmap ( hdc, dwWidth, dwHeight );

    hMemDC = CreateCompatibleDC(hdc);
    ReleaseDC(NULL,hdc);
	
    hbmMask = (HBITMAP)SelectObject(hMemDC, hBitmap);
    PatBlt(hMemDC,0,0,dwWidth,dwHeight,WHITENESS);
    SetTextColor(hMemDC,RGB(0,0,0));
    TextOut(hMemDC,0,0,buf,size);
    SelectObject(hMemDC, hbmMask);
    DeleteDC(hMemDC);

    // Create monochrome (1 bit) mask bitmap.  
    GetObject(hBitmap, sizeof(BITMAP), &bm);
    hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
	
    // Get some HDCs that are compatible with the display driver
    hdcMem = CreateCompatibleDC(0);
    hdcMem2 = CreateCompatibleDC(0);
    SelectObject(hdcMem, hBitmap);
    SelectObject(hdcMem2, hbmMask);
	
    // set the transparent color
    SetBkColor(hdcMem, RGB(0xFF,0xFF,0xFF));
	
    //Create the mask with some voodoo
    BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
    BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);

    //clean up
    DeleteDC(hdcMem);
    DeleteDC(hdcMem2);
    DeleteDC(hdc);
	
    ICONINFO ii;
    ii.fIcon = FALSE;  // Change fIcon to TRUE to create an alpha icon
    ii.xHotspot = 0;
    ii.yHotspot = 0;
    ii.hbmMask = hbmMask;
    ii.hbmColor = hBitmap;

    // Create the icon with transparent background
    hAlphaCursor = CreateIconIndirect(&ii);

    DeleteObject(hBitmap);
    DeleteObject(hbmMask);

    return hAlphaCursor;
}
I am calling DestroyIcon() on the handle returned from CreateIntIcon() when I'm done with it. I'm absolutely sure that the leak is in the terrible function I wrote.

Any help rewriting the function or fixing the leak would be greatly appreciated.

For reference I stole code from:
http://support.microsoft.com/kb/318876
http://www.winprog.org/tutorial/transparency.html
 

streetfighter 2

New Member
Joined
Jul 26, 2010
Messages
1,655 (0.33/day)
Location
Philly
Well I had a chance to look at it and I'm amazed to say that my hodgepodge code isn't leaking handles :slap:. It did have one problem with displaying single digit numbers that I fixed. I replaced the call to "_stprintf" with:
Code:
_sntprintf(buf, 3, "%d  ", temp);
Now single digit numbers display correctly but they're not centered; I'll fix that bit later (probably using DrawText()). :laugh: I also removed the call to PatBlt() as it was apparently useless.

I let the program run all night and it eventually consumed 15,575K of virtual memory and then stopped dead. It continued to consume working set memory though.

Since it (eventually) stopped consuming virtual memory I naturally assumed that if I gave it a smaller stack it wouldn't grow so large. I set the size of the stack for the thread to 1024K and ran the program all day and I'm still waiting on the results but it doesn't look fixed.

My next guess is to examine the heap, and then find out if there are better methods of allocating when creating bitmaps . . .

Any help is appreciated.

EDIT: I created an array with 100 icons generated from my CreateIntIcon() function and then used the array to swap the tray icons and it still eats memory. I'll take it as proof that it's just the Windows memory dispatcher and it's fuckin' with me but good. :(
 
Last edited:

ctrain

New Member
Joined
Jan 12, 2010
Messages
393 (0.08/day)
Do you see any weird memory usage if you comment out the BitBlt() calls?

Also be weary of SelectObject(), I believe you must select the original object (from the initial return) back in before you can clean up properly.
SelectObject(hdcMem, hBitmap);
SelectObject(hdcMem2, hbmMask);


You probably want to avoid stuff like PatBlt if you can, BitBlt() is as fast as you can ever hope to get. It also might be accelerated by video driver.
 
Last edited:

streetfighter 2

New Member
Joined
Jul 26, 2010
Messages
1,655 (0.33/day)
Location
Philly
Well I looked at it a bit more today . . .

I tried your suggestion ctrain, but it still leaks. I removed all the other code from the class I'm writing so it contains only a call to my CreateIntIcon() function followed by a call to DestroyIcon() on the returned handle just to isolate the problem. When I comment out the call to CreateIntIcon() it doesn't leak, so I know (or at least I think I know :)) that's where the problem lies.

I used VMMap to see where it was leaking too. I noticed that the program is leaking onto the heap (should've been obvious eh?:D). I'm thinking about calling GetProcessHeaps(), before and after calling CreateIntIcon(), and then using HeapDestroy() on any unique heaps. Unfortunately since the final version will be used in a multithreaded program it's not particularly safe solution. . .

I can't think of anything better though.
 
Last edited:

ctrain

New Member
Joined
Jan 12, 2010
Messages
393 (0.08/day)
ughgh this icon stuff is atrocious.

Try splitting out parts of that function that can manage being created at runtime and killed at closure, ie don't GetDC() every single time. Just set up for drawing once at start. Should make it easier to keep track of where it's happening.
 

streetfighter 2

New Member
Joined
Jul 26, 2010
Messages
1,655 (0.33/day)
Location
Philly
I got the hang of GDI drawing recently and as a test I rewrote my CreateIntIcon() function. I'm pretty sure it doesn't leak handles, has optimal performance, it's centered, it's thread-safe and it allows colors/fonts. I need to do some more testing on it (and some of the var names are funky) but here's the code:
PHP:
//Creates a DC for use in multithreaded programs (works in single threaded as well)
HICON TrayIcon::CreateIntIcon(int temp)
{
    HDC hMemDC, hdcMem2;
    HBITMAP hBitmap, hbmMask;
    HICON hAlphaCursor = NULL;
	TCHAR buf[3]; 
	_sntprintf(buf, 3, _T("%d"), temp);
	int size = _tcslen(buf);
	
	//Only safe way I could find to make a DC for multithreading
	HDC hdc = CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);

	//Makes it easier to center the text
	RECT cbox;
	cbox.left = 0;
	cbox.top = 0;
	cbox.right = 16;
	cbox.bottom = 16;
	
	//Create the text bitmap.
    hBitmap = CreateCompatibleBitmap ( hdc, cbox.right, cbox.bottom );
    hMemDC = CreateCompatibleDC(hdc);
	SelectObject(hMemDC, hBitmap);

	/// Draw the text bitmap
	HBRUSH hbrBkgnd = CreateSolidBrush(RGB(0,0,0));
	FillRect(hMemDC, &cbox, hbrBkgnd);
	DeleteObject(hbrBkgnd);
	SelectObject(hMemDC, GetStockObject(DEFAULT_GUI_FONT)); //using a stock font
	SetBkColor(hMemDC, RGB(0,0,0));
	SetTextColor(hMemDC, RGB(255,255,255));
	DrawText(hMemDC, buf, size, &cbox, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
	
	//Create monochrome (1 bit) mask bitmap.
    hbmMask = CreateBitmap(cbox.right, cbox.bottom, 1, 1, NULL);
    hdcMem2 = CreateCompatibleDC(0);
    SelectObject(hdcMem2, hbmMask);

    /// Draw transparent color and create the mask
    SetBkColor(hMemDC, RGB(0,0,0));
	BitBlt(hdcMem2, 0, 0, cbox.right, cbox.bottom, hMemDC, 0, 0, SRCCOPY);

	//clean up
    DeleteDC(hMemDC);
    DeleteDC(hdcMem2);
	
    ICONINFO ii;
    ii.fIcon = TRUE;
    ii.xHotspot = 0;
    ii.yHotspot = 0;
	ii.hbmMask = hbmMask;
    ii.hbmColor = hBitmap;

    // Create the icon with transparent background
    hAlphaCursor = CreateIconIndirect(&ii);

    DeleteObject(hBitmap);
	DeleteObject(hbmMask);
	DeleteDC(hdc);

    return hAlphaCursor;
}
If you're batch creating icons it's best to instantiate "hdc" outside and pass it to CreateIntIcon().
 
Last edited:
Top