Tech & IT/프로그래밍

VC++ Flickering, 깜박거림 제거 관련 글

해피콧 2009. 1. 17. 13:57
'); }
'); }

 화면 깜박거림을 제거합시다.  | VC++ 일반 2005-05-09 오후 11:34:20
최병훈 (angleafro)  최병훈님께 메시지 보내기최병훈님을 내 주소록에 추가합니다.최병훈님의 개인게시판 가기 번호: 7084   / 평점:  (6.3)  / 읽음:3,712

화면 깜박거림을 제거합시다.

 

화면 떨림이란 모니터의 주사선과 그래픽카드의 잘못된 설정으로 생길수 있습니다.

그래픽 카드를 바꾸고, 모니터를 좋은 것으로 바꾸서 쓰면 되죠..

맞나요.. ㅋㅋ

 

그럼 프로그램할 때 화면 조정 말고 , 사이즈 조정이나 크기조정 또는 드래그, 및 콘트롤 사용시 나오는 깜박거림은 도대체 모죠?

 

해답은 있나요?

네 당근 입니다, (오이 부모왈~~)

 

그럼 시작하는 과정이므로 간단하게 가장 쉬운 폼뷰부터 시작하죠..

 

그럼 왜 깜박거림이 생기는가?

 

한가지 동일한 원인에 의하여 프로그램중 일부분이 그리기 작업을 하게 되고, 다른 부분이 그 곳에 다시 그리기 작업을 하게 됩니다.

그리고, 순식간에 두 가지 페인팅 작업이 발생이 되는데, 두번째 작업을 하기 전에 사용자가 첫번째 그리기 작업을 보기 되늣것입니다.

 

그럼 해결 방법은 있군요.

. 두가지를 그리기를 하는데, 첫번째 작업이 끝나고 아주 빨리 두번째 그리기 작업을 하는것입니다.

그러나 , 이 방법은 사용자 피씨가 슈퍼콤이 아니면 아주 힘들겠죠..

어차피 두가지가 순식간의 터울로 드려지기 때문에 그리기 작업을 나눠서 하지 말고 동시에 해보자는 것입니다.

 

그럼 깜박꺼림(Flicker)를 제거한는 간단한 프로그램을 짜 보죠.

초보자들이 비절씨를 할 때 아장 쉽게 할수 있는 것 중 하나가, CFormView 클레스를 뷰로 하여 작성한는 것입니다.

폼뷰로 프로그램을 작성하였을 때 쉽게 볼수 있는데요..

OwerDraw 속성을 적용하지 않는 기본 콘트롤이 폼뷰의 사이즈를 조정하면 깜박거리죠..

 

그럼 깜박거림을 없게하려면.

가장 쉬운게 부모 윈도우, 즉 폼뷰가 변경될 때 콘트롤들이 다시 그려지지 않게 하면 됩니다.

WS_CLIPCHILDREN

이 속성을 이용하면 되죠.

대신 이속성을 폼이 키기가 변경될 때 적용하면 더욱 효과적이죠.

void CFlicker_freeView::OnSize(UINT nType, int cx, int cy)

{

             ModifyStyle(0, WS_CLIPCHILDREN); // turn on WS_CLIPCHILDREN

             CFormView::OnSize(nType, cx, cy);           // default

             UpdateWindow();

             //this->Invalidate(false);

             ModifyStyle(WS_CLIPCHILDREN, 0); // turn off WS_CLIPCHILDREN

             GetClientRect(&m_rectDraw);

}

이렇게요..

한가지 중요한게 남았는데.

구릅박스죠..

그릅박스 경우는 문제가 있어요.

 

 

자신의 차일드를 포함할수 있는데,

구름박스의 테두리 안쪽이 폼에도 속하지 않고, WS_CLIPCHILDREN 속성에 의하여 그룹박스에도

속하지 않게되어서  화면이 요상해 집니다.

이걸 해결 할려면. Transparent속성을 활성화 시켜 주면 됩니다.

 

다음에는 페인팅 작업이 너무 많아서 깜박거리는 것을 예기해 보죠..

수고 하세요.

이 글에 평점 주기: 

질문&답변
 formview를 이용한 다디얼로그의 크기를 변경시킬때 깜박이는 현상해결.. 방법 없나요..ㅜㅜ  | VC++ 일반 2003-12-20 오전 1:23:29
홍성호 (dybbls)  홍성호님께 메시지 보내기홍성호님을 내 주소록에 추가합니다.홍성호님의 개인게시판 가기 번호: 406068   / 평점:  (-)  / 읽음:1,194

 formview를 이용해서 MDI 프로그램을 만들었습니다.

formview안에는 대부분 이미지로 차 있습니다.

 

근데 창 크기를 변경시킬때 심하게 깜박이는 현상이 나타납니다...

물론 이미지는 변하지 않습니다. 단지 크기만 창 크기메 맞게 변합니다.

 

