C++ GDI+ Objects Memory leak/too many objects? - c++

ive been making a c++ drawing program and i have a part where im drawing using gdi+ inside a function so i need to declare my graphics object every time i call the function.. this obviously is wrong and causes a leak somewhere so after some calls it gets slower and then suddenly stops drawing all together ,as expected.
im trying to clear my objects every time it finishes drawing but it doesnt seem to solve the issue. i thought maybe the PEN objects are part of the issue but some insight would be very helpful
my code:
void function()
{
DWORD pdwGduStartup;
GdiplusStartupInput GdiStartupInp;
GdiplusStartup(&pdwGduStartup, &GdiStartupInp, NULL);
Pen pnPen_Blue(Gdiplus::Color(255, 0, 0, 255), 2.0F);
Pen pnPen_Green(Gdiplus::Color(255, 255, 0, 255), 2.0F);
LPCSTR LGameWindow = "MyWindow";
HWND hGameWindow = FindWindow(NULL, LGameWindow);
Graphics graphics(GetDC(hGameWindow));
for (int n=10; n>0; n--)
graphics.DrawLine(&pnPen_Green,0, 0, 0, n);
GdiplusShutdown(pdwGduStartup);
graphics.Flush();
}
thanks alot!
edit: added release DC did not do anything!
DWORD pdwGduStartup;
GdiplusStartupInput GdiStartupInp;
GdiplusStartup(&pdwGduStartup, &GdiStartupInp, NULL);
Pen pnPen_Blue(Gdiplus::Color(255, 0, 0, 255), 2.0F);
Pen pnPen_Green(Gdiplus::Color(255, 255, 0, 255), 2.0F);
LPCSTR LGameWindow = "MyWindow";
HWND hGameWindow = FindWindow(NULL, LGameWindow);
HDC GDC = GetDC(hGameWindow);
Graphics graphics(GDC);
ReleaseDC(hGameWindow, GDC);
GdiplusShutdown(pdwGduStartup);
graphics.Flush();
My GDI objects still rises up to 10,000!

GdiplusShutdown must be called after all GDI+ objects are removed. In your code Pen and Graphics destructors are called after GdiplusShutdown, when the objects go out of scope.

I guess the problem is in GetDC. You should call ReleaseDC or use WTL smart pointers.
As I remember such things where easy to debug with task manager. It shows HANDLE count as well as GDI object count for a process, so you can see what lines generate new objects.

I didnt code my GDI+ right logically. in the logic first you need to create the graphics object, and only then loop draw on it. anything else is WRONG and code wont fix it!

Try something like this, use pointers that you can easily delete when done.
DWORD pdwGduStartup;
GdiplusStartupInput GdiStartupInp;
GdiplusStartup(&pdwGduStartup, &GdiStartupInp, NULL);
Pen pnPen_Blue* = new Pen(Gdiplus::Color(255, 0, 0, 255), 2.0F);
Pen pnPen_Green* = new Pen(Gdiplus::Color(255, 255, 0, 255), 2.0F);
LPCSTR LGameWindow = "MyWindow";
HWND hGameWindow = FindWindow(NULL, LGameWindow);
HDC GDC = GetDC(hGameWindow);
Graphics* g = new Graphics(GDC);
// Do stuff here
ReleaseDC(hGameWindow, GDC);
delete pnPen_Blue;
delete pnPen_Green;
g->Flush();
delete g;
delete GDC; // not sure
GdiplusShutdown(pdwGduStartup);

Related

Using the Gdiplus outside of the main [duplicate]

I'm using the GDI+ API just to display an image (bmp) on the screen. Here is the code of the function doing the task:
void drawImg_ArmorInfo_PupupWnd(HDC hdc) {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Image* image = new Image(L"C:/Users/Darek/F2 stuff/art/intrface/armor_info_1.bmp");
int a1 = image->GetWidth();
int a2 = image->GetHeight();
Graphics graphics(hdc);
graphics.DrawImage(image, 0, 0);
delete image;
GdiplusShutdown(gdiplusToken);
}
The problem is that I'm getting an exception: Access violation writing location 0xXXXXXXXX after calling the GdiplusShutdown function. What's interesting when I comment out the
Graphics graphics(hdc);
graphics.DrawImage(image, 0, 0);
part the program runs without any problems but, of course, the image isn't drawn. When I comment out the call to GdiplusShutdown function - no problems again.
What's wrong with the code and what can be the reason of the problem here?
graphics falls out of the scope after the GdiplusShutdown so it cannot destruct correctly.
Try this:
Graphics * graphics = new Graphics(hdc);
graphics->DrawImage(image, 0, 0);
delete graphics;

