C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[384] 깜빡임을 물리치자
홍환민.행복 [hhshhm] 18978 읽음    2003-04-05 03:16
안녕하세요? 민입니다.

도스시절에서부터 윈도우즈 프로그래밍 시대에까지 많은 사람들을
고민하고 힘들게 하던 문제가 바로 그래픽 관련 깜박임 문제입니다.
그리고 씨빌더에 있는 컴포넌트를 사용할 때에도 역시나 부딪치게
되는 골치아픈 문제입니다.

씨빌더 사용자들이 이러한 깜박임 문제로 고통받는 것을 미리 예방해
보고자 이렇게 간단한 강좌를 준비하게 되었습니다.

먼저 이 글에서 뿌려준다, 그려준다, 파일로부터 로드해서 뿌려준다
등의 표현을 '그려준다' 라는 표현으로 통일했음을 알려 드립니다.

자.. 서론이 길었군요. 그럼 본론으로 들어가 보도록 하죠.

이 글에서 다루는 깜박임은 다음과 같이 세가지 경우입니다.

1. 컴포넌트 자체의 깜박임 현상
2. 컴포넌트 위의 그림의 깜박임 현상
3. ListBox, ComboBox, StringGrid, ListView, TMemo 등의 컴포넌트
    에서 한꺼번에 많은 추가, 삭제, 변경 등의 작업을 할 때의 깜박
    임 및 속도저하 문제

-----------------------------------------------------------------------
1. 컴포넌트 자체의 깜박임 현상

몇몇 컴포넌트에서 깜박임 현상을 종종 보게 됩니다. 예를 들면
판넬 자체가 깜박거리는 문제 등이 있을 수 있습니다. 깜박이는
현상을 겪게 되는 경우는 다양합니다.

◆ 왜 이러한 깜박임 현상이 나타나는가 ?

이유는 바로 WM_ERASEBKGND 메시지에 있습니다.
운영체제인 윈도우즈에서는 WM_ERASEBKGND 메시지를 특정 컨트롤에
보냄으로서 '너 배경 다시 그려라' 라고 명령을 하게 됩니다.
그리고 이러한 WM_ERASEBKGND 메시지는 WM_PAINT 메시지가 보내지기
전에 항상 보내지게 되어 있습니다. 그러므로 컴포넌트가 WM_PAINT
메시지를 자주 받게 되는 상황에 놓이게 되면 WM_ERASEBKGND 메시지도
자주 받게 되어 자주 배경을 새로 그리게 됩니다.
이러한 이유로 컴포넌트에 깜박임 현상이 생기게 되는 것입니다.

◆ 그렇다면 해결방안은 ?

먼저 TWinControl의 자손들은 윈도우 핸들(handle)을 가지고 있기 때문에
메시지를 받을 수 있고, TGraphicControl/TControl의 자손들은 윈도우
핸들을 가지고 있지 않기 때문에 메시지를 받을 수 없습니다.
이러한 이유로 각각 해결 방안이 달라지게 됩니다.

   1. TWinControl에서 상속된 컴포넌트일 경우

   WM_ERASEBKGND 메시지를 무시해 버리면 됩니다.

   예제
   아래 내용을 헤더파일에 추가하십시요. 아시겠지만 메시지 맵이라는
   기법을 사용해서 WM_ERASEBKGND 메시지를 다루고 있습니다.

    private :
    void __fastcall MyFunct( TWMEraseBkgnd &msg ) { NULL; };

    public :
    BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER( WM_ERASEBKGND, TWMEraseBkgnd, MyFunct );
    END_MESSAGE_MAP( MyControl )

   2. TGraphicControl/TControl에서 상속된 컴포넌트일 경우

   예제
   컴포넌트의 ControlStyle을 조정해 주면 됩니다.
   아래 한줄을 당신의 폼 클래스의 생성자에 추가하시면 됩니다.

    MyControl->ControlStyle = MyControl->ControlStyle << csOpaque;

-----------------------------------------------------------------------
2. 컴포넌트 위의 그림의 깜박임 현상

컴포넌트 위의 계속해서 대량의 내용을 포함하는 그래픽 파일 등을
그려야 하는 경우, 아니면 그러한 대량의 내용을 한번 그려주고 계속
사용하는 경우에서 WM_PAINT 메시지를 자주 받게 될 상황에 놓인 경우
등에서 깜박임 현상을 겪게 됩니다.

◆ 왜 이러한 깜박임 현상이 나타나는가 ?

다량의 내용을 직접 화면에 그려주게 되면 그래픽 연산과 화면에 직접
그리는 속도의 문제 등의 이유로 인하여 화면에 그 내용이 그려지는
과정이 사람의 눈에 인식될 수가 있습니다. 이러한 화면에 다량의 내용이
그려지는 과정이 여러번 반복되게 되면 깜박거림 현상이 나타나게 되는
것입니다.

