검색결과 리스트
분류 전체보기 에 해당되는 글 580건
- 2017.03.05 OpenGL 렌더링 컨텍스트
- 2017.03.05 픽셀 포맷
- 2017.02.07 탭 컨트롤 세로 방향
- 2017.02.07 Visual Studio 이전 스타일 설정
- 2017.02.07 탭 컨트롤
- 2017.01.17 가상메모리
- 2016.12.08 더블 버퍼링 구현 방법
- 2016.12.01 더블 버퍼링
- 2016.11.22 MFC 다이얼로그에서 전체화면 그리기
- 2016.11.18 DC의 정의
- 2016.11.18 윈도우의 정의
- 2016.11.18 컨트롤
- 2016.11.17 GDI 오브젝트
- 2016.11.17 DC의 필요성
- 2016.10.18 각 사용하는 방식 자투리
윈도우즈 응용 프로그램은 다수의 창을 포함하는 것이 보통
따라서 원한다면 각각의 창에 픽셀 포맷을 지정하는 것도 가능
각각의 창에 대해 한 번씩 SetPixel Format 함수를 호출
현재의 렌더링 창을 지정하는 방법을 구현
GDI 함수들이 이용하는 창 장치 컨텍스트의 개념이 OpenGL 환경에서는 렌더링 컨텍스트라 불리는 개념에 포함
장치 컨텍스트가 GDI를 위한 드로잉 모드와 명령을 기억하는 것처럼 렌더링 컨텍스트는 OpenGL 설정과 명령을 기억
wglCreateContext 함수를 이용하면 OpenGL 렌더링 컨텍스트를 만들 수 있음
이 함수는 올바른 픽셀 포맷을 가진 창의 장치 컨텍스트를 인자로 받으며 OpenGL 렌더링 컨텍스트의 데이터 타입은 HGLRC 이다
HGLRC hRC;
HDC hDC;
hRC = wglCreateContext(hDC);
렌더링 컨텍스트는 이를 만드는데 이용한 창과 호환
두 개의 창이 서로 다른 드로잉 모드나 원근 모드를 사용할 수 있으므로 응용 프로그램은 두 개 이상의 렌더링 컨텍스트 가질 수 있음
OpenGL 명령은 작업할 목적지 창을 알아야 하므로 한 스레드 당 하나의 렌더링 컨텍스만을 활성화
활성화된 컨텍스트를 "현재 컨텍스트"
렌더링 컨텍스트는 디바이스 컨텍스트와 연결되며 결국 특정한 창과 연결
OpenGL 명령이 어느 창에 렌더링 해야 하는지 알 수 있다
하나의 창에서 다른 창으로 OpenGL 렌더링 컨텍스트를 바꿀 수도 있지만 이 때는 각 창이 같은 픽셀 포맷을 가져야 함
렌더링 컨텍스트를 활성화 하고 특정한 창과 연결하는 데는 wglMakeCurrent 함수가 사용
wglMakeCurrent(hDC, hRC);
윈도우즈의 디바이스 컨텍스트 개념은 2D 그래픽 응용 프로그램에 맞게 디자인
메모리 장치 컨텍스트를 만들지만, 이 경우 모델 역할을 해줄 장치 컨텍스트를 제시
창 인자에 NULL을 지정하더라도 데스크 톱의 장치 컨텍스트를 모델로 이용
3D 그래픽 렌더링의 대상이 되는 창이나 장치는 단순한 색상 깊이 이상의 다양한 특성을 필요하며 하드웨어 렌더링 장치 (3D 그래픽 카드)를 이용하는 경우는 더욱
OpenGL이 렌더링을 시작하기 전에 필요한 렌더링 설정에 따라 창을 설정
하드웨어와 소프트웨어 렌더링 중 어떤 방식을 사용하는가?
단일 버퍼와 이중 버퍼 방식 중 어떤 방식을 사용하는가?
깊이 버퍼가 필요한가?
스텐실 버퍼와 목표 알파, 누적 버퍼 등을 사용하는가?
일단 이러한 창의 인자를 지정한 뒤에는 나중에 변경 불가능
깊이 버퍼와 색상 버퍼만을 가진 창을 스텐실과 색상 버퍼만을 가진 창으로 바꾸고자 한다면, 첫 번째 창을 제거하고 새로운 특성을 가진 창을 다시 만들어야 함
일반적으로 창의 3D 특성은 창이 만들어진 직후에 한 번 설정
픽셀 포맷을 지정하기 위해 PIXELFORMATDESCRIPTOR 구조체를 제공
이들 멤버 값은 임의로 결정할 수 없으며 하나의 창에 대해서 제한된 종류의 픽셀 포맷만을 사용
픽셀 포맷값은 OpenGL 드라이버나 소프트웨어 렌더러에서 얻을 수 있음
* 픽셀 포맷의 선택과 설정
ChoosePixelFormat 제공
이 함수는 먼저 여러분이 원하는 3D 창의 속성들을 포함하는 픽셀 포맷 구조체를 만들고 이와 가장 유사한 픽셀 포맷을 찾아 이에 해당하는 인덱스를 리턴
이렇게 받은 픽셀 포맷을 두 번째 윈도우즈 함수 SetPixelFormat에 전달하여 픽셀 포맷을 지정
무엇보다 먼저 원하는 3D 창의 특성을 나타내도록 PIXELFORMATDESCRIPTOR 구조체를 채워야 함
ChoosePixelFormat 함수는 항상 바른 픽셀 포맷을 찾아 돌려주며 가능하면 하드웨어 가속 픽셀 포맷을 우선적으로 선택
도구 - 옵션 - 키보드 - 다음 추가 키보드 매핑 구성표 적용 - Visual C++ 6
이러면 빌드를 F7을 눌러서 실행할 수 있다
Ctrl + D 찾기 창 메뉴에 보이게 하기
파일에서 찾기 아이콘 옆에 단추 추가/제거 를 눌렀을 때
Ctrl + D 를 단축키로 하는 찾기 창을 추가할 수 있다
1. 탭 컨트롤을 다이얼로그에 붙인 후 변수를 추가한다
카테고리(범주) : Control
CTabCtrl m_tabCtrl;
2. 탭 개수를 추가하고 헤더에 이름을 추가한다
3. 각 탭에 다이얼로그를 보여줄 다이얼로그를 생성한다
그대로는 자식 윈도우로 사용할 수 없으므로 일부 속성을 변경
Border 속성 : None
Style 속성 : Child
- 이렇게 수정한 대화상자 리소스는 메인 프레임 윈도우가 되지 못하고 특정 윈도우의 자식 윈도우로 생성/동작
4. 수정한 대화상자의 클래스를 추가한다
5. 탭 컨트롤 영역을 얻어와 탭 컨트롤의 자식으로 각 다이얼로그를 생성하고
보여주는 다이얼로그를 설정한다 - OnInitDialog()
6. 탭을 눌렀을 때 이벤트 처리는 현재 보여주는 윈도우는 닫고 선택한 윈도우를 보여준다 - OnTcnSelchangeTab()
7. 창이 삭제될 때 객체들을 삭제한다 - OnDestroy()
가상 메모리
- 물리적인 메모리(RAM)와 하드디스크의 페이징 파일 합한 것
- 사실 페이징 파일은 물리적인 RAM과 논리적으로는 동일하되 다만 속도가 좀 느린 메모리
- 응용 프로그램 입장에서 볼 때 자신의 주소 공간에 연결된 가상 메모리가 물리적인 RAM인가 페이징 파일인가는 전혀 신경쓰지 않음
- 운영체제의 안정성에도 큰 역할
- 각 프로세스의 주소 공간은 상호 독립적이기 때문에 프로세스 끼리 서로의 주소 영역을 침범할 수 없다
- 운영체제는 프로세스가 생성될 때마다 독립적인 4G 바이트의 주소 공간을 생성하고 물리적인 메모리를 논리적인 주소 공간에 연결
- 물리적인 메모리와 논리적인 주소 공간의 대응관계는 페이지 테이블이라는 표에 작성
- 페이지 테이블에는 가상메모리의 어디쯤이 응용 프로그램의 누구의 몇 번지에 연결되어 있다는 정보가 기록
- 응용 프로그램은 오로지 자신의 주소 공간 상의 번지만 다룸
- 프로세스는 자신에게 주어진 4G의 가상 주소 공간이 실제의 메모리인 것 처럼 사용
- 주소 공간 : 이 페이지 테이블에 기록 되어 있는 응용 프로그램이 참조하는 주소값
- 가상 주소 공간 : 실제로 존재하는 메모리가 아니라 다만 페이지 테이블에 기록되는 개념적인 주소
* 핵심은 모든 그리는 것을 비트맵에 그리고 나중에
화면에 결과 비트맵을 보여준다
기존 DC 와 호환되는 Memory DC를 생성하고 거기에 비트맵을 선택하여
Memory DC를 이용하여 그림을 그린 다음
Bitmap을 기존 DC를 이용하여 그려준다
다시 순서를 정리하면
1. 기존 DC와 호환되는 Memory DC 객체 생성 - CreateCompatibleDC( );
2. 기존 DC와 호환되는 비트맵 객체 생성 - CreateCompatibleBitmap( );
3. 메모리 DC가 비트맵 객체를 선택 - SelectObject( )
4. 메모리 DC를 가지고 그림을 그림 - MoveTo, LineTo, Ellipse, Rect 등등
5. 비트맵 객체를 기존 DC를 가지고 그려줌 - BitBlt( )
6. 메모리 객체 해제
void CView::OnPaint(CDC* pDC)
{
// 1. memDC 객체 생성
CDC memDC;
memDC.CreateCompatibleDC(pDC);
// 2. 메모리 비트맵 생성
CRect rect;
GetClientRect(rect);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
// 3. 메모리 DC 가 메모리 비트맵 선택
CBitmap* oldmap = memDC.SelectObject(&bitmap);
// 4. 그려주기
memDC.MoveTo(0, 0);
memDC.LineTo(100, 100);
// 5. 그려주기
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
// 6. 메모리 객체 해제
memDC.SelectObject(oldmap);
memDC.DeleteDC();
bitmap.DelectObject();
}
* 더블 버퍼링
- 화면에 보여줄 버퍼와 내부 작업에 사용할 버퍼를 따로 유지
- 내부 버퍼에 미리 그림을 그린 후 화면 버퍼로 고속 전송
- 그리는 중간 과정을 숨겨진 내부 버퍼에서 처리
- 더블 버퍼링에 사용되는 내부 버퍼는 구체적으로 메모리 영역인데
이 메모리의 영역은 외부 버퍼, 즉 화면의 포맷과 호환되어야 한다
- 그래야 내부 버퍼에 그린 그림을 별도의 조작없이 외부 버퍼로 고속 전송 가능
- 윈도우즈에서는 내부 버퍼를 메모리에 직접 작성할 필요가 없는데
왜냐하면 비트맵이 내부 버퍼 역할을 멋지게 대신함
- 화면 DC 와 호환되는 비트맵(색상 포맷이 같고 크기가 동일)을 생성한 후
이 비트맵에 그림을 긜면 비트맵 자체가 내부 버퍼 역할을 함
- 비트맵에 그려진 화면을 전송할 때는 BitBlt 함수 사용
ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);
CRect rect;
GetDesktopWindow()->GetWindowRect(rect);
MoveWindow(rect);
ShowWindow(SW_SHOWMAXIMIZED);
RedrawWindow();
- 윈도우즈에서 화면상에 무엇인가를 그리는 과정은 상상외로 복잡하다
- 대충 출력하기는 쉽지만 꼭 필요할 때 최대한 짧은 시간에 최소한의 영역만 그리면서도 깜빡임을 최소화 하는 것은 보통 어려운 것이 아니다
- 그리기를 얼마나 빠른 시간에 효율적으로 할 수 있는가에 따라 응용 프로그램의 질에 큰 차이가 나게 되므로 결코 가볍게 다룰 수 있는 주제가 아니다
- 윈도우즈에서 그리기가 복잡하고 난해한 근본적인 이유는 여러 개의 프로그램이 동시에 실행되는 멀티 태스킹 운영체제
- 윈도우즈 프로그램은 화면을 혼자서 사용하지 못하며 화면에 출력하는 것이 아니라 자신이 차지하고 있는 윈도우에 그것도 허가된 영역에만 그릴 수 있다
- 게다가 공간적으로 제약이 따를 뿐만 아니라 한 번 그려 놓은 그림이 항상 그대로 있다고 보장되지 않는 시간적 제약도 있다
- 복수 개의 프로그램이 같은 화면에 겹쳐서 공존하다 보니 서로 간에 지켜야 할 약속과 제약이 존재하고 그리는 과정도 복잡하다
- 이런 복잡한 과정을 조금이나마 단순화하기 위한 장치가 DC
- DC에는 그리기에 필요한 여러 가지 정보가 저장되어 있으며 프로그램은 DC의 정보를 참조하여 그리기를 하고 DC의 정보를 조작하여 그리는 방법을 변경
-
윈도우의 일반적인 세가지 특징
1. 윈도우는 화면상에 존재한다
메모리상에만 내부적으로 존재하는 것이 아니라 화면상에 나타나며 사용자의 눈에 보인다
숨겨지거나 가려지거나 일시적으로 가시 영역을 벗어나는 경우가 없지는 않지만 이 경우에도 당장 보이지 않을 뿐 여전히 화면 상에 있다
2. 그 모양은 반드시 직사각형이다
즉, 화면상의 좌상단 좌표를 가지고 높이 와 폭이 있으며 각 변끼리는 수직을 이룬다
둥근 모양이나 세모 모양의 윈도우도 만들 수 있지만 이 경우도 나머지 부분이 투명할 뿐이지 결국은 직사각형이다
3. 윈도우는 독립적으로 사용자와 상호작용을 할 수 있다
실행 결과를 화면에 출력하기도 하고 사용자로부터 입력을 받기도 한다
사용자로부터 명령을 처리하여 다시 화면으로 출력을 내보내거나 다른 윈도우로 재 입력을 보내기도 한다
윈도우가 상호작용을 한다는 말은 단순히 존재하기만 하는 것이 아니라 능동적인 동작을 한다는 얘기며 문법적으로 하자면 사용자로부터 또는 시스템으로부터 입력된 명령과 신호를 처리하는 메시지 처리 능력이 있다는 뜻이다
* 컨트롤
- 사용자와의 인터페이스를 이루는 도구
- 인터페이스를 이룬다는 말은 사용자로부터 명령과 입력을 받아들이고 출력결과를 보여준다
- 컨트롤은 입출력도구
- 프로그램은 실행 중에 끊임없이 사용자와 통신
- 컨트롤에 명령과 정보를 받아들이고 또한 컨트롤을 통해 실행 결과를 사용자에게 보고
- 버튼, 에디트, 리스트 박스, 콤보 박스, 스크롤 바, 스태틱
- 컨트롤도 하나의 윈도우
- 화면의 일정한 영역을 차지
- 자신의 고유 메시지를 처리할 수 있는 능력
- 메모장이나 탐색기 같은 진짜 윈도우처럼 타이틀 바나 경계선을 가지고 독립적으로 사용되는 것은 아님
- 보통 대화상자의 차일드 윈도우로 존재
- 윈도우를 만들 때는 WNDCLASS 형의 구조체를 정의하고 RegisterClass 함수로 등록한 후 CreateWindow 함수를 호출
- 그러나 컨트롤은 윈도우즈가 운영체제 차원에서 제공하기 때문에 윈도우 클래스를 등록할 필요 없이 미리 등록되어 있는 윈도우 클래스를 사용하기만 하면 된다
* DC (Device Context)
- 출력에 필요한 모든 정보를 가지는 데이터 구조체
* DC를 얻는 방법
1. GetDC() 를 사용하여 DC를 얻고 사용 후 ReleaseDC() 로 해제
- DC는 주로 하나의 윈도우와 연관되는 출력 정보를 가진다
- 그래서 인수로 어떤 윈도우에 대한 DC가 필요한가를 알려주어야 함
- GetDC() 는 hWnd가 가리키는 윈도우에 적당한 DC를 만들어 그 핸들을 리턴
- DC도 메모리를 차지하므로 할당 후 해제 원칙이 반드시 준수
HDC hdc = GetDC(hWnd);
각종 출력
ReleaseDC(hWnd, hdc);
2. 두 번째 얻는 방법은 WM_PAINT 메시지 루틴에서만 사용 가능
- BeginPaint()로 얻으며 핸들을 해제할 때는 EndPaint() 함수 사용
- PAINTSTRUCT 형의 구조체를 지역 변수로 선언하고 다음과 같이 사용
PAINTSTRUCT paintStruct;
HDC hdc;
WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
각종 출력
EndPaint(hWnd, &ps);
- PAINTSTRUCT 에는 그리기 속도를 비약적으로 향상시킬 수 있는 정보들이 있음
- 이 정보를 활용하는 방법에 대해서는 다음에 자세하게 배울 것
CKMSceneDataSeries *pSeries = g_Doc.m_pSceneData->GetDefaultSeries();
CKMVolume *pVol = pSeries->m_pVolume;
short *pVolData = (short*)(pVol->GetDataSource());
int nDim[3] = {pVol->m_nDim[0], pVol->m_nDim[1], pVol->m_nDim[2]};
m_fPixelSpacing[0] = (float)(pVol->m_fSpacing[0]);
m_fPixelSpacing[1] = (float)(pVol->m_fSpacing[1]);
m_fPixelSpacing[2] = (float)(pVol->m_fSpacing[2]);
m_volumeData->data.setValue(SbVec3i32(dimension), SbDataType(SbDataType::SIGNED_SHORT), 0, pData, SoSFArray::COPY);
m_volumeData->extent.setValue(-(spacing[0] * dimension[0])/2, -(spacing[1] * dimension[1])/2, -(spacing[2] * dimension[2])/2,
(spacing[0] * dimension[0])/2, (spacing[1] * dimension[1])/2, (spacing[2] * dimension[2])/2);
SbVec3i32 dimension = pVolumeData->data.getSize();
/*SoVolumeData* volumeData = new SoVolumeData();
short* rawData = (short*)m_pSceneData->GetDefaultSeries()->m_pVolume->GetDataSource();
int dimension[3] = { m_pVolume->m_nDim[0], m_pVolume->m_nDim[1], m_pVolume->m_nDim[2] };
float spacing[3] = { m_pVolume->m_fSpacing[0], m_pVolume->m_fSpacing[1], m_pVolume->m_fSpacing[2] };
volumeData->data.setValue(SbVec3i32(dimension), SbDataType(SbDataType::SIGNED_SHORT), 0, rawData, SoSFArray::COPY);
volumeData->extent.setValue(-(spacing[0] * dimension[0])/2, -(spacing[1] * dimension[1])/2, -(spacing[2] * dimension[2])/2,
( spacing[0] * dimension[0])/2, (spacing[1] * dimension[1])/2, (spacing[2] * dimension[2])/2);
m_pRootSeparator->addChild(volumeData);*/
#define SO_MOUSE_PRESS_EVENT(EVENT,BUTTON) \
(SoMouseButtonEvent::isButtonPressEvent(EVENT,SoMouseButtonEvent::BUTTON))
#define SO_MOUSE_RELEASE_EVENT(EVENT,BUTTON) \
(SoMouseButtonEvent::isButtonReleaseEvent(EVENT,SoMouseButtonEvent::BUTTON))