Using the GDI+ API to draw an image

I'm using the GDI+ API just to display an image (bmp) on the screen. Here is the code of the function doing the task:
void drawImg_ArmorInfo_PupupWnd(HDC hdc) {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Image* image = new Image(L"C:/Users/Darek/F2 stuff/art/intrface/armor_info_1.bmp");
int a1 = image->GetWidth();
int a2 = image->GetHeight();
Graphics graphics(hdc);
graphics.DrawImage(image, 0, 0);
delete image;
GdiplusShutdown(gdiplusToken);
}
The problem is that I'm getting an exception: Access violation writing location 0xXXXXXXXX after calling the GdiplusShutdown function. What's interesting when I comment out the
Graphics graphics(hdc);
graphics.DrawImage(image, 0, 0);
part the program runs without any problems but, of course, the image isn't drawn. When I comment out the call to GdiplusShutdown function - no problems again.
What's wrong with the code and what can be the reason of the problem here?
graphics falls out of the scope after the GdiplusShutdown so it cannot destruct correctly.
Try this:
Graphics * graphics = new Graphics(hdc);
graphics->DrawImage(image, 0, 0);
delete graphics;

initialise an MFC Cimage to a solid colour

I don't use c++ or MFC/ATL etc so assume I know nothing.
The whole picture is that I need to take accept a PNG with a transparency layer and write it out as a JPEG with specified compression to pass to another system.
What I would like to know here, is how can I initialize a target CImage structure with solid white?
I've done this so far (yes I know it has other stylistic issues)
void ClipBoardFuncs::savePNGASJPEG(char filePath[256], char errorBuff[256]) {
int sizeOfErrorBuff = 256;
CImage imagePNG;
CImage imageJPG;
int xPNG, yPNG = 0;
imagePNG.Load(filePath);
xPNG = imagePNG.GetWidth();
yPNG = imagePNG.GetHeight();
//Create my target JPG same size and bit depth specifiying that there is no alpha channel (dwflag last param)
imageJPG.Create(xPNG, yPNG, imagePNG.GetBPP(), 0);
//Let there be white....
for (int x = 0; x <= xPNG; x++)
{
for (int y = 0; y <= yPNG; y++)
{
imageJPG.SetPixelRGB(x, y, 255, 255, 255);
}
}
//Copy the image over onto on the white
//BitBlt copies everything....
//imagePNG.BitBlt(imageJPG.GetDC(), 0, 0);
//Draw is more like the C# draw examples I've seen
imagePNG.Draw(imageJPG.GetDC(), 0, 0);
imageJPG.ReleaseDC();
HRESULT hr = NULL;
hr = imageJPG.Save(filePath, Gdiplus::ImageFormatJPEG);
imagePNG.Destroy();
imagePNG.ReleaseGDIPlus();
imageJPG.Destroy();
imageJPG.ReleaseGDIPlus();
LPCTSTR error = _com_error(hr).ErrorMessage();
strncpy_s(errorBuff, sizeOfErrorBuff, _com_error(hr).ErrorMessage(), _TRUNCATE);
}
The lovely C# people have this answer:
Convert Transparent PNG to JPG with Non-Black Background Color
But I need the c++ MFC solution to use as an exported function in a DLL.
By exported Function I mean the same architecture as you would find in kernel32.dll - sorry I do not know the terminology to differentiate that kind of DLL from one stuffed with COM objects.
Can anyone suggest a faster way to initialize the imageJPEG structure to solid white than the nested x/y for loops I have here?
Cheers
4GLGuy
The initialization can be done with Rectangle or FillRect (for this, FillRect is probably preferred--Rectangle draws an outline of the rectangle in the current pen color, which we probably don't want).
So, the sequence looks something like this:
CImage png;
png.Load(pDoc->filename);
CRect rect{ 0, 0, png.GetWidth(), png.GetHeight() };
CImage jpeg;
jpeg.Create(rect.Width(), rect.Height(), png.GetBPP());
auto dc = jpeg.GetDC();
HBRUSH white = CreateSolidBrush(RGB(255, 255, 255));
FillRect(dc, &rect, white);
png.Draw(dc, 0, 0);
jpeg.ReleaseDC();
jpeg.Save(L"Insert File name here", Gdiplus::ImageFormatJPEG);
jpeg.Destroy();
jpeg.ReleaseGDIPlus();
png.ReleaseGDIPlus();
GDI+ is "smart" enough that when you do a .Draw with an image that has an alpha channel, it takes that channel into account, without your having to use TransparentBlt (or anything similar).
SetTransparentColor will not work for what you're trying to do here. SetTransparentColor is for an image that does not have an alpha channel ("transparency layer"). You then use it to choose a color that will be treated as if it were transparent--which can certainly be useful, but isn't what you want here.
You can use memset instead, but only for colors where the red, green, and blue channels all have the same values (i.e., black, white, or some shade of grey). Otherwise, you can do the fill on your own with a nested loop, but in most cases you probably want to use FillRect instead (it may be able to use graphics hardware for acceleration, where the loop will pretty dependably just run on the CPU--worst case, they're both about the same speed, but in some cases FillRect will be faster).
imagePNG.Draw(imageJPG.GetDC(), 0, 0);
Each call to GetDC must have a subsequent call to ReleaseDC. See also CImage::GetDC documentation.
CImage::GetDC provides a handle to memory device context. This handle can be used to draw using standard GDI functions. The handle should later be cleaned up with CImage::ReleaseDC.
CImage::Draw may not know what the transparent color is. You have to use TransparentBlt to tell it what the transparent color is. For example, to replace red color with white color:
HDC hdc = imageJPG.GetDC();
CDC dc;
dc.Attach(hdc);
CRect rc(0, 0, xPNG, yPNG);
dc.FillSolidRect(&rc, RGB(255, 255, 255));
dc.Detach();
imagePNG.TransparentBlt(hdc, rc, RGB(255, 0, 0));//replace red with white
imageJPG.ReleaseDC();
...
imageJPG.Save(...);
Or just use CImage::SetTransparentColor:
imagePNG.SetTransparentColor(RGB(255, 0, 0));
imagePNG.Draw(hdc, rc);
for (int x = 0; x <= xPNG; x++){...}
To do this using a loop, change the condition in the loop to x < xPNG and x < yPNG.

How to draw on the windows desktop using the GDI API? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Draw on screen with GDI+ (or GDI) similar to Inspect
I'm attempting to write a snake game that has no windows, but freezes the foreground and draws the snake on top of it. When the game ends the foreground should be unfrozen.
I have written some testing code that is supposed to draw a square on the foreground, but all it seems to do is freeze the desktop for a second and freeze the window in the foreground until I minimize, maximize, close it, or bring another window into the foreground, and it doesn't draw anything. In the code, I try to store a bitmap of the desktop so that I can essentially reset it to it's original state and paint the square in a different position. Can anybody spot the problem with my code?
//Handle to the desktop window
HWND hDesktopWindow = GetDesktopWindow();
//Lock the window to prevent other applications drawing on it
if(!LockWindowUpdate(hDesktopWindow)){
return 1;
}
//Calling GetDC with argument NULL retrieves the desktop's DC
HDC hdcDesktop = GetDCEx(hDesktopWindow, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE);
//Create a compatible DC to allow us to store a bitmap of the desktop
HDC hdcCompatible;
if((hdcCompatible = CreateCompatibleDC(hdcDesktop)) == NULL){
return 1;
}
//Create a compatible bitmap with the same dimensions as the desktop
HBITMAP hScrBitmap;
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
if((hScrBitmap = CreateCompatibleBitmap(hdcDesktop, cx, cy)) == NULL){
return 1;
}
//Select the bitmap into the compatible DC
SelectObject(hdcCompatible, hScrBitmap);
//Copy the Desktop into the bitmap
if(!BitBlt(hdcCompatible, 0, 0, cx, cy, hdcDesktop, 0, 0, SRCCOPY)){
return 1;
}
//Create a DC compatible with the bitmaps DC for drawing the rectangle
HDC hdcRectangle;
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
//Create a compatible bitmap for the rectangle to be drawn in
HBITMAP hRectangleBitmap;
if(!CreateCompatibleBitmap(hdcRectangle, 100, 100)){
return 1;
}
//Fill the rectangle bitmap
if(!FloodFill(hdcRectangle, 0, 0, RGB(255,0,0))){
return 1;
}
//Copy the rectangle onto the desktop bitmap
if(!BitBlt(hdcCompatible, 100, 100, 100, 100, hdcRectangle, 0, 0, SRCCOPY)){
return 1;
}
//Copy the bitmap onto the actual desktop
if(!BitBlt(hdcDesktop, 0, 0, cx, cy, hdcCompatible, 0, 0, SRCCOPY)){
return 1;
}
//Allow time to view the result
Sleep(1000);
//Allow other applications to draw on the desktop again
LockWindowUpdate(NULL);
//Cleanup
ReleaseDC(hDesktopWindow, hdcDesktop);
DeleteDC(hdcCompatible);
DeleteObject(hScrBitmap);
Any help would be greatly appreciated :)
Trying to do this directly on the desktop is going to be problematic. You'd be better off by taking a snapshot of the desktop, then create a window that's the size of the whole desktop, copy the snapshot to the window, and do all your drawing there. (This was a common trick done in old screensavers that did things like "erode" the desktop.)
You don't own the desktop window, so you'll always have problems with invalidation and repaints.
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
When you write C code like this then a single-point-of-return tends to be very important. A call like this is going to return FALSE, can't cast a HBITMAP to HDC, and the show is over badly. No diagnostic and no call to unlock again.
Favor the C++ RAII pattern to ensure that you always unlock:
class DesktopLocker {
public:
DesktopLocker() { LockWindowUpdate(GetDesktopWindow()); }
~DesktopLocker() { LockWindowUpdate(NULL); }
};
void foo() {
DesktopLocker lock;
// etc...
}
There's not much wisdom of painting directly to the desktop window, there's little guarantee that whatever you draw will last. Sleeping and locking updates are just band-aids. Take a look at the source of Rainmeter, it's well done.

C++ GDI+ Drawing Image on layered window not working

So I've found a lot of code samples, guides, and answers on SO about drawing an image to a layered window. I've tried using pure HBITMAPS and the WIC libs to draw, and now I'm on to GDI+ to draw (which is much simpler and is seemingly easier to use, and thus far it has solved a lot of errors that were caused by faulty WIC code).
I'm currently stuck on UpdateLayeredWindow. No matter what I try I can't get it to work. Right now, it's returning 87, or ERROR_INVALID_PARAMETER. The question is, which one is incorrect? I'm stumped! The below code seems to be the solution other than the fact that UpdateLayeredWindow is refusing to work.
What am I doing wrong?
Here is the code that sets up the HDC/bitmap information/graphics object.
// Create DC
_oGrphInf.canvasHDC = GetDC(_hwndWindow);
// Create drawing 'canvas'
_oGrphInf.lpBits = NULL;
_oGrphInf.bmpCanvas = CreateDIBSection(_oGrphInf.canvasHDC,
&_oGrphInf.bmpWinInformation, DIB_RGB_COLORS,
&_oGrphInf.lpBits, NULL, 0);
// Create graphics object
_oGrphInf.graphics = new Gdiplus::Graphics(_oGrphInf.canvasHDC);
The above works fine - I step through it and all of the pointers work.
And here is the method that draws the PNG.
void Splash::DrawPNG(PNG* lpPNG, int x, int y)
{
LOGD("Drawing bitmap!");
HDC hdcMem = CreateCompatibleDC(_oGrphInf.canvasHDC);
// Select
HBITMAP bmpOld = (HBITMAP)SelectObject(hdcMem, _oGrphInf.bmpCanvas);
Gdiplus::Color trans(0, 0, 0, 0);
_oGrphInf.graphics->Clear(trans);
_oGrphInf.graphics->DrawImage(lpPNG->GetImage(), x, y);
_oGrphInf.graphics->Flush();
SIZE szSize = {_oGrphInf.bmpWinInformation.bmiHeader.biWidth,
_oGrphInf.bmpWinInformation.bmiHeader.biHeight};
// Setup drawing location
POINT ptLoc = {0, 0};
POINT ptSrc = {0, 0};
// Set up alpha blending
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.BlendFlags = 0;
// Update
if(UpdateLayeredWindow(_hwndWindow, _oGrphInf.canvasHDC, &ptLoc,
&szSize, hdcMem, &ptSrc,
(COLORREF)RGB(0, 0, 0),
&blend, ULW_ALPHA) == FALSE)
LOGE("Could not update layered window: %u", GetLastError());
// Delete temp objects
SelectObject(hdcMem, bmpOld);
DeleteObject(hdcMem);
DeleteDC(hdcMem);
}
Pulling my hair out! Help?
EDIT: I just decided to re-write the call to UpdateLayeredWindow function, which solved the incorrect parameter issue. Here is what I came up with. However, it still does not work. What am I doing wrong?
UpdateLayeredWindow(_hwndWindow, _oGrphInf.canvasHDC,
NULL, NULL, hdcMem, &ptLoc,
RGB(0, 0, 0), &blend, ULW_ALPHA)
For alpha information to be preserved in drawing operations, you have to make your Graphics object based on a memory-backed Bitmap object, not an HDC, and of course your Bitmap needs to be in a format with an alpha channel.
You'll need to use this Bitmap constructor: http://msdn.microsoft.com/en-us/library/ms536315%28v=vs.85%29.aspx
Just give that a stride of 0, a pointer to your DIB's bits, and PixelFormat32bppPARGB.
Then use Graphics::FromImage to create your Graphics object.
I've never used UpdateLayeredWindow, so I can't verify that that side of it is correct.