정말 답답합니다. 왜 이러는건지.. 힘드네요.. 프로그램..

 

이 글에 평점 주기: 
 [답변][MAX] Window 깜빡임의 실체 - WM_ERASEBKGND와 WM_PAINT 2003-12-20 오전 2:42:08
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406075   / 평점:  (8.3)  
Window는 뭔가 새로 그려야할 필요성 있을 때마다 WM_PAINT Message를 받습니다. OnPaint라는 이름으로 WM_PAINT에 대한 Handler Function이 보통 만들어지죠. System으로부터 WM_PAINT가 날아오는 상황은 다음과 같습니다.

    - 윈도우가 처음 생성되었을 때
    - 윈도우의 위치가 이동되었을 때 
    - 윈도우의 크기가 변경되었을 때(최소 및 최대화 포함)
    - 윈도우의 전체 또는 일부가 다른 윈도우에 가려져 있다가 나타날 때
    - 윈도우가 스크롤 될 때
    - UpdateWindow나 RedrawWindow 함수가 불렸을 때
    - InvalidateRect나 InvalidateRgn 함수가 불려서 다시 그려져야할 영역이 발생 한 후,
      Message Queue에 다른 처리할 Windows Message가 없을 때

그런데 WM_PAINT가 날아오기 '전'에 대부분 같이 따라오는 Message가 있습니다 WM_ERASEBKGND라는 것으로, Erase Background 라는 의미죠. 즉, 배경을 지워라.

Window의 기본 Message Procedure(DefWindowProc)는 사용자가 WM_ERASEBKGND를 받고도 아무 처리를 하지 않으면, WNDCLASS의 hbrBackground 맴버에 정의된 색상으로 배경을 지워버립니다. 깜빡이는 것은 아래 처럼 되기 때문에 발생하는 것이지요.

    1. WM_ERASEBKGND 받음
    2. 배경 지움
    3. WM_PAINT 받음
    4. Image 다시 그림

2까지가 '깜'이고 4까지 가면 '빡'이 됩니다. 깜빡 깜빡의 실체는 이것입니다.
손쉬운 처리는 WM_ERASEBKGND를 받았을 때, 배경을 안지우도록 하는 방법입니다. 보통 OnEraseBkgnd 정도의 이름을 갖게 되는 WM_ERASEBKGND Message Handler에서 0을 return 하면 됩니다.

    BOOL CAboutDlg::OnEraseBkgnd(CDC* pDC)
    {
        return 0;
    }

깜빡 거리는 것이 WM_PAINT Message Handler에서 그려주는 어떤 것이 아니라, 또 다른 Window인 '자식' Control이라면, Window Styles 중에 WS_CLIPCHILDREN Style을 '부모' Window에 먹여서 자식 Control에 의해 가려지는 영역은 그리기 대상에서 아예 제외시켜버리는 것이 일반적인 해결책입니다.

 질문&답변
 RedrawWindow와 Invalidate와의 차이?  | VC++ 일반 2003-12-21 오후 8:33:13
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406316   / 평점:  (-)  / 읽음:1,009

 RedrawWindow와 Invalidate와의 차이가 무엇인지 알고 싶습니다.

RedrawWindow한부분에 Invalidate로 대체하면 제대로 안되는 이유를 모르겠습니다.

 

코드

 

    CClientDC dc(this);

    CRect rect;

    CString status;

 

    GetClientRect(rect);

    status.Format( "%-20s Line %d, Col %d", msg, point.x, point.y);

 

    RedrawWindow();

    //Invalidate();

    dc.MoveTo(0,rect.bottom-30);

    dc.LineTo(rect.right, rect.bottom-30);

    dc.TextOut(0, rect.bottom-20, status);

 

RedrawWindow();로하면 문자열이 잘 출력되는데

Invalidate();로 하면 문자열이 출력되었다가 이내 지워 집니다.

 

이 글에 평점 주기: 
 [답변][MAX] Win32 API RedrawWindow와 Invalidate 함수의 차이(내용 추가) 2003-12-21 오후 9:17:08
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406322   / 평점:  (9.0)  
    BOOL RedrawWindow(
       LPCRECT lpRectUpdate = NULL,
       CRgn* prgnUpdate = NULL,
       UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE 
    ); 

보시다시피, MFC CWnd::RedrawWindow 함수는 Win32 API RedrawWindow 함수를 호출 할 때 아래와 같은 옵션을 '기본값'으로 씁니다.


    RDW_INVALIDATE
        - 대상 Window의 특정 영역 또는 Client 영역 전체를 Invalidate 함
    RDW_ERASE
        - 대상 Window가 Repaint될 때, WM_ERASEBKGND Message도 같이 받게 함
    RDW_UPDATENOW
        - 함수가 반환되기 전에 대상 Window들이
          WM_NCPAINT, WM_ERASEBKGND, WM_PAINT Message를 받음

