Click here to Skip to main content
15,881,882 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
INTRODUCTION AND RELEVANT INFORMATION:

FORMER TITTLE OF THE QUESTION:
Window produces flicker-like ( slow repainting-like ) effect when resized


I have a complex painting to implement in my main window’s WM_PAINT handler. I have submitted a link to the picture that describes it, bellow:

http://pbrd.co/1hGVPPu[^]

Logo’s marked with 1 and 2, are drawn using GDI+. Logo marked as 1, is metafile, and logo marked with 2 is PNG.

If I leave out drawing of the second logo, my window doesn’t flicker, yet if I add the drawing of the second logo in my WM_PAINT, the following effect, illustrated with the picture below, occurs ( this is just a sketch made in Paint, but hopefully it will clear things up ):

http://pbrd.co/1hGW6C3[^]

It seems as if the repainting is slow.

IMPORTANT NOTE: This effect happens on child windows, the background is painted properly.

Just in case it matters, the information about child windows:

All 5 child windows are static controls.

Static controls with blue gradient are painted with double buffering, using GDI, in WM_CTLCOLORSTATIC.

Orange static control is subclassed, and is ownerdrawn.

IMPORTANT NOTE: I must say that this is my first time using GDI+.

In my WM_PAINT handler, I have made compatible memory DC, required for double buffering, like this:

C++
HDC hdc = BeginPaint( hwnd, &ps), hdcMemImg, MemDC;
MemDC = CreateCompatibleDC(hdc); // back buffer.


Compatible bitmap has dimensions of main window’s client area.

As I’ve said, everything seems to work fine, since I use GDI and double buffering to paint/draw.

