Solved

clipping problem with rapid MoveWindow calls

Posted on 1998-08-25
16
657 Views
Last Modified: 2013-11-19
In a window derived from CView I am doing some animation.  When I do fast repeated calls to MoveWindow for an image which is supposed to fly around the screen, the image disappears partway through the movement.  It acts like it is getting clipped.  Even when I only have one image in the view, it still won't go around in a circle for example without getting clipped.  The clipping does not happen on the first move, but once it starts the image does not appear again until it stops moving.  At this point the view gets invalidated so that brings it back.

While I am not writing a game, the animation probably uses some of the same techniques.  Do I need to handle the movement differently?
0
Comment
Question by:Ashurbanipal
  • 6
  • 5
  • 5
16 Comments
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
That happens because by default the windows erase their background. That means that every time you move your window it erases its entire background and then it paints itself. The same happens to the parent of the moving window, but only in the updated region. That's why the best result you will get is a flickering flying window.
You can work around the problem handling the WM_ERASEBKGND in the moving window. If you supply a handler that does nothing then you will not get the nasty flicker. You will need to do the same with the parent window, since it also erases its background at the place where the moving window was. Don't forget, however, that your OnPaint() handlers should cover the entire window surface. Otherwise you will get some ugly mess on the screen.
0
 
LVL 2

Expert Comment

by:warmcat
Comment Utility
some pointers...

If the clipping behaviour is only in horizontal areas, then this could be due to the redraw of the window and the refreshing of the screen clashing.  Games programmers synchronize their redraws to the 'vertical retrace' event of the video card to avoid this.

Also, redraws of windows... ie, delivery of WM_PAINT, is not synchronized to the MoveWindow() event.  The WM_PAINT is posted to the window and is queued with other messages waiting for the window.

If you want high-quality animation, and the data you are animating is static, ie, does not vary, you are better off making an AVI like the ones the '95 user interface shows while doing file copies.

If WM_ERASEBKGND is the issue here, I recall you can set a window's - it may be a window class' - background brush to NULL.

Regards,

-Andy
0
 

Author Comment

by:Ashurbanipal
Comment Utility
I checked out erasing the background, but it did not work very well.  Then I tried the following block of code immediately after the MoveWindow.  