Win32 API Invalidate 함수의 역할은, OS가 Window에게 '어떤 영역이 무효화 되었으니 다음 번 WM_PAINT를 받을 때 다시 그렸으면 좋겠다'는 표시를 하는 것입니다. 보통 Invalidate가 호출되고 나면 OS는 대상 Window의 Message Queue에 WM_ERASEBKGND 그리고 WM_PAINT 순으로 Message를 보냅니다.

Window가 실제로 Repaint를 하는 시점은, Invalidate가 호출된 시점이 아니라, Invalidate의 호출 이후에 OS가 대상 Window에 다시 WM_PAINT Message를 던져준 시점입니다.

Invalidate를 썼을 때, TextOut으로 뿌려준 부분이 '낼름' 지워지는 것은 아마도, TextOut으루 뿌려준 이후에, WM_PAINT Message Handler 함수 안에서 TextOut으로 출력한 내용을 덮어써버리는 것 같네요.

RedrawWindow를 쓰더라도 RDW_UPDATENOW 옵션을 빼면 그냥 Invalidate를 불러준 것과 같은 효과입니다. RedrawWindow를 쓸 때 RDW_UPDATENOW을 쓰면, 함수가 반환 되기 전에 WM_PAINT Message가 Message Queue 들어옵니다. 그렇게 되면 그 시점에서 바로 다시 그리기가 일어나고 뒤에 TextOut으로 뭔가 그려진 집니다.

따라서, 화면에 보이고 있는 것은, RedrawWindow가 제 역할을 했다기 보다는 그냥 불필요한 Paint 동작이 다행스럽게도 TextOut 보다 먼저 발생했기 때문입니다. 차라리 Invalidate나 RedrawWindow를 모두 호출하지 않았다면 문자열들이 그냥 보였을 것입니다.

지워진다는 것을 보니, 문제의 Code WM_PAINT Message Handler 함수 안에 있는 것이 아닐 것 같은데 맞나요? 그럼 잘 출력된 것 처럼 보여도, 출력된 문자를 다른 Window로 한 번 덮었다가 (Invalidate) 빼면 글자가 지워집니다. 확인해보세요. 화면에 보이는 것을 계속 유지하고 싶다면, WM_PAINT Message Handler 안에서 계속 그려지게 만들어야 합니다.

이 글에 평점 주기: 
         [답변]한가지만 더요.. 2003-12-21 오후 10:22:22
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406334   / 평점:  (-)  

 그렇다면 RedrawWindow()는 WM_NCPAINT, WM_ERASEBGD, WM_PAINT메세지가

처리된 다음에 리턴이 된다는 것인가요?

아니면 그냥 위 메세지들을 메세지 큐에만 던져두고 리턴을 한다는 것인가요?

 

 

이 글에 평점 주기: 
                 [답변]RedrawWindow에 RDW_UPDATENOW 옵션이 있으면 WM_PAINT가 즉시 전달됩니다. 2003-12-21 오후 10:31:18
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406335   / 평점:  (9.0)  

RedrawWindow 함수에 현재 쓰인 옵션(RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE)만 보면, WM_ERASEBGD Message와 WM_PAINT Message가 먼저 처리됩니다.

Break Point를 걸어서 확인해도 되죠. RedrawWindow 함수에 RDW_UPDATENOW가 있으면, RedrawWindow 호출 시, 바로 WM_PAINT Message Handler가 처리되는 것을 볼 수 있습니다. 그 이후에 RedrawWindow 함수 이후 부분의 Code가 실행됩니다.

Invalidate는 WM_PAINT Message를 대상 Window로 보내는 것이 아니라 System이 대상 Window에 WM_PAINT Message 보내는 작용을 유발시키는 것입니다. 정확히 언제 WM_PAINT Message가 대상 Window에 도착하는지는 알 수 없죠.
이 글에 평점 주기: 
                         [답변]감사합니다...그런데.. 2003-12-21 오후 10:54:19
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406337   / 평점:  (-)  

 저도 이런저런 코드를 넣고 spy++로 확인해 보니

ReDrawWindow의 경우는 WM_PAINT를 Send시키고 바로 처리를 한후 리턴하는데

Invalidate의 경우는 WM_PAINT를 Post시키는 군요.

 

아무튼 알려주셔서 감사합니다. 도움이 많이 되었습니다.

이 글에 평점 주기: 
                                 [답변]... 2003-12-21 오후 11:55:13
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406347   / 평점:  (-)  
옙 내부적으론 그렇게 처리됩니다.

첨언하자면, WM_PAINT는 우선 순위가 낮아서, Invalidate에서 Post가 되더라도, 항상 다 도착하지는 않습니다. Queue 안에 아직 처리되지 못한 WM_PAINT가 하나라도 존재하면 그냥 무시 당합니다. (따라서 Message Queue에는 WM_PAINT는 하나가 있거나 없거나 그렇죠.)