When I need to paint the logo’s marked with 1 and 2, I do it with this code:
C++
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    Image image(L".\\resources\\BG.emf"), image1(L".\\resources\\RGF.png");
    switch(msg)
    {
    case WM_ERASEBKGND:
       return (LRESULT)1;
    case WM_PAINT:
       {
          PAINTSTRUCT ps;
          HDC hdc = BeginPaint( hwnd, &ps), hdcMemImg, MemDC;
          MemDC = CreateCompatibleDC(hdc); // back buffer.
          
          // CreateCompatibleBitmap and other usual stuff

          /******************** left logo *******************/

          Graphics graphics( MemDC );
             
          //============= aspect ratio ================//
             
          UINT o_height = image.GetHeight(),
               o_width =  image.GetWidth();
              
          INT n_width = 80;
          INT n_height = 100;

          double ratio = ( (double)o_width ) / ( (double)o_height );
              
          if (o_width > o_height)
          {
             // Resize down by width
             n_height = static_cast<UINT>( ( (double)n_width ) / ratio );
          }
          else
             n_width = static_cast<UINT>(n_height * ratio);
              
          //========== ensure high graphic quality ======================//
             
          graphics.SetSmoothingMode( SmoothingModeHighQuality );
          graphics.SetInterpolationMode( InterpolationModeHighQualityBicubic );
          graphics.DrawImage( &image, r.left + 5, r.top + 10, n_width, n_height );
             
          /******************** right logo *******************/
              
          Graphics graphics1( MemDC );
             
          //============= aspect ratio ================//
              
          o_height = image1.GetHeight(), 
          o_width = image1.GetWidth();
          n_width = 90;
          n_height = 100;
          ratio = ( (double)o_width ) / ( (double)o_height );
          if (o_width > o_height)
          {
             // Resize down by width
             n_height = static_cast<UINT>( ( (double)n_width ) / ratio );
          }
           else
             n_width = static_cast<UINT>(n_height * ratio);
             
          //=========== ensure high graphic quality ============//
          
          graphics1.SetSmoothingMode( SmoothingModeHighQuality );
          graphics1.SetInterpolationMode( InterpolationModeHighQualityBicubic );
          graphics1.DrawImage( &image1, r.right - r.left - 90, r.top + 10,
             n_width, n_height );
              
          // Then do BitBlt of MeDC to hdc, and clean it up


If additional code snippets are required, ask and I will edit my post, but for now, they are omitted to keep the post short and concise.

I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.

My CPU is single core ( 2,67 GHz ), and I have 768 MB of RAM.

IMPORTANT UPDATE:

When I turn on Task Manager, I can see that the memory consumption of my application sky rockets.

It never drops, it always grows, especially when I resize the window.

Detailed description of my efforts to solve this can be found bellow.

I have decided to take the advice of the more experienced and better developers, and will provide link to the demo project.

One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here http://www.resedit.net/[^].

Here is the link to the demo project ( all the necessary comments and explanations are in the projects comments ):http://www.uploadmb.com/dw.php?id=1382012579[^].

MY EFFORTS TO SOLVE PROBLEM:

Browsing through the Internet, SO archive, CodeProject, and CodeGuru, I was unable to see the problem-it seems ( and I believe too ), that the principles of double buffering in GDI+ and GDI are the same.

IMPORTANT UPDATE:

I have downloaded VLD from http://vld.codeplex.com/[^], followed their instructions, but VLD didn't detect any memory leaks.

Also, I have made a copy of the project and have deleted static controls, leaving only the window's background to ease my debugging.

Memory consumption described above still happens.

It seems to stop only if I don't draw logos.

QUESTION:

Since this is my first time using GDI+, is there something that I’m missing, in view of releasing/deleting some GDI+ object or something similar ( all the GDI+ code for drawing in WM_PAINT is submitted )?

Again, I believe that my other painting code works well, but I will post it if required.

UPDATE:
Regarding the updates provided above, what should I do to fix the problem of excessive memory consumption described above?

Can someone review small demo project provided above, and try to give me useful advice?


Thank you.

Regards.
Posted
Updated 17-Oct-13 6:07am
v6
Comments
Captain Price 11-Oct-13 2:41am    
This seems to be a complex UI. One Window procedure is not the only thing which causes flickering . If you can upload complete project source, you are more likely to get right answers !
AlwaysLearningNewStuff 11-Oct-13 2:45am    
Thank you for your comment, I was afraid that size of the code will deter members from trying to help me.

Still, if you believe that it will be beneficial, I will do as you have advised me.

Thank you again.

Regards.
Captain Price 11-Oct-13 2:59am    
I believe you meant posting source on this page as text and i did not meant that. You can zip your project, upload it somewhere else, and provide with a link.
AlwaysLearningNewStuff 11-Oct-13 4:57am    
Unfortunately, this is a commercial product...
Captain Price 11-Oct-13 5:19am    
Then upload only the source of the User Interface.

Drawing is "heavy lifting" so you should optimize your drawing code, by using global or static Images and image information.

Consider a global MemDC in which are booth images (at different coordinates) and other optimizations. ;-)
 
Share this answer
 
Comments
Captain Price 16-Oct-13 4:11am    
booth images ?
Anyway, a +5
AlwaysLearningNewStuff 17-Oct-13 8:08am    
I have updated my question with new findings, and have provided a link to the demo project were I have isolated the problem to drawing the background of the main window.

Static controls are deleted for easier debugging.

Hopefully you can find some time to give it a quick glance, I would appreciate it.

Thank you for your efforts.

Regards.
In your window procedure, change this line at the top:
C++
Image image(L".\\resources\\BG.emf"), image1(L".\\resources\\RGF.png");


to this:
C++
static Image image(L".\\resources\\BG.emf"), image1(L".\\resources\\RGF.png");

Then debug your program, open Task Manager and see the difference (Memory consumption).

Your line at the beginning of the WndProc creates 2 images every time WndProc receives a message. So by the time you close your program there might be about 100,000 images created, which is very bad.

Now that is fixed, but still there're some flickering because you create 2 Memory DCs and 2 Bitmaps every time your window receives a WM_PAINT message. In this case you're deleting these 4 items properly, but it would be faster as the other post states, to use your memory DC as a global (or static) variable.

The link i provided you in my other answer discuses all these matters. You should read that again from the beginning to the end, very carefully.

Tip:
Create your main memory-dc globally, and select a bitmap as big as the screen to it, just once (during WM_CREATE). And delete the DC and the bitmap when your program terminates (During WM_DESTROY message).

C++
HDC hdcMem;
HBITMAP hBitmap;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
   case WM_CREATE:
      {
	  HDC hdc = GetDC(hwnd);

	  hdcMem = CreateCompatibleDC(hdc);

	  int w = GetSystemMetrics(SM_CXSCREEN);
          int h = GetSystemMetrics(SM_CYSCREEN);
	  hBitmap = CreateCompatibleBitmap(hdc, w, h);
          SelectObject(hdcMem, hBitmap);

	  ReleaseDC(hwnd, hdc);
      }
   case WM_DESTROY:
      DeleteDC(hdcMem);
      Deleteobject(hBimap);
   ///...........


Edit:
I optimized your main.cpp. Now there is no flickering. Just copy the code in the following link to main.cpp and compile:
http://pastebin.com/JHaY8hZR[^]

I used 4 memory-DCs in the above source file. 3 MemoryDCs for the 3 images, they are created in WM_CREATE and destroyed in WM_DESTROY (Does not change throughout the application life-time). And one memory-DC (main memory DC) to draw on each WM_PAINT message. This memory DC is also created in WM_CREATE as big as the screen size and destroyed in WM_DESTROY.

So the only scenario happens in WM_PAINT is:
-- BitBlt images in 3 memory-DCs to main memory-DC.
-- Draw other things to main memory DC
-- BitBlt main memory dc to screen.

In your version of the application all of the following things happened during every and each WM_PAINT message:
-- Create a main memory DC
-- Create a memory dc for image handling
-- Load the 3 images, draw them to image handling dc and BitBlt them to main memory DC (One by one)
-- Draw other things to main memory DC
-- BitBlt the main memory DC to screen.
-- Unload 3 images and delete all created memory DCs.

So, you can clearly see why my version does not flicker.
 
Share this answer
 
v4
Comments
AlwaysLearningNewStuff 17-Oct-13 14:47pm    
Thank you so much for reviewing my code!

It seems OK now, but memory increase is now around 20K.

It does drop sometimes by roughly 20K, but mainly it increases for +20K on each resize and stays that way. Is that normal?

I have read that article before, but I do not know how to implement the suggestions in the above post and in the article yet.

I will try and post further questions if I get stuck.

I have tried to make Image variables global, and initiate them in WM_CREATE and it gave roughly the same result, as making them static.

As for slight memory increase, maybe I should make bitmaps/icons from the window procedure to be global and static variables as well?

At this moment, I will try to make memory DC's and compatible bitmaps global and static, that should speed up my code.

I will report my results as soon as possible.

Thank you so much again, I really appreciate your help a lot.

As soon as I get satisfying results I will accept your solution and give you +5, I think that you have earned it.

Hopefully it will not take long for me to optimize my code.

Regards.
Captain Price 20-Oct-13 0:01am    
See my edit.
AlwaysLearningNewStuff 20-Oct-13 3:01am    
Thank you so much!

I can learn a lot from this!

After thinking about it, your previous question did answer why my code consumes too much memory, so I think that this question is answered.

As for WM_PAINT optimization-I think that this should be separate question, therefore I will accept your answer ( +5 from me, as I think you deserved it ).

Regards.
Captain Price 20-Oct-13 3:10am    
thanks :)
AlwaysLearningNewStuff 2-Nov-13 1:40am    
I have posted a question about optimizing this code so it works faster and without slight flickering.