◆ 그렇다면 해결방안은 ?

더블 버퍼링(Double Buffering).
이 것이 바로 해결의 실마리입니다. 흔히 그래픽이나 게임 프로그래밍을
할때 많이 접하게 되는 용어이지요. 원리는 생각외로 간단합니다.

다량의 그래픽 내용을 화면에 직접 그리지 않고 일단 메모리에 그린 뒤
(넣어준 뒤) 그 메모리의 내용을 화면에 그려주는 것이지요. 메모리 전송
속도는 사람 눈으로 감지를 못하는 속도이니 해결이 가능한 것입니다.

아래는 '씨빌더에서는 이러한 더블 버퍼링을 어떤 식으로 활용할 수 있을까'
에 대한 간단한 예입니다.

Q> PaintBox의 Canvas에 그래픽 파일을 직접 그려주고 사용하다 보니까
    깜박임 현상이 발생합니다. 어떻게 해야 하나요?

A> TBitmap 등의 컴포넌트를 생성해서 이 컴포넌트에다가 그래픽 파일을
    그리는 작업을 합니다. 그런 뒤에 PaintBox의 OnPaint 이벤트 핸들러
    에서 TBitmap의 Canvas의 내용을 자신의(PaintBox의) Canvas로 가져
    오는 식으로 코딩을 하면 됩니다.

TBitmap 등의 컴포넌트를 위의 더블버퍼링 설명에서의 메모리로 활용을
한 것입니다.

-----------------------------------------------------------------------
3. ListBox, ComboBox, StringGrid, ListView, TMemo 등의 컴포넌트
    에서 한꺼번에 많은 추가, 삭제, 변경 등의 작업을 할 때의 깜박
    임 및 속도저하 문제

ListBox, ComboBox, StringGrid, ListView, TMemo 등의 컴포넌트에서
한꺼번에  많은 항목을 추가, 삭제, 변경하는 등의 작업을 할 경우에
깜박임 현상과 속도저하 현상을 겪게 됩니다.

◆ 왜 이러한 깜박임 현상이 나타나는가 ?

일단 ListBox, ComboBox, StringGrid, ListView, TMemo 등의 컴포넌트
에서 항목으로 이용되는 것이 바로 TStrings 입니다.(혹은 TStrings를
상속받은 TStringList 등이죠.) 이 TStrings 의 Add 메서드나 Delete
메서드를 호출하게 되면 해당작업을 하게 되고 작업 후의 변화를 컴포
넌트에  반영해야 하기 때문에  WM_PAINT 메시지와  함께  컴포넌트에
갱신(update)이 일어나게 됩니다.

그런데 예를 들어 Add 메서드를 1000개를 한꺼번에 호출한다면 컴포넌트
화면 갱신이 1000번 일어나게 되므로 깜박임 현상과 함께 속도의 저하
현상이 일어나게 되는 것입니다.

◆ 그렇다면 해결방안은 ?

TStrings의 메서드인 BeginUpdate()와 EndUpdate()를 호출하시면 됩니다.
BeginUpdate()를 호출후 Add 메서드나 Delete 메서드를 호출하는 등의
작업을 하신 뒤 EndUpdate()를 호출하시면 되는 것입니다.

BeginUpdate()를 호출하게 되면 EndUpdate()를 호출할 때까지 컴포넌트
화면 갱신을 안하다가 EndUpdate()가  호출되면  그때 비로소 컴포넌트
화면 갱신이 일어나게 됩니다.

그러므로 이러한 BeginUpdate()와 EndUpdate() 메서드를 사용하게 되면
컴포넌트의 깜박임 문제 및 속도 저하 문제를 해결할 수 있습니다.
-----------------------------------------------------------------------

자.. 이것으로 씨빌더를 이용하면서 발생할 수 있는 '깜박임(flicker)'
문제들에 대해 다루어 본 글을 마치도록 하겠습니다. 미흡한 글이지만
여러분들에게 많은 도움이 되었으면 하는 바램입니다.

이 글에서 다루지 못한 점이나 잘못된 점이 있다면 제게 메일 남겨주시면
감사하겠습니다.

빌더동의 무한한 발전이 있기를 빌며..
모두 행복하세요.

---[참고자료]----------------------------------------------------------
"Flicker problem with TWinControls and TGraphicControl/TControl
  descendants.." 라는 제목의 인터넷 문서.  James.
"C++Builder 4 온라인 도움말"
-----------------------------------------------------------------------


+ 부록...

다음 글은 제  강좌에서 다루지 않은 핸들이 없는 컴포넌트는 왜
깜박이는지에 대해 설명하고 있습니다.

아래 글은 하이텔 비주얼파워툴 동호회의 델파이 Q/A란을 보다가
발견한 글입니다. 참 도움이 될만한 글인 것 같아서 이렇게 즉시
퍼다 올립니다. ^^; (덕분에 게시물 작성자 양병규님께 허락받을
시간은 없었습니다. ^^;)


