Jump to content
  • Advertisement
Sign in to follow this  
lizzard_

Window with background image (resize problems)

This topic is 3005 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

I've been trying to figure this one out for a while now and I just don't know what else I can try.

I have a window in which I am loading a background image with gdiplus.

Problem 1:

The first thing you notice when you resize the window is that the image repaint can't follow the window border, so you can briefly see the window background (in this case, it's black).

Is there any way to fix this?

Problem 2:

The image is being redrawn after a resize event. However, The previously visible area is not included in the update region, which results on what it looks like a stack of images of different sizes.

This can be fixed by handling WM_SIZE and calling InvalidateRect( hWnd, NULL, FALSE ); or by handling WM_NCCALCSIZE and returning WVR_REDRAW.

I wonder if there's any better way to do it.

Problem 3:

Whenever I maximize the window, I don't really see a nice and smooth transition. After taking a closer look at the problem, I noticed that a "shadow" of the original window (including the non-client area) is being kept there until it is maximized and receives a WM_PAINT message that covers it up.

This screenshot might help understanding what I'm trying to explain (it was taken after the window was maximized, but before being repainted):

http://img341.imageshack.us/img341/8401/wnd.png

I hope someone can give me a hand with this, thanks in advance.

Here's my code (I've removed most of the original code because the problems I mentioned can be reproduced with this one):


#include <windows.h>
#include <stdio.h>
#include <gdiplus.h>

#pragma comment( lib, "gdiplus.lib" )

using namespace Gdiplus;

char m_szAppName[] = "AppName";

HINSTANCE m_hInstance;

int m_iScreenWidth, m_iScreenHeight;

HWND m_hMainWnd;

void OnPaint( HDC hDC )
{
RECT rcClient;
GetClientRect( m_hMainWnd, &rcClient );

Graphics graphics( hDC );

Image image( L"background.bmp" );

graphics.DrawImage( &image, 0, 0, rcClient.right, rcClient.bottom );
}

LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_CREATE:
{
break;
}
case WM_DESTROY:
{
PostQuitMessage( 0 );

break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;

HDC hDC = BeginPaint( hWnd, &ps );

OnPaint( hDC );

EndPaint( hWnd, &ps );

break;
}
case WM_ERASEBKGND:
{
return 1;
}
default:
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

return 0;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
HANDLE hMutex = CreateMutex( NULL, FALSE, m_szAppName );

if ( !hMutex || GetLastError() == ERROR_ALREADY_EXISTS )
{
return 0;
}

m_hInstance = hInstance;

GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;

GdiplusStartup( &gdiplusToken, &gdiplusStartupInput, NULL );

WNDCLASSEX wcex;

wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = MainWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )GetStockObject( BLACK_BRUSH );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = m_szAppName;
wcex.hIconSm = NULL;

if ( !RegisterClassEx( &wcex ) )
{
return 0;
}

m_iScreenWidth = GetSystemMetrics( SM_CXSCREEN );
m_iScreenHeight = GetSystemMetrics( SM_CYSCREEN );

HWND hWnd = CreateWindow(
m_szAppName,
m_szAppName,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
( m_iScreenWidth / 2 ) - ( 800 / 2 ), ( m_iScreenHeight / 2 ) - ( 600 / 2 ),
800, 600,
NULL,
NULL,
hInstance,
NULL
);

if ( !hWnd )
{
return 0;
}

m_hMainWnd = hWnd;

ShowWindow( hWnd, nCmdShow );
UpdateWindow( hWnd );

MSG msg;

while ( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

GdiplusShutdown( gdiplusToken );

return msg.wParam;
}

Share this post


Link to post
Share on other sites
Advertisement
What is your rationale for not correctly handling WM_ERASEBKGND? As the documentation indicates this is pretty important to handle properly if you want your window repainted nicely. I suspect at least a good chunk of your problems can be tied to that alone.

As for your Problem #2 - invalidating the window in WM_SIZE is probably the best option.

Share this post


Link to post
Share on other sites
Quote:
Original post by ApochPiQ
What is your rationale for not correctly handling WM_ERASEBKGND? As the documentation indicates this is pretty important to handle properly if you want your window repainted nicely. I suspect at least a good chunk of your problems can be tied to that alone.

As for your Problem #2 - invalidating the window in WM_SIZE is probably the best option.


Thanks for replying. Just before you posted, I solved problem 3 by checking for a flag on WM_ERASEBKGND.

As for problem 1, gdiplus might just be too slow to redraw in time for the image to fit the resized window, that's my guess atleast.

Regards

Share this post


Link to post
Share on other sites
I missed this the first time - is there a reason for loading the image over and over again in every WM_PAINT handling? Can you just cache the image instead? That should get you a bit of extra draw speed.

Share this post


Link to post
Share on other sites
Quote:
Original post by ApochPiQ
I missed this the first time - is there a reason for loading the image over and over again in every WM_PAINT handling? Can you just cache the image instead? That should get you a bit of extra draw speed.


Well, I've done that before, it seemed obvious to me that file i/o would be responsible for the slowness of redraws. At the time, I didn't notice any visible difference though. And now I am unable to make it work so I can't test again.

Once again, thanks.

Share this post


Link to post
Share on other sites
Besides the important things ApochPiQ mentioned, you should also resize the cached image whenever the window is resized, so whenever the cached image is used to paint the background, it's just blitted rather than stretched blitted. This will also allow you to take advantage of the rcPaint "dirty rectangle" given to you inside of the PAINTSTRUCT, so that you don't need to repaint the entire window. That won't help on resizing, but it will for other things.

That rcPaint rect, btw, also defines the clipping rect that BegPaint setup for the hDC it returned to you. That may be the cause of the "stepped" effect you see when you resize the window from small to large.

Share this post


Link to post
Share on other sites
Quote:
Original post by redllar
Besides the important things ApochPiQ mentioned, you should also resize the cached image whenever the window is resized, so whenever the cached image is used to paint the background, it's just blitted rather than stretched blitted. This will also allow you to take advantage of the rcPaint "dirty rectangle" given to you inside of the PAINTSTRUCT, so that you don't need to repaint the entire window. That won't help on resizing, but it will for other things.

That rcPaint rect, btw, also defines the clipping rect that BegPaint setup for the hDC it returned to you. That may be the cause of the "stepped" effect you see when you resize the window from small to large.


Thanks for replying. Yes, I was already doing that in my original code. I'm comparing client area rects and using a background DC from which I BitBlt whenever I don't need to resize the image.

For now, I've solved my problems by making sure that the area that needs to be repainted is black. When the user is done resizing, it repaints the whole client area with a resized background image.

If anyone's interested, I can post the new code.

Regards

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!