If you decide to take a look, you will see that I didn't implement all of your improvements.

The reason for this is that I want to give you a chance to see the entire code, since there may be a possibility to improve it even more.

Here is the link to the question, hopefully you can find some time to help with constructive advice as you did here:

http://www.codeproject.com/Questions/677463/Inexperienced-win32-developer-asks-for-help-in-opt[^]

Thanks again.

Regards.
Hi, there are a number of different issues with your ccode - all of which degrade performance in one way or another.

When I first tried your program, I resized it continuously for about 20 seconds then logo1 and logo2 dissapeared! Combined with your question this led me to suspect gdi leaks on a wholesale scale.

You have at least 4 leaks and something else that probably warrant some attention.

1) As Pravinda Ama mentioned, you're leading loading the two images from disk each time WindowProc is called. You're better-off making them static and only loading them in the WM_INIT handler, deleteing them in WM_DESTROY. So, there's 2 leaks.

2) Next, you're leaking fonts. After you use the fonts you do select them back out of the DC, but you forget to delete them. DeleteObject(hf); will of course, do the trick - just add the call to delete the font to each of the times you use one. This is another 2 leaks.

3) Next, I notice that you use a LOGBRUSH or something similar for drawing the gridlines. It seems like a good case for using HPEN hPenPozadina = CreatePen(PS_SOLID, 1, RGB(216,216,216));, which I suspect would work faster than a (potentially) more complex brush.