제  목:[답변] 라벨은 왜 깜빡이는가 그 이유...          관련자료:없음  [24912]
보낸이:양병규  (슈베르트)  1999-09-03 02:00  조회:232


TLabel 은 TGraphicControl을 상속받아서 만들어졌기때문에 윈도핸들이 없으므로
움직이면 깜빡거릴 수 있습니다. 왜 깜빡일까요?

핸들이 없는 콘트롤들(TLabel, TImage, TBevel, TSpeedButton....)은 실제 윈도우
콘트롤이 아니라 가상 콘트롤입니다.  음... 몬소린고하니...

TAbcd 라는 윈도핸들이 있는 실제 콘트롤이 하나 있다고 칩시다...
그리고 Abcd1에는 가상콘트롤을 리스트로 관리 할 수 있다고 치고(추가,삭제..)

Abcd1에 가상 콘트롤을 하나 추가 합시다.( 어떻게든 했다고 치고 )
그리고는 Abcd1에 드디어 마우스가 움직인다고 칩시다.
그러면 Abcd1은 마우스가 움직여졌으니깐 WM_MOUSEMOVE메시지가 발생할겁니다.

그런데 얘는 그걸 바로 처리하지 않고 자기한테 가상 콘트롤이 있나 보고 있으면
혹시  마우스의 위치가 가상콘트롤 위에서 움직여지는게 아닌가 확인하고 정말로
그렇다면 Abcd1은 자기가 WM_MOUSEMOVE를 처리하지 않고 가상콘트롤에게 그 메시
지를 전달 합니다. 그러면 가상 콘트롤이 자기가 알아서 하겠죠?

화면에 윈도를 그리는 방식도 마찬가지입니다.   Abcd1이 화면에 자신을 그리는데
자기의 몸을 화면에 다 그리고 나서 가상콘트롤이 있나 확인하고 있으면 가상콘트
롤들의 그리는 루틴을 하나씩 실행하게 합니다.  이제 좀 감이잡히셨나요?

실제 예를 들어서...

Panel1위에 Label1이 있을때
Panel1은 핸들이 있는 실제 콘트롤이지만 Label1은 핸들이 없는 가상 콘트롤입니다.
그래서 화면이 다시 갱신(다시 그리기)해야 할 필요가 있어도 Label1은 그 사실을
알지 못하고(WM_PAINT를 직접 못 받으니깐) 일단 Panel1이 메시지를 받고 자기 다
그린 다음에 Label1에게 그리라고 알려줍니다. 그래서 항상 Panel1이 다 그려지고
그 위에 다시 Label1이 그려지죠.... 물론 워낙 빠르긴 합니다만 그래도 순간적으로
Panel1이 그려지고 다시 Label1이 그려지는 모습이 깜빡거리게 보입니다.

그런데 Label1의 위치가 바뀌는데 왜 Panel1이 다시 그리는냐?....


만약에

┏━━━━━━━━━━━━━━━━┓
┃ Panel1                         ┃
┃┏━━━━━━┓                ┃
┃┃Label1      ┣━━━━┓      ┃
┃┗━━━━━┳┛Label2  ┃      ┃
┃            ┗━━━━━┛      ┃
┗━━━━━━━━━━━━━━━━┛

휴~ 그림 그리기 힘들당~

그림처럼 Panel1위에 Label1, Label2가 있다고 쳤을때 Label1의 위치를 왼쪽으로
1픽셀 움직였다고 칩시다.  그러면 Label1이야 자기가 움직였으니깐 자기는 다시
그릴 수 있지만 Label2하고 Paenl1은 그 사실을 모르니깐 Label1이 움직여서 생긴
빈공간을 다시 그릴 수가 없쟎아요? 그래서 Label1은 움직여지거나 크기가 바뀌거
나 Visible이 바뀌거나 등등 다시 그릴필요가 있을때 그 사실을 Panel1에게 알리고
Panel1은 자기 밑에 있는 모든 가상 콘트롤들에게 다시 그리라고 알립니다. 그래서
아까 말 한것럼 다시 그리는 과정에서깜빡임이 생깁니다.

결론적으로... 간단한 한마디...

TLabel대신에 윈도 핸들이 있는 TStaticText를 사용하십시오..

그럼
평안하시길~


볼랜드 어려버..ㅠ [test369]   2007-03-13 20:41 X
MyControl->ControlStyle = MyControl->ControlStyle << csOpaque;

볼랜드 c++ 6 빌더에서는 에러 인데.. 어쩌지 ㅠㅠ
zeFa [kdh8070]   2007-04-23 21:12 X
저 출처와 작성자 기제하구 이글좀 제 블러그에 갖어갈께요. 요즘에 작업하는부분이라 ;; ㅎㅎ

+ -

관련 글 리스트
384 깜빡임을 물리치자 홍환민.행복 18978 2003/04/05
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.