360 likes | 889 Views
Windows Programming 제대로 배우기 Chapter 9. Painting Styles 학습 목표 DC 를 얻는 3 가지 방법 학습 Raster Operation 기능 학습 윈도우의 두 가지 출력 방법 방법 1 이제까지의 모든 출력은 WM_PAINT 에서 함 데이터를 변경한 뒤에는 InvalidateRect 함수를 사용하여 재출력 (WM_PAINT 가 다시 호출되도록 ) 하는 방식 사용 방법 2 GetDC(hWnd) 함수 사용
E N D
Windows Programming제대로 배우기 Chapter 9. Painting Styles
학습 목표 • DC를 얻는 3가지 방법 학습 • Raster Operation 기능 학습
윈도우의 두 가지 출력 방법 • 방법 1 • 이제까지의 모든 출력은 WM_PAINT에서 함 • 데이터를 변경한 뒤에는 InvalidateRect 함수를 사용하여 재출력 (WM_PAINT가 다시 호출되도록)하는 방식 사용 • 방법 2 • GetDC(hWnd) 함수 사용 • 현재 윈도우의 DC Handle을 가져다 주는 함수로 WM_PAINT 메시지 부분에서만이 아니라도 언제든지 출력 가능
WM_PAINT에 의한 방법 // 해당 윈도우에 대해 Painting에 필요한 // 준비를 한 다음 현재 윈도우의 DC 반환 ** BeginPaint는 해당 윈도우의 Update해야 할 영역 (Clipping Region : 클라이언트 영역의 특정부분에 그리기를 한정하는 영역)을 계산하며, 그 영역은 윈도우가 생성되거나, 움직여지거나, 사이즈가 변경되거나 스크롤이 되거나 하는 Client Area에 대한 처리가 행해지는 동작이 이루어져 윈도우를 다시 그려야 할 때 시스템에 의해 자동으로 계산됨 ** 이 영역은 User가 InvalidateRect나 InvalidateRgn 함수를 호출함으로써 결정될 수 있음
InvalidateRect(hWnd, NULL, TRUE); • 업데이트될 영역을 NULL로 주고 모든 영역을 전부 다시 지움 • 프로그래머가 특정영역만을 지정하여 업데이트하게 하기 위해서는 영역을 다음처럼 지정함 • InvalidateRect(hWnd, &r2, TRUE);
예제 : TestMode1-by WM_PAINT 이 예제는 마우스가 어느 영역에 클릭될 지 알 수 없으므로 이런 경우는 특정 영역을 주어 Invalidate시킬 수 없다. 즉, 전체 윈도우를 다시 출력해야만 한다. InvalidateRect(hWnd, NULL, TRUE); 전체윈도우
R1 R2 R3 이 그림처럼 업데이트할 영역이 분할되어야 할 경우에는 영역을 지정해야 함 InvalidateRect(hWnd, &r2, TRUE);
Let’s think about it • 만약 WM_LBUTTONDOWN에서 BeginPaint 함수를 사용하여 DC를 얻어와 바로 출력하면 어떻게 될까 ? (직접 실험 해보도록 …) • 화면에는 아무것도 출력되지 않음 • 이유 : BeginPaint함수는 WM_PAINT 이외의 곳에서 사용할 없다.
GetDC()에 의한 방법 (1) GetDC함수는 특정 윈도우의 Client영역에 대한 DC핸들을 얻기 위해 윈도우 핸들을 주어 사용 –BeginPaint함수와는 달리 WM_PAINT 이외의 곳에서도 사용가능 (2) 파라미터에 NULL을 주어 사용하면 모니터 전체의 스크린 영역에 대한 DC핸들이 반환됨 (CreateDC함수에 “DISPLAY” 사용과 동일) (3) 윈도우의 타이틀바 영역에 대한 DC를 얻기 위해서는 GetWindowDC함수 사용
예제 : TestMode2-by GetDC 처음부터 출력됨 윈도우 업데이트되어도 계속 출력됨
// 실행시 항상 출력되는 원 Invalidate함수 호출 안하고 바로 출력가능. 그러나, 이전에 그려져 있던 그림을 상관하지 않음
GetDC에 의한 출력은 즉각적이며, 일시적인 출력방법 • 배경과 관계없이 특정 출력을 일시적, 또는 즉각저긍로 반영하고자 하는 경우에 사용됨 • 언제 WM_PAINT에서 출력하고, 언제 GetDC를 이용하여 출력하는 것이 좋은가? • 정적 출력(static) vs. 동적 출력(Dynamic) 참조
정적 출력(Static) vs. 동적 출력(Dynamic) • 윈도우에는 두 가지 다른 모드의 출력방법이 있음 • 실험 : TestMode2-by GetDC 예제에서 WM_PAINT에 있는 출력 문장을 코멘트를 쳐보고, 실행 => 초기 화면에 아무것도 출력안됨 => 윈도우 업데이트하면 윈도우의 모든 데이터 삭제됨
WM_PAINT에 의한 출력은 윈도의 정적인 상태 (Static 출력) • 배경이나 윈도우가 정적인 상태가 되었을 때의 출력 • GetDC에 의한 출력은 일시적인 변화, 즉 어플리케이션의 동적인 상태를 표현하는 데 적합, Invalidate함수를 사용하여 재출력하는 방법에 비해 매우 즉각적(Dynamic 출력) • 움직이거나 동적으로 표현되는 상태를 나타내는 출력은 그 효율성으로 보아, 배경을 모두 재출력할 필요없이 GetDC로 즉각 출력
GetDC는 이전 출력 상태에 대해 상관하지 않음 • 마지막 원을 하나만 남기기 위해서는 이전의 원을 지워야 함 • 전체를 다시 그리지 않고, 이전 그림만을 지우려면 어떻게 해야 하는 가 ? • 이전 영역과 새로운 영역을 Union 시켜 Invalidatet시키면 되는가 ? 가능은 하나, 만약 두 영역의 위치가 완전히 다르며 멀리 떨어져 있는 경우 그 Union된 영역은 매우 커지며, 결국 InvalidateRect함수에 전체 영역을 주는 것과 마찬가지가 됨 • 동적 출력을 위한 해결법은 다음 2가지 해결해야 함 • 그림을 어떻게 지우는가? • 변경 시 이전 그림을 지우고 새로이 나태내는 문제 해결 => Raster Operation필요
Raster Operation • 점, 선을 그릴 때, 원하는 픽셀과 해당 디스플레이 표면 사이의 Bitwise부울 연산을 수행 • 두 픽셀 사이에(변경하려는 픽셀과 원래의 디스플레이 표면) bit연산을 수행하는 것을 의미 • Raster : 그래픽 디스플레이 장치의 방식에 관한 용어, 이미지를 점들의 패턴으로 표현하는 방식 • 윈도우 시스템은 DC에 대한 출력 시 사용되는 Raster Operation에 대한 Mix모드를 설정할 수 있는 SetROP2라는 기능 제공
int SetROP2( HDC hdc, // handle of device context int fnDrawMode // drawing mode );
// 출력시 Source는 Destination Pixel에 무조건 덮어씌움 SetROP2(hdc, R2_XORPEN); X Y XOR 0 0 0 0 1 1 • 0 1 1 1 0 XOR는 X,Y가 서로 다르면 1
XOR의 직관적인 의미는 원하는 bit를 지우는 데 (Erase) 사용할 수 있음 X 0100 Y 0011 ----------- Result 0111 Y 0011 //여기에 Y 패턴을 한번 더 XOR ----------- Result 0100 • XOR 연산을 이용하면 특정 Pattern을 최초에는 쓰게 되고, 두번째 XOR는 그 특정 Pattern만을 배경에 관계없이 지울 수 있게 됨 • 결론적으로 그림을 배경과 관계없이 지우는 데에는 XOR연산이 사용됨 // XOR
InvalidateRect를 호출하지 않고도, 이전 그림을 즉각적으로 지운 후 다시 새로운 좌표에 그릴 수 있음 SetROP2(hdc, R2_XORPEN); DrawObject(hdc, m_Rect, RGB(0,255,0), 1); // 이전 Rect지움 // 좌표 변경…… DrawObject(hdc, m_Rect, RGB(0,255,0), 1); // 새로운 위치에 다시 그림
// 처음부터 XOR 모드로 출력하여, 잔상이 // 남지 않도록 함 // 이전 그림을 지우는 작용 // 새로운 위치에 다시 그림을 // 그리는 작용
애니메이션의 원리 • 타이머에 의해 움직이는 그림은 시각적으로 애니메이션 효과를 나타냄 • Part II의 8장에서 작성했던 TestBallNBar프로그램을 RasterOperation을 사용하여 바꾸면 깜박임 현상이 없어짐 • 8장의 예제는 InvalidateRect를 호출하였으므로 심한 깜박임 현상이 나타남
// default 처리 : 특별히 다른 ROP 코드를 지정하지 // 않는 한 XORPEN이 적용됨 // DrawObject함수내에서 Raster Mode를 지정하도록 하면, // 다른 곳에서 일일이 매번 지정하지 않아도 됨
// Block들의 Rect배열 초기화 // Block 그려주는 함수 // 바의 영역 아래로 공이 갔을 때, 체크하는 함수 // 볼이 각 Block에 맞았는 지 체크하는 함수
실습 • 왼쪽 벽 HitCount, 위쪽 벽 HitCount, 오른쪽벽 HitCount 출력 • 벽돌 색 변경, 벽돌 숫자 위의 그림처럼 축소 및 확대