However, the largest performance-killer that I've investigated has been the drawing of icon2 - RGF.png. Wow! The image itself is 997x1200 x 32bits = 1,196,400 pixels = 4,785,600 bytes - this is for an image that has a display-size of about 90x100x32bits = 9,000 pixels = 36,000 bytes. The image is always drawn the same size - there is no dynamic scaling. It's a really bad idea to do this. **Every** time the window is drawn, you're scaling 4,785,600 bytes down to 36,000 bytes - you're only using 0.75% of it - yes! You're wasting 99.25% of the effort that goes into drawing icon 2.

I resized this image with an image editor, down to 83x100 pixels (to keep within 90x100 pixel limit imposed in the code) Now, drawing the window seems just as fast with the icon as without.
You should resize your image resources appropriately, including several copies of the image at different resolutions if this is how your program uses it. This is just the same idea behind applications icons - it's not uncommon to see 16x16, 32x32, 48x48 and 256x256 pixel copies of the same image.


Also, you should get a GDI leak detector - I use GDIView, which I found at: http://www.nirsoft.net/programmer_tools.html[^]
It will tell you what sort of GDI resources as well as the number of them, that a process consumes. It makes it really easy to know if you're looking for a leaked brush or a font or whatever.

Lastly, profiling the code shows a lot of time seems to be spent in GDI+ stuff. If you resize the icon images in an external program then simply show them, you can probably do without GDI+ for the drawing stage, which should prove even faster again. The idea is to do all the pre-calculatin that you can, so you're not stuck scaling and blending large images at run-time.
 
Share this answer
 
v2
Comments
AlwaysLearningNewStuff 20-Oct-13 3:03am    
Thank you so much!

I can learn a lot from this!

I also use this opportunity to thank you for always being there for me, and for supporting me in my efforts to improve-that means a lot to me.

As for WM_PAINT optimization-I think that this should be separate question, therefore I will accept your answer ( +5 from me, as I think you deserved it ).

We shall stay in touch, as I suspect that there will be a lot more interesting problems to solve in the near future :)

Regards.
enhzflep 20-Oct-13 4:12am    
You're welcome. :)

It's becoming increasingly rare that QA-style questions are well-posed, thought-out and the product of having tried, yet failed at a particular task.

Even rarer still, are questions I find interesting and on topics I have chosen to pursue.

You served up a real treat in all regards. My 5.

'Till next time. :)
Best Regards.
AlwaysLearningNewStuff 1-May-14 1:44am    
Hi! It's been a long time... I have encountered a small problem with edit controls painting. Can you take a look ( http://www.codeproject.com/Questions/767217/Edit-control-not-repainted-entirely-with-chosen-br?loginkey=false ) if the heat "down there" hasn't entirely exhausted you ?

Best regards :)
Does your Window's window class has these 2 styles : CS_HREDRAW and CS_VREDRAW. These 2 styles causes flickering. To eliminate this remove those styles from your window class and try the following:

C++
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
   case WM_SIZE:
        InvalidateRect(hwnd, NULL, TRUE);
        return 0;
   //...
   //other lines....
}


BTW, this article is very cool. (and i caught that styles issue from that):
http://www.catch22.net/tuts/flicker-free-drawing[^]
 
Share this answer
 
Comments
AlwaysLearningNewStuff 17-Oct-13 8:08am    
I have updated my question with new findings, and have provided a link to the demo project were I have isolated the problem to drawing the background of the main window.

Static controls are deleted for easier debugging.

Hopefully you can find some time to give it a quick glance, I would appreciate it.

Thank you for your efforts () by the way, I have read the same article, and did everything you have suggested, in the beginning, but memory consumption described in my question update persists ).

Regards.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900