MSG msg;
while (::PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

This removed the clipping problem, but the image flickers contiually through the move.  From what I've read the best way to deal with this is by using a technique called "virtual windows", where everything gets output to a virtual HDC and when it comes time to paint the proper region is copied from the virtual HDC to the screen.  When I try to implement it though in the main window of the MDI nothing happens, so I must be doing something wrong.

I create the virtual HDC in the OnCreate for the main window

int max_x=GetSystemMetrics(SM_CXSCREEN);
int max_y=GetSystemMetrics(SM_CYSCREEN);

HDC hdc=::GetDC(GetSafeHwnd());
virtualdc=CreateCompatibleDC(hdc);
HBITMAP hbit=CreateCompatibleBitmap(hdc,max_x,max_y);
SelectObject(virtualdc,hbit);
HBRUSH hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(virtualdc,hbrush);
PatBlt(virtualdc,0,0,max_x,max_y,PATCOPY);

Then in OnPaint for the main window it looks like this:

PAINTSTRUCT paintstruct;
HDC hdc=:BeginPaint(GetSafeHwnd(),&paintstruct);
BitBlt(hdc,paintstruct.rcPaint.left,
               paintstruct.rcPaint.right-paintstruct.rcPaint.left,
               paintstruct.rcPaint.bottom-paintstruct.rcPaint.top,
               virtualdc,
               paintstruct.rcPaint.left,
               paintstruct.rcPaint.top,
               SRCCOPY);
EndPaint(&paintstruct);

I expected the above to have some effect, at least making a mess of the screen, but nothing.  Is this code really overriding the paint command?  It seems as if a ON_WM_PAINT message gets sent, but the painting is really done elsewhere.  How do I do this?
0
 
LVL 2

Expert Comment

by:warmcat
Comment Utility
Ash, on your flickering problem, you will always have some flicker because your screen update is not syncd to the video card frame.

On your latest issue, the MFC code that calls OnPaint() has already done the Begin/EndPaint() call and given you a nice prepared hDC as a parameter.  Kill your Begin/EndPaint() code, and use the supplied hDC and maybe something will happen for you.

0
 
LVL 2

Expert Comment

by:warmcat
Comment Utility
Ash, sorry, I was thinking of CView::OnDraw().  Ignore my second point above.
0
 

Author Comment

by:Ashurbanipal
Comment Utility
It sounds as though I need to do two things, sync to the video card and get the virtual hdc working.  One book I have give an example for the virtual hdc and that is the code I've tried to implement.  Either I've set it up wrong or there is more going on with MFC (their example isn't MFC based).  What's the trick?

It sounds as though I need a good book on game programming or web site with good examples.  I'm stuck though in having to use a CView in an MDI application, no flexibility there.
0
 
LVL 2

Accepted Solution

by:
milenvk earned 150 total points
Comment Utility
In fact the "virtual DC" idea is the one that helps in flicker situations. I use it very often and it always works great. Your code with the creation of the memory DC and bitmap is correct. I think that the problem could be somewhere else around that code.

I created an MFC application for you that displays animated icon on the screen in a sine trajectory. It works flawlessly. But, as I said, you have to override the OnEraseBkgnd - otherwise you will get the flicker for sure!!! Try it with and without the empty OnEraseBkgnd override.

Here is the significant code for the view (all the rest is just the default AppWizard created code):

File TestView.h:

class CTestView : public CView
{
protected: // create from serialization only
      CTestView();
      DECLARE_DYNCREATE(CTestView)

      HDC m_virtualdc;
      int m_posX;
      int m_posY;
      HICON m_hIcon;

// Attributes
public:
      CTestDoc* GetDocument();

// Operations
public:

// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CTestView)
      public:
      virtual void OnDraw(CDC* pDC);  // overridden to draw this view
      virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
      protected:
      virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
      virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
      virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
      //}}AFX_VIRTUAL

// Implementation
public:
      virtual ~CTestView();
#ifdef _DEBUG
      virtual void AssertValid() const;
      virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
      //{{AFX_MSG(CTestView)
      afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
      afx_msg void OnPaint();
      afx_msg BOOL OnEraseBkgnd(CDC* pDC);
      afx_msg void OnTimer(UINT nIDEvent);
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};



File TestView.cpp:

int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
      if (CView::OnCreate(lpCreateStruct) == -1)
            return -1;

      int max_x=GetSystemMetrics(SM_CXSCREEN);
      int max_y=GetSystemMetrics(SM_CYSCREEN);

      HDC hdc=::GetDC(GetSafeHwnd());
      m_virtualdc=CreateCompatibleDC(hdc);
      HBITMAP hbit=CreateCompatibleBitmap(hdc,max_x,max_y);
      SelectObject(m_virtualdc,hbit);
      HBRUSH hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
      SelectObject(m_virtualdc,hbrush);
      m_hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));

      SetTimer(1, 40, NULL);
      
      return 0;
}

void CTestView::OnPaint()
{
      PAINTSTRUCT paintstruct;
      
      HDC hdc=::BeginPaint(GetSafeHwnd(), &paintstruct);

      int width = paintstruct.rcPaint.right-paintstruct.rcPaint.left;
      int height = paintstruct.rcPaint.bottom-paintstruct.rcPaint.top;
      
      PatBlt(m_virtualdc,0,0,width,height,PATCOPY);

      DrawIcon(m_virtualdc, m_posX, m_posY, m_hIcon);

      BitBlt(hdc,
            paintstruct.rcPaint.left,
            paintstruct.rcPaint.left,
            width,
            height,
            m_virtualdc,
            paintstruct.rcPaint.left,
            paintstruct.rcPaint.top,
            SRCCOPY);

      EndPaint(&paintstruct);
}

BOOL CTestView::OnEraseBkgnd(CDC* pDC)
{
      return TRUE;
}

void CTestView::OnTimer(UINT nIDEvent)
{
      static CRect rect;
      CView::OnTimer(nIDEvent);
      if(nIDEvent == 1)
      {
            GetClientRect(rect);
            if(m_posX >= rect.Width())
                  m_posX = 0;
            else
                  m_posX = m_posX + 2;

            m_posY = int(sin(m_posX/20.0)*20) + 100;
            RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE);
      }
}

You can further optimize the drawing to invalidate only the regions that have changed - like a region including the previous position of the moving object plus the new position of the object. And this windows animation stuff really does not have to do anything with the so called "synchronization of the video card frame". That only can be used when you draw directly to the video card. I think that the solution here will work in your case. And by the way - the moto of MFC has always been "Ease plus power", in the case it means that you can always use non MFC functionality merged with MFC code, so every piece of plain Win32 SDK code will work without problem in your MFC app if, of course, you put it in the right place.
0
 
LVL 2

Expert Comment

by:warmcat
Comment Utility
>And this windows animation stuff really does not have to
> do anything with the so called "synchronization of the
> video card frame". That only can be used when you draw
> directly to the video card.

There are a bunch of APIs like DirectDraw which are very aware of Frame Sync/ frame buffering issues.  You're right that it is hard to synchronize Windows GDI activity around the Frame Sync, but that doesn't mean that using the Frame Sync is a bad idea, it means that animating via the GDI will give you artefacts like 'tearing'.

Much better to let someone else's specific implementation take the strain and use an AVI if you can.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 2

Expert Comment

by:milenvk
Comment Utility
warmcat: agree...
I only said that _windows_ animation has nothing to do with frame sync and so on... Of course DirectDraw and DirectX technology at all are for that, but it _cannot_ be implemented in any real windows applications, i.e. they are always full screen, full PC control, etc... gamelike stuff :-)
I just thought that Ashurbanipal is trying to use animation in standard windows application and not in a DirectDraw one... AVI idea is good and cheap in time and resource - I agree with that also, but what if the animation depends on user input? You cannot achieve that with static AVIs...
0
 
LVL 2

Expert Comment

by:warmcat
Comment Utility
Mile,

>Of course DirectDraw and DirectX technology at all are
>for that, but it _cannot_ be implemented in any real
>windows applications, i.e. they are always full screen,
>full PC control

This is not true.  Even Quake II, which uses one or another of these APIs runs happily in a window on my PC while other apps share time.  Not that it's the best way to experience it, as a 640x480 window on a 1600x1200 desktop, but it works fine that way, as I recall without tearing.

On the AVI thing, yes, it's only suitable for static animations, as I said earlier, but it's not clear what Ash is trying to achieve.
0
 

Author Comment

by:Ashurbanipal
Comment Utility
Ash is dealing with a software package which simulates industrial plants like paper mills.  Originally it was never meant to have a graphic display, but the effort to make it a general use product requires it.  Currently the graphics work fine for pumps and plots of land that don't move around the screen.  Then I tried to simulate planetary motion, that's when the problem came up.  Speed is a serious consideration for simulation, dealing with the screen is quite a load on the package.  Once I get it working I can start to look into optimizing it.  I haven't dealt with AVI's before, but it sounds like that will come.  All part of the continuing education.

I'm neck deep in alligators and should be able to check out your example this evening, Mile.  It looks pretty good.
0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
What's up Ash? Any progress with that problem?
0
 

Author Comment

by:Ashurbanipal
Comment Utility
Your example works as you described.  When I went to implement it in my software two issues came up.

1. I was using MoveTo and LineTo, both class members of dc.  Do win32 versions exist for these?  I couldn't find any on the developer's network cd, but they must exist.

2. I take it that child windows of the CView need to have their OnPaint overriden so that they write to the m_virtualdc in their parent.

Are you in school?  I may have need of a good software person in the future.
0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
Well Ash,

1.In Win32 there are ::LineTo and CDC::LineTo functions - they are exactly the same as in Win16. ::MoveTo is changed to ::MoveToEx in Win32, but CDC still has a member CDC::MoveTo.

2. Yes - you have to override OnPaint functions of all the children of your m_virtualdc holder. Firs make them transparent - i.e. override OnEraseBkgnd() like shown so it does nothig. Second override OnPaint() and send all the drawing to the m_virtualdc of the parent - you can access m_virtualdc using a global pointer to it or, better, cast the CWnd* returned from GetParent() function to say CTestView. You have to calculate the accurate coordinates when drawing from child windows, since their 0, 0 position is offset according to the m_virtualdc 0, 0 position.

I am not in school - I am currently a software developer... What will you need that software person for? Consulting I guess :-) Be welcome to ask questions
0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
Ash, if my answer works for you you are supposed to rate me and grant me the points...
thank you...
0
 

Author Comment

by:Ashurbanipal
Comment Utility
Yes, I know.

My needs will involve modifications to an existing software package.  Two issues come to mind, one involves 3D display of data, the other deals with a dll which interfaces between the package and various external packages.  The first one I would like to have, but don't have a good feel for what it would cost versus what would be a satisfactory final result.  The second depends on whether customers are willing to come up with the money for it, I'll know that later this year.  When the time comes I may need to farm these tasks out, that's why I'm interested in people who might be able to handle them.

John Arkison
johna@jhasimulations.com
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now