Visual Studio 2019 한글 형식(인코딩). 유니코드, EUC-KR

프로그래밍 언어/WinApi 2020. 7. 28. 10:17

문자를 입력 받았을 경우, 한글인지 아닌지 판별하기 위하여 작업 도중,

 

WCHAR, char의 한글 형식을 같은 유니코드를 사용하고 있는 줄 알았으나

 

값이 다르게 나와서 찾아 보았다.

 

 

한글의 형식은 컴파일러마다, 운영체제마다 다르다고 한다.

 

Window10, Visual Studio 2019의 환경에서

 

WCHAR 형은 유니코드를 사용하고

 

char 형은 EUC-KR을 사용하는 것 같다.

 

ex)

    //WCHAR eee[20] = TEXT("가나하");
    unsigned char eee[20] = "가나하";
    unsigned char eee1;
    unsigned char eee2;

    eee1 = *((unsigned char*)eee + 0); 
    eee2 = *(((unsigned char*)eee) + 1); 
    TextMB::Box(eee1, eee2);

    eee1 = *((unsigned char*)eee + 2);
    eee2 = *(((unsigned char*)eee) + 3);
    TextMB::Box(eee1, eee2);

    eee1 = *((unsigned char*)eee + 4);
    eee2 = *(((unsigned char*)eee) + 5);
    TextMB::Box(eee1, eee2);

 

  유니코드 WCHAR char
AC00 00AC B0A1
B098 98B0 B3AA
D558 58D5 C7CF

 

둘 다, 두번째 바이트에 최상위 비트가 1인지 0인지 확인하면 한글 판별이 가능할 것 같다.

 

그런데 WCHAR의 값은 리틀엔디안 형식이니 반대로 나온게 맞는것 같은데,

char의 값은 그대로 나왔다. 뭐지..?

 

-- 유니코드 문자표

https://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_%EC%98%81%EC%97%AD

https://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_A000~AFFF

 

-- EUC-KR 문자표

https://uic.win/ko/charset/show/euc-kr/

 

-- CP949

https://charset.fandom.com/ko/wiki/CP949

 

 

EUC-KR를 찾다가 보니 CP949, KS X 1001, KS X 1003 등등 여러 인코딩이 나오던데

언급은 하지 않겠다...

https://codingdog.tistory.com/entry/cp949-vs-euc-kr-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EC%9E%88%EB%8A%94%EC%A7%80-%EA%B0%84%EB%8B%A8%ED%9E%88-%EC%95%8C%EC%95%84%EB%B4%85%EC%8B%9C%EB%8B%A4

설정

트랙백

댓글

win32api 서브클래싱, 전역클래싱, 슈퍼클래싱

프로그래밍 언어/WinApi 2020. 7. 27. 13:23

Tab(탭) 키로 각 차일드 윈도우간 포커스 이동을 하려고 했더니 클래싱을 사용해야할 것 같다.

 

# 서브클래싱

서브클래싱이란 도스의 인터럽트 가로채기와 개념적으로 동일하며 윈도우 프로시저로 전달되는 메시지를 중간에
가로채는 기법이다.

운영체제 -> 서브클래스 프로시저 -> 윈도우 프로시저

서브클래스 프로시저는 메시지를 먼저 받아서 다음의 세가지 방법으로 이 메시지를 처리할 수 있다.
① 통과 : 관심 없는 메시지는 바로 윈도우 프로시저로 넘겨버린다.
② 직접 처리 : 원하는 메시지가 왔을때 중간에 메시지를 처리하고 리턴. 윈도우 프로시저로는 전달되지않는다.
③ 변형 : 메시지를 받아서  원하는 변경 후 윈도우 프로시저로 보낸다. (키입력의 변경등)


새로운 윈도우 프로시저를 생성해서 아래 함수를 통해 변경할 대상 윈도우의 새로운 메시지 처리 함수를 지정한다.
원래번지=SetWindowLongPtr(변경할 대상윈도우,GWLP_WNDPROC,서브클래스 프로시저)서브 클래스 프로시저에서는 원하는 메시지 이외에는 다시 윈도우 프로시저로 넘겨줘야 하기때문에 관심없는
메시지에 대해서는 프로시저 마지막에 아래 함수를 사용하여 메시지를 다시 윈도우 프로시저로 넘겨주면된다.
LRESULT CallWindowProc(WNDPRCO lpPrevWndFunc,HWND hWnd,UINT Msg,WPARAM wParam,
LPARAM lParam);
메시지를 마음대로 변경할 수 있는 서브클래싱은 아주 유용하고도 강력한 기법임에 틀림없지만 그 이면에는 항상
잠재적인 위험성이 도사리고 있어 아주 조심스럽게 사용해야 한다. 꼭 필요하지 않는 한 서브클래싱은 하지 않는
것이 좋지만 불가피할 경우는 다음의 주의사항을 잘 숙지하고 안전하게 사용하자.

1. 윈도우의 원래 기능을 보존할 것.
2. 필요한 메시지를 제외한 메시지는 온전하게 윈도우 프로시저로 보낼 것.
3. 여분 메모리(cbWndExtra,cbClsExtra)를 절대로 건드리지 말것.
4. 여분 메모리가 필요한 경우 윈도우 프로퍼티를 사용할 것.

 

 

예제)

 

HWND hSub;        // 서브클래싱할 윈도우 핸들
WNDPROC wp_OldSubProc;        // 서브클래싱하기 전의 윈도우 프로시져 주소

 

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) 
{ 
    ... 
    WNDCLASS WndClass;


    switch (iMessage)

    { 
    case WM_CREATE: 

 

    ...

 

        // 프로시저 변경

        wp_OldSubProc = (WNDPROC)SetWindowLong( hSub, GWL_WNDPROC, (LONG)Test1Proc );

 

    ...

 

        // 프로그램 종료시 다시 원래의 프로시져 주소로 교체 해준다.
        SetWindowLong( hSub, GWL_WNDPROC, (LONG)wp_OldSubProc );

    ...

}

 

 

LRESULT CALLBACK Test1Proc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    switch( iMessage )
    {
    case WM_KEYDOWN:
        break;
    }

    return CallWindowProc( wp_OldSubProc, hWnd, iMessage, wParam, lParam);
}




# 전역 서브클래싱

서브 클래싱도 시점과 방법, 그리고 효과가 미치는 범위에 따라 다음 두 가지로 분류된다.

■ 인스턴스 서브클래싱 : 특정한 하나의 윈도우 만을 대상으로 서브클래싱, SetWindowLongPtr 사용된다.

■ 전역 서브클래싱 : 윈도우 클래스에 대해 서브 클래싱, 이미 만들어진 윈도우에 대해서는 효과가 없으며 앞으로
  만들어질 윈도우만 영향을 받는다. SetClassLongPtr이 사용된다.


에디트를 생성하고 에디트를 대상으로 전역 서브 클래싱을 할 경우 에디트가 속한 "edit"윈도우 클래스가
서브 클래싱의 대상
이 되므로 서브 클래싱한 이 후 생겨나는 모든 edit는 서브 클래싱의 영향을 받게된다.

SetClassLongPtr(hEdit1,GCLP_WNDPROC,(LONG_PTR)OldEditProc);전역 클래싱은 윈도우 클래스 자체를 바꾸는 것이지만 다른 프로세스에는 영향을 주지 못한다.
시스템 전역 클래스에 정보는 시스템이 가지고 있지만 각 프로세스가 참조하는 클래스 정보는 응용프로그램이
시작될 때 시스템으로 전달받는 복사본일 뿐이다. 즉 서브클래싱은 같은 프로세스 내에서만 효력을 발휘한다.


# 슈퍼클래싱

슈퍼클래싱은 기존클래스(=베이스 클래스)의 정보를 바탕으로 하여 완전히 새로운 클래스를 만드는 것이다.
슈퍼클래싱에 의해 새로 만들어진 윈도우 클래스는 베이스 클래스와 동일하지만 여기에 변화를 주면 원하는대로
윈도우 클래스를 수정할 수 있다.

슈퍼클래싱의 핵심 함수는 GetClassInfo(Ex)함수이다.

BOOL GetClassInfo(HINSTANCE hInstance,LPCTSTR lpClassName,LPWNDCLASS lpWndClass);이 함수는 lpClassName 윈도우 클래스의 정보를 조사하여 lpWndClass가 가리키는 WNDCLASS 구조체에
채운다. 첫번 째 인수는 이 윈도우 클래스를 만든 응용프로그램의 인스턴스 핸들이되 버튼, 에디트 등의 시스템에
의해 만들어진 윈도우 클래스일 경우는 NULL을 주면 된다. 이 함수 호출에 의해 윈도우 클래스의 정보를 조사할
수 있는데 예를 들자면 GetClassInfo(NULL,"edit",&WndClass); 함수를 호출하면 "edit" 윈도우 클래스의 정보를
WndClass 구조체에 복사한다. 이 함수는 등록된 윈도우 클래스의 정보를 다시 돌려받으므로 RegisterClass함수의
반대 함수라고 생각하면 쉽다.

WndClass 구조체에 윈도우 클래스의 정보가 조사되었으므로 이 구조체의 정보를 바탕으로 적절히 수정한 후 다시
RegisterClass로 새로운 윈도우 클래스를 등록하는 것이 바로 슈퍼클래싱이다.

원본(GetClassInfo) ->  사본(수정) -> 새로등록(RegisterClass)

WndClass 구조체의 정보중 몇가지는 반드시 수정되어야 한다. 일단 클래스 이름이 베이스 클래스와 같아서는
안되므로 lpszClassName 멤버가 달라져야 한다. 또한 hInstance 멤버에는 이 윈도우 클래스를 등록하는 응용
프로그램의 인스턴스 핸들을 대입해야 하는데 그래야 이 인스턴스가 종료될 때 등록해제 된다. 그리고 가장 중요한
lpfnWndProc멤버를 수정하여 응용 프로그램이 정의한 윈도우 프로시저로 메시지를 보내도록 해야 할 것이다.
단 이때 기존의 윈도우 프로시저 번지는 복구를 위해 잘 보관해 두어야 한다.


[슈퍼 클래싱 예제]
LRESULT CALLBACK SuperProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    switch (iMessage)
    {
    ...
    }
    return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    ...
    WNDCLASS WndClass;


    switch (iMessage)

    {
    case WM_CREATE:
        GetClassInfo(NULL,TEXT("edit"),&WndClass); // "edit"클래스에 대한 정보 조사
        WndClass.hInstance=g_hInst;
        WndClass.lpszClassName=TEXT("SuperEdit"); // 새로운 이름 등록
        WndClass.hCursor=LoadCursor(NULL,IDC_WAIT); // 커서 변경
        OldEditProc=WndClass.lpfnWndProc; // 기존의 윈도우 프로시저 저장
        WndClass.lpfnWndProc=SuperProc; // 새로운 프로시저로 변경
        RegisterClass(&WndClass); // 수정한 윈도우 등록
        hEdit1=CreateWindow(TEXT("SuperEdit"),NULL,WS_CHILD | WS_VISIBLE |
            WS_BORDER | ES_AUTOHSCROLL,
            10,10,200,25,hWnd,(HMENU)ID_EDIT1,g_hInst,NULL);
        SetFocus(hEdit1);
        return 0;

    ...
}

 

서브 클래싱과 슈퍼클래싱은 둘다 사용목적이 비슷하며 상호 대체성이 있다. 어떤 방법을 쓸 것인가는 상황에
따라 다르겠지만 대체로 슈퍼클래싱이 더 안전하고 부릴 수 있는 기교도 더 많다.
특정 윈도우 하나만 수정할 때는 인스턴스 서브클래싱을 사용하고 다량의 윈도우를 한꺼번에 바꿀 때는 전역 또는
슈퍼클래싱을 사용하는 것이 편리하다.

 

 

 

 

참고 사이트 : http://egloos.zum.com/itbaby/v/4513218

참고 사이트 : http://blog.daum.net/creazier/15309359

설정

트랙백

댓글

Visual Studio 2019 win32api 아이콘 변경이 안된다

프로그래밍 언어/WinApi 2020. 7. 22. 13:32

리소스에 아이콘 추가 후,

 

메인 클래스에

 

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON1));

 

를 수정해도 탐색기에 표시되는 아이콘이 변경되지 않았다.

 

 

자료를 찾아도 MFC, C#은 나와있지만 win32api는 없었다.

아마 내부적으로 캐시된게 잘 처리가 안되지 않았을까... 라고 누가 그러더라..

아래와 같이 다시 불러와 주니 바뀌었다.

 

 

프로젝트에서 기존항목을 누르고 변경할 아이콘을 선택한 후에 컴파일을 하니 바뀌었다.

 

다시 해보니 안된다....

 

껐다 키고 뭔짓을 해야된다.....

설정

트랙백

댓글

Visual Studio 2019 sprintf 에러 끄기

프로그래밍 언어/WinApi 2020. 7. 22. 12:05

Visual Studio 2019에서 컴파일시, sprintf 를 사용할 수 없음으로 에러가 뜰 경우

 

심각도 코드 설명 프로젝트 파일 줄 비표시 오류(Suppression) 상태
오류 C4996 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. MP_works C:\Users\root\Documents\Visual Studio 2019\Projects\MP_works\MP_works\MP_works.cpp

 

 

 

 

아래와 같이 대처

---> 메뉴에 프로젝트 -> 속성 -> 왼쪽 메뉴에 구성속성 -> C/C++ -> 일반 -> SDL검사를 아니오로 변경

 

 

 

설정

트랙백

댓글

윈도우즈 디바이스 드라이버 개발 방법

프로그래밍 언어/WinApi 2014. 12. 18. 21:52

윈도우즈 디바이스 드라이버 개발 방법

디바이스 드라이버 개발을 처음 시작하시는 분들이 가장 많이 하시는 질문은 “ 디바이스 드라이버를 개발하려면 무엇을 어떻게 공부해야 하나요 ? 라는 것이다 .

 

일반 애플리케이션 프로그램 개발을 시작하는 경우에는 자료도 많고 , 주변에 개발자들도 많아서 공부 방법이나 자료들을 쉽게 접할 수 있는데 , 디바이스 드라이버 분야는 개발자들이 많은 분야도 아니고 , 자료도 쉽게 찾을 수도 없다 보니 입문자들에게는 어디서부터 어떻게 시작해야 할지 막막해 한다 . 간혹, 디바이스 드라이버에 열정이 있으신 분들중에는 홀로 무수한 나날을 디버깅과 테스트를 통해 개발의 고수가 되는 경우도 있다.

하지만 , 일반 개발자들은 아직도 디바이스 드라이버 개발을 위해 어떻게 준비를 해야 할지 어려워하는게 현실이다. 그래서 이런 컬럼 에서는 필자가 그 동안 경험하고 느낀 것을 토대로 초보자들도 쉽게 접근할 수 있는  개발 방법들을 소개하려고 한다 .

우선 , 디바이스 드라이버 개발 방법의 내용을 언급하기 전에 여러분이 한 가지 알아두어야 할 사항이 있다 . 이전 1 부 컬럼 에서도 언급했듯이 디바이스 드라이버와 운영체제는 서로 밀접한 관계를 맺고 있다 . 그래서 이번 컬럼에서 다루는 윈도우 디바이스 드라이버 개발 방법이 다른 운영체제들에도 동일하게 적용되지 않을 수도 있다는 것을 참고해서 글의 내용을 이해했으면 한다 .

1. 무엇을 공부해야 하나 ?
응용프로그램 , 웹 프로그래밍 , 게임 등의 프로그램들처럼 디바이스 드라이버도 소프트웨어의 한 종류로 볼 수 있다 . 그래서 일반적인 대학교의 컴퓨터 학과들에서 배우는 알고리즘 , 운영체제 , 프로그래밍 언어 , 자료구조 , 컴파일러 등등의 다양한 지식들을 기본적으로 알고 있는 것은 디바이스 드라이버를 공부하는데 하는데 많은 도움이 된다 .

하지만 , 이런 기본적인 내용들 이외에 디바이스 드라이버를 개발하기 위해서 좀 더 알아야 할 지식들이 있다 . 그 내용들에 대해서 간략히 살펴보도록 하자 .

운영체제 아키텍처 : 다른 프로그램들도 마찬가지겠지만 , 프로그램이 동작하는 기반이 되는 운영체제를 이해하지 않고서는 제대로 된 프로그램을 개발 할 수가 없다 . 그 중에서도 디바이스 드라이버는 운영체제의 커널에서 동작 하므로, 운영체제 구조 및 개념들 ( 메모리 관리 , 스케줄링 , I/O 요청의 흐름 , 동기화 등등 ) 을 제대로 이해해야 개발하는데 문제가 없게 있게 된다 . 하지만 , 윈도우는 리눅스와 같은 Open Source 운영체제가 아니고, 내부 구조가  블랙박스처럼 베일에 싸여 있다보니,  O/S 내부 내용을 공부 하려해도 항상 정확한 자료들을 가지고 아니어서 이해하는데 많은 어려움이 있다. 그래서, 이런 경우엔  직접 디버깅을 통해 알아내야 하는데, 이런 부분은 윈도우 운영체제를 이해하는데 어렵게 하는 부분이 되고 있다.

Windows Shared Source programs

( http://www.microsoft.com/resources/sharedsource/Licensing/default.mspx )

Microsoft 에서는 윈도우 소스코드를 공유하는 “ Windows Shared Source programs 제도를 운영하고 있다 . 이 프로그램에 참여방법은 여러 가지 방법이 있는데, 그 중에 Microsoft MVP 자격으로 참여해 볼 수 있는 형태가 있다.  관심 있는 분들은 MVP 에 도전해 보길 바란다.  

•  컴퓨터 구조 및 CPU 아키텍처 : 운영체제 아키텍처를 이해하기 위해서는 먼저 컴퓨터 구조나 CPU 아키텍처의 이해가 필요하다 . 즉 , 넓은 의미로 운영체제도 CPU 및 컴퓨터 구조 기반 위에서 작동하는 프로그램이기 때문에 CPU 및 컴퓨터의 구조를 이해하는 것은 기본 중에 기본이다 . 그래서 기본적인 CPU 동작원리라던지 컴퓨터의 내부 구조에 대한 지식은 운영체제 및 드라이버를 이해하는데 필요하다.

•  C/C++ 언어 : 많이 질문하시는 내용 중에 “ 디바이스 드라이버는 어떤 언어로 개발하나요 ? ” 가 있다 . 윈도우 디바이스 드라이버 개발 시에는 C/C++ 언어 정도를 이해한다면 개발하는데 무리는 없다 . 그리고 , 가끔 디버깅 (Debugging) 이나 리버스 엔지니어링 (Reverse Engineering) 이 필요한 경우에 어셈블리 언어 (Assembly) 의 지식이 필요하다. 그렇다고 어셈블리 언어를 완벽한 이해해야 하는것은 아니다. 대신 남의 코드를 디버깅하는 경우 소스 코드가 어셈블리 코드로 보여 내용을 보고 분석할 줄 알아야 하는데 이때 간단히 코드를 보고 이해할 수 있을 정도의 지식이면 된다.

•  API 프로그래밍 : 디바이스 드라이버는 누군가의 요청에 의해 기능을 하게 된다 . 사용자가 GUI 를 가진 애플리케이션을 통해 I/O 요청을 하면 디바이스 드라이버가 해당 작업을 하게 되고 , 결과는 다시 애플리케이션에게 전달되는 구조를 가진다.  이처럼 디바이스 드라이버는 애플리케이션과 항상 상호작용을 한다.
그래서, API 프로그래밍을 공부할때는 단순 API 사용법의 내용이 아닌 디바이스 드라이버와 애플리케이션 사이의 통신 및 전체 I/O 구조에 대한 내용을 중심으로 공부하길 바라며 이 부분은 추후에 드라이버를 공부하는데도 많은 도움이 된다.

•  H/W 지식 : 디바이스 드라이버는 소프트웨어와 하드웨어 사이에 중간의 다리역할을 하는 프로그램이라 볼 수 있다 . 이러다 보니 디바이스 드라이버를 작성하기 위해서는 소프트웨어 지식 뿐만 아니라 , 하드웨어적인 지식도 필요로 하게 된다. 그렇다고 H/W 스펙의 전기적인 내용이나 물리적인 회로등의 이해를 요구하는 것은 아니다. 대신. 기본적인 H/W 스펙 문서에 나와있는 용어라던지 통신 프로토콜등에 대해 먼저 이해하기 바라며, 드라이버에서 사용되는 H/W 관련 개념들도 같이 공부하길 바란다.  

•  디바이스 드라이버 모델 : 윈도우에서는 디바이스 드라이버를 어떤 방식으로 개발하라는 드라이버 모델들을 제시하고 있으며, 이 모델들을 이용해 드라이버를 좀 더 쉽게 개발할 수 있게 해준다. 현재 윈도우에서 제공하는 드라이버 모델은 10 가지 이상의 방법들을 제시하고 있고 , 개발하려는 장치 및 드라이버 종류에 따라 추천하는 드라이버 모델들이 존재하고 있다 . 하지만 , 이런 다양한 드라이버 모델은 개발자들이 드라이버 종류에 따라 각각 새로운 모델들을 익히기 위해 시간과 노력을 배로 들게 하는 단점이 있다 . 그래서 Microsoft 에서는 이런 어려움을 해결하기 위해 새로운 드라이버 모델을 계속 발전시키고 , 통합하는 과정을 진행하고 있다 .

아래 표는 현재까지의 윈도우 운영체제의 종류에 따른 지원하는 드라이버 모델이다 .

Windows 종류

Driver Model

Windows 95/98/ME

VxD, WDM ( Windows 98/ME 에 해당 )

Windows NT

NT Kernel Mode Driver

Windows 2000/XP/2003/VISTA

NT Kernel Mode Driver, WDM, WDF


아래 표는 Microsoft 에서 디바이스 및 드라이버 종류에 따라 권장하는 드라이버 모델에 대한 설명이다 . ( http://www.microsoft.com/whdc/driver/foundation/DrvRoadmap.mspx# )

Device/Driver Class

Recommended Driver Model

1394 device

Depends on device class : KMDF or WDM

Auxiliary display (SideShow)

Windows Portable Devices (WPD),
which is UMDF-based

Bluetooth L2CAP

Depends on device class : KMDF or device-class-specific model such as AVStream

Bluetooth Radio Frequency Communication (RFCOMM)

UMDF

Cell phone, PDA, portable media player

WPD, which is UMDF-baased

Digital camera

Depends on device characterisitics :
Media Transfer Protocol (MTP)/UMDF or WIA

Display adapter

Windows Display Driver Model

File system filter

FS mini-filter

Generic filter driver

KMDF

Human input device (HID)

UMDF (except for mouse and keyboard) or KMDF

Keyboard/Mouse filter

KMDF

Modem, cable modem

KMDF, WDM, or NDIS-WDM

Network Transport Driver Interface (TDI) client

KMDF

Network-connected device

UMDF

Other

Depends on device class and characteristics : UMSF, KMDF, or WDM

Printer

UniDrv, PScript, XPS

Scanner

WIA

Secure digital (SD)

Depends on device class : KMDF or WDM

Serial and parallel devices (legacy)

UMDF or KMDF

Smart card device

WDM

USB device

Depends on device class : UMDF, KMDF, or WDM

Video capture

AVStream



Port/Adapter Class

Recommended Driver Model

Audio adapter

AVStream, PortCls

HID miniport

Microsoft-supplied or KMDF

Network adapter

NDIS-WDM

Network adapter (USB)

NDIS-KMDF

PC card

Microsoft-supplied or KMDF

PCI

Microsoft-supplied or KMDF

Serial, parallel port

Microsoft-supplied or KMDF

Storage adapter (SCSI and ATA)

Scsiport, Storport, ATA port

USB adapter

Microsoft-supplied or KMDF



VxD ( Virtual Device Driver ) : Windows 95/88/ME 에서 사용되는 드라이버 모델로 디바이스 드라이버를 가상화시킨다는 개념을 적용해 드라이버를 만든다 . 확장자는 vxd 로 만들어진다 .

NT Kernel Mode Driver : Windows NT 용 드라이버를 만들기 위한 드라이버 모델로 드라이버 개발을

WDM ( Windows Driver Model ) : 윈도우 디바이스 드라이버를 어떻게 만들어야 한다는 하나의 스펙이라고 볼 수 있다 . 하나의 드라이버 바이너리 파일을 가지고 다양한 윈도우 에서 사용 할 수 있는 드라이버를 만들기 위한 목적으로 만들어졌으며 , 현재 Windows 98/ME/2000/XP/2003/Vista 에서 사용할 수 있다 . 기본적인 개념은 NT Kernel Mode Driver 구조를 바탕으로 만들어 졌으며 , PnP, Power 관리 , WMI 등을 지원한다 .

WDF ( Windows Driver Foundation ) : Windows Vista 가 출시되면서 만들어진 드라이버모델로 WDM 모델에서 발전된 형태이다 . 윈도우 API 프로그래밍을 WDM 에 비유하면 , MFC 는 WDF 해당한다고 볼 수 있다 . WDF 는 크게 두 가지 종류로 나뉘는데 , KMDF ( Kernel-Mode Driver Framework ) 와 UMDF ( User-Mode Driver Framework ) 가 그에 해당된다 .

NDIS ( Network Driver Interface Specification ) : 윈도우에서 네트워크 관련 드라이버를 개발할 때 사용되는 드라이버 모델로 네트워크 드라이버를 좀 더 모듈화 시켜서 쉽게 만들 수 있는 방법을 제공해준다 .

그럼, 현 시점에서는 이렇게 "다양한 드라이버 모델중에 어떤 모델을 공부해야 하나?" 생각이 들것이다.
필자 생각에는 먼저 WDM 드라이버 모델을 공부하라고 말하고 싶다. 이유는 여러분들이 처음 애플리케이션 만들때를 생각해 보면, 처음엔 쉽게 개발을 하기 위해 라이브러리들이 많이 지원되는 MFC 나 비주얼 베이직, 델파이 등을 이용해 프로그램들을 만들었을 것이다. 하지만 이것의 내부 구조 및 원리들을 이해하기 위해서는 API 를 다시 공부 했어야 했다. 이것은 기본적인 원리를 이해하지 못하고는  발전된 개념들을 이해하기 어렵기 때문이다. 그래서 드라이버 모델에서도 API 와 같은 WDM 를 우선적으로 공부하면 드라이버의 기본적인 개념이나 구조를 이해하는데 많은 도움이 될 것이기 때문이다. 그리고나서, 필요에 따라 WDF 를 공부한다던지, 해당 Device Class에 따른 드라이버 모델들을 공부하는 방법을 권한다.

2. 어떻게 공부해야 하나 ?

프로그래밍을 배울 때 듣는 속담 중 하나가 “ 백문이 불여일견 , 백견이 불여일타 “ 라고 했다 . 그 만큼 실제 책으로 공부하고 눈으로 보는 것 보다는 직접 코딩을 하면서 프로그램을 작성하는 것이 백배 더 효과적이라고 한다 .

디바이스 드라이버도 이런 공부 방법대로 많이 만들어보는 것이 제일 중요하다고 생각한다 . 필자도 처음에는 디바이스 드라이버를 책으로만 볼 때는 무슨 내용들을 말고 있는지 , 어떻게 드라이버가 작동 되는지 이해가 잘 되지 않았지만 , 이것저것 개발을 하면서 조금씩 이해를 한 경험이 있다 .

그러나 , 처음 드라이버를 만들려고 해도 뭔가 이론적인 바탕이 있어야 하는데 이런 부분은 혼자 공부하기에는 어려운 부분이 많다 . 그래서 여러 명이서 스터디 그룹을 만들어 같이 공부하면서 서로 공부한 것들을 공유하는 방법을 생각해 볼 수 있을 것이다 . 그리고 , 개발자 저변은 넓지 않은 분야이다 보니 자료들을 찾아보는데도 한계가 있다 . 그렇기 때문에 다양한 세미나 및 개발자 모임 등에는 적극적으로 참여해서 정보를 얻어야 한다 . 그 다음에는 관심 있는 프로그램이나 분야 중에 디바이스 드라이버가 사용되는 것을 선정해 직접 만들어보면서 실제 이론 내용을 적용해 보는 형태로 공부를 하면 많은 도움이 될 것이다 .

그럼 , 여러분들이 개발하기 전에 몇 가지 고려해야 할 사항들을 소개하겠다 .

•  지원할 운영체제 : 제일 먼저 어떤 운영체제를 지원 할지를 결정해야 한다 . 그래야 지원하는 운영체제에 따른 H/W 지원 유무및 드라이버 모델이 정해지고 , 경우에 따라서는 디바이스 드라이버를 운영체제에서 기본적으로 지원하고 있어서 디바이스 드라이버 개발을 별도로 하지 않아도 되는 경우들이 있다 . 그렇기 때문에 우선 지원할 운영체제를 무엇으로 할지 선택해야 한다 .

•  Device 및 Driver Class : 개발하려는 디바이스 드라이버가 지원하는 장치 종류 및 드라이버가 어떤 종류인지 선택 해야 한다 . 다시 설명하면 , 장치관리자에 올라가는 장치 목록에 어떤 장치로 보이게 할 것인지를 선택해야 한다는 말이다 . 어떤 장치 종류이냐에 따라 앞에서도 언급한 드라이버 모델이 결정되어서 개발 방향이 결정이 되기 때문에 이 부분도 중요하게 결정되어야 한다 .

•  Windows Logo 인증 : 여러분들은 장치를 구매했을 때 박스나 PC 에 Windows Logo 스티커가 있는 걸 보거나 , 드라이버 설치시에 경고창이 나타나는 것을 본 경험이 있을 것이다 . 이 Logo 의 의미는 Microsoft 는 Windows 운영체제에서 하드웨어와 소프트웨어가 안정적으로 동작할 수 있도록 하드웨어와 소프트웨어의 완벽한 호환성에 관한 명확한 기준을 제공하고 있으며 , Microsoft 의 WHQL 에서는 제시한 기준에 적합한지를 시험해서 기준에 적합하면 Windows Logo 인증 마크를 사용할 수 있는 권한을 부여하는 제도 이다 . 이런 인증 제도는 비즈니스적인 측면과 관련된 부분이긴 하지만 , 여러분의 H/W 및 디바이스 드라이버 품질을 검증해 보길 원한다면 한번 인증을 진행해 보는 것도 좋은 방법이 될 거라 본다 . 대신 별도의 인증 비용이 들어가니 이점은 참고하기 바란다 .
( http://www.microsoft.com/whdc/winlogo/default.mspx )

디바이스 드라이버를 개발하기 위해 필요한 지식 및 방법에 대한 내용을 필자의 경험을 토대로 간략히 살펴봤다 . 공부에는 왕도가 없다라는 말이 있다 . 디바이스 드라이버를 공부하는 것도 특별한 비법이 있거나 빨리 실력이 느는 방법이 따로 있는것은 아니다. 특히나 드라이버나 시스템 프로그래밍쪽은 다른 분야보다 공부한 만큼 바로 바로 결과가 나오지 않아 쉽게 포기를 하기도 하는 분야다.  이럴수록 여러분들은 차근차근 기본기부터 다진다는 생각으로 공부을 했으면 한다. 
끝으로, 한 가지 더 부탁을 하자면, 인내와 끈기를 가지고 문제 해결를 끝까지 해결하려는 자세를 가졌으면 한다. 디바이스 드라이버를 개발하면서 느낀것은 쉽게 해결되는 문제가 없다는 것이다. 운영체제와 애플리케이션 , H/W 등의 다양한 구성 요소들 사이에서 동작을 하다 보니, 문제점을 파악하고 디버깅하고  테스트하는 과정은 정말 어려움 고난의 과정이다. 그래서 처음 생각과 달리 디버깅이나 테스트에서 많이들 힘들어하고 어려워한다. 하지만, 이런 과정은 여러분들이 고수로 가게 해주는 밑바탕이 될 것이며, 드라이버 개발을 재미를 느끼게 해주는 기폭제가 될것이라 본다.   

그럼, 이제 디바이스 드라이버 개발을 어렵게 느끼지 말고 바로 지금 공부를 시작해보자. 

 

출처 : http://driveronline.org/bbs/view.asp?tb=begin&no=5

설정

트랙백

댓글

Winapi) tooltip track이 안될 때

프로그래밍 언어/WinApi 2014. 11. 28. 00:16

Windows XP, 7 환경에서

툴팁(tooltip)에 TTF_TRACK 속성을 줬는데도 불과하고

실행되지 않을 때에는 아래와 같이 수정하면 잘 된다.


ti.cbSize  =  sizeof(TOOLINFO);


  ->   ti.cbSize  =  sizeof(TTTOOLINFOA_V2_SIZE);

or

  ->   ti.cbSize  =  sizeof(TTTOOLINFOW_V2_SIZE);

or

  ->   ti.cbSize  =  TTTOOLINFOA_V2_SIZE;

or

  ->   ti.cbSize  =  TTTOOLINFOW_V2_SIZE;


공동 컨트롤러의 옛 버전은 sizeof(TOOLINFO)를 사용하고

5.xx 버전 이상??은 sizeof(TTTOOLINFOA_V2_SIZE)를 사용한다고 한다.

버전이 바뀌면서 크기가 달라져서 그렇다고 한다.

설정

트랙백

댓글

Winapi) 소스에서 lib 추가하기

프로그래밍 언어/WinApi 2014. 11. 27. 23:17
#pragma commnet(lib, "추가할 lib")

ex)
#pragma comment( lib, "comctl32.lib")


설정

트랙백

댓글

영상처리 winapi 소스

프로그래밍 언어/WinApi 2014. 10. 10. 10:20


--------------------------

기상 방송 같은 효과

--------------------------


------- 기상 방송을 보면 아나운서가 파란 뒷 배경에서 설명하는데

방송에서는 아나운서 뒤에 기상 그림이 나온다.

이런 영상 효과를 내는 예제 소스이다. 웹캠이 연결되어 있어야하고

같은 디렉토리 내에 image.bmp 파일이 있어야한다.


#include <windows.h>

#include "vfw.h"

#pragma comment(lib, "vfw32.lib")


#define BITMAP_MAXSIZE (1024*768*3+10)


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

HWND HWndMain;

LPCTSTR lpszClass=TEXT("WiseCat");


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst = hInstance;


WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);


hWnd=CreateWindow(  lpszClass

,lpszClass

,WS_OVERLAPPEDWINDOW

,CW_USEDEFAULT

,CW_USEDEFAULT

,CW_USEDEFAULT,CW_USEDEFAULT

,NULL

,(HMENU)NULL

,hInstance

,NULL);        

ShowWindow(hWnd,nCmdShow);


while(GetMessage(&Message,NULL,0,0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}


return (int)Message.wParam;

}


LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR);


HWND vfw;

BITMAPINFO Bm;

HDC hdc;

BITMAPFILEHEADER * stpBFH;

BITMAPINFOHEADER * stpBIH;

unsigned char * BMbuf;

unsigned int uiPad;


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HANDLE hFile;

PAINTSTRUCT ps;

static unsigned int uiX;

static unsigned int uiY;

DWORD dwRead;


switch(iMessage)

{

case WM_CREATE:

HWndMain = hWnd;


// Bitmap 열고 처리 //시작---------------------------------------------

hFile = CreateFile(TEXT("image.bmp"),

GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if(INVALID_HANDLE_VALUE != hFile)

{

BMbuf = (unsigned char *)malloc(BITMAP_MAXSIZE);

ReadFile(hFile, BMbuf, BITMAP_MAXSIZE, &dwRead, NULL);

CloseHandle(hFile);


stpBFH = (BITMAPFILEHEADER *)BMbuf;

stpBIH = (BITMAPINFOHEADER *)(BMbuf + sizeof(BITMAPFILEHEADER));


uiX = (unsigned int)(stpBIH->biWidth);

uiPad = uiX%4;

uiY = (unsigned int)(stpBIH->biHeight);

}

// Bitmap 열고 처리 //끝-----------------------------------------------


// 웹캠 설정 // 시작------------------------

vfw = capCreateCaptureWindow(  TEXT("CAM")

,WS_CHILD | WS_VISIBLE

,0

,0

,400

,300

,hWnd

,NULL);


capDriverConnect(vfw,0);

capGetVideoFormat(vfw,&Bm,sizeof(Bm));

Bm.bmiHeader.biWidth  = 320;

Bm.bmiHeader.biHeight = 240;

capSetVideoFormat(vfw,&Bm,sizeof(Bm));

//capDlgVideoFormat(m_capwnd); 

capSetCallbackOnFrame(vfw, FramInfo);

capPreviewRate(vfw, 1);

capPreview(vfw, FALSE);

// 웹캠 설정 // 끝--------------------------

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd,&ps);

EndPaint(hWnd,&ps);

return 0;


case WM_DESTROY:

free(BMbuf);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}


LRESULT CALLBACK FramInfo(HWND hWnd, LPVIDEOHDR lpData)

{

static int iCntX;

static int iCntY;

static int Jump;

unsigned char * ucpPixel;


// 원본 영상 그리기 // 시작-------------------------------

hdc = GetDC(HWndMain);

StretchDIBits(hdc  , 0

, 0

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight

, 0

, 0

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight

, lpData->lpData

, &Bm

, DIB_RGB_COLORS

, SRCCOPY);

// 원본 영상 그리기 // 끝---------------------------------


// 영상 정보 편집 // 시작--------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits - 3;

Jump= 0;

for(iCntY = 0; iCntY < Bm.bmiHeader.biHeight ; ++iCntY)  

{        

for(iCntX = 0; iCntX  < Bm.bmiHeader.biWidth  ; ++iCntX, Jump += 3)

{

ucpPixel = ucpPixel + 3;

if(lpData->lpData[Jump + 2] > 100)

{

continue;

}

else if(lpData->lpData[Jump + 1] > 100)

{

continue;

}

else if(lpData->lpData[Jump + 0] > 50)

{

lpData->lpData[Jump] = *(ucpPixel + 0);  // Blue

lpData->lpData[Jump + 2] = *(ucpPixel + 2);  // Red

lpData->lpData[Jump + 1] = *(ucpPixel + 1);  // Green

}

}

ucpPixel = ucpPixel + uiPad;

}

// 영상 정보 편집 // 끝----------------------------------------------


// 편집된 영상 그리기 // 시작----------------------------

StretchDIBits(hdc  , Bm.bmiHeader.biWidth +20

, 0

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight

, 0

, 0

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight

, lpData->lpData

, &Bm

, DIB_RGB_COLORS

, SRCCOPY);

// 편집된 영상 그리기 // 끝------------------------------


ReleaseDC(HWndMain, hdc);


return 0;

}

/*

#include <windows.h>

#include <Vfw.h>

#pragma comment (lib,"vfw32.lib")


#define BITMAP_MAXSIZE (1024*768*3+10)


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

HWND hWndMain;

LPCTSTR lpszClass=TEXT("Class");

//WinMain 시작


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance

,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;

//1. 윈도우 속성값 등록

WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);  //주소에 Write


//2. 윈도우 생성

hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

NULL, (HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow); //화면에 뿌려줌


//3. 메시지 처리(무한 반복)

while (GetMessage(&Message,NULL,0,0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr);

HBITMAP hBit;

BITMAPINFO Bm;

BITMAPFILEHEADER * stpBFH;

BITMAPINFOHEADER * stpBIH;

unsigned char * BMbuf;

unsigned int uiPad;


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

HWND hVFW;

PAINTSTRUCT ps;

HANDLE hFile;

static unsigned int uiX;

static unsigned int uiY;

DWORD dwRead;


switch(iMessage)

{

case WM_CREATE:

hWndMain=hWnd;

hdc=GetDC(hWndMain);


// Bitmap 열기 //-------------------------------------------------------

hFile = CreateFile(TEXT("image.bmp"),

GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if(INVALID_HANDLE_VALUE != hFile)

{

BMbuf = (unsigned char *)malloc(BITMAP_MAXSIZE);

ReadFile(hFile, BMbuf, BITMAP_MAXSIZE, &dwRead, NULL);

CloseHandle(hFile);


stpBFH = (BITMAPFILEHEADER *)BMbuf;

stpBIH = (BITMAPINFOHEADER *)(BMbuf + sizeof(BITMAPFILEHEADER));


uiX = (unsigned int)(stpBIH->biWidth);

uiPad = uiX%4;

uiY = (unsigned int)(stpBIH->biHeight);

}

// Bitmap 그리기 //끝-----------------------------------------------


hVFW=capCreateCaptureWindow(TEXT("VFW"),WS_CHILD | WS_VISIBLE , 0,0,320,240,hWnd,0);

capDriverConnect(hVFW,0);

capPreviewRate(hVFW,10);

capGetVideoFormat(hVFW,&Bm, sizeof(Bm));

Bm.bmiHeader.biWidth = 320;

Bm.bmiHeader.biHeight = 240;

capSetVideoFormat(hVFW, &Bm, sizeof(Bm));

capPreview(hVFW,TRUE);


hBit = CreateCompatibleBitmap(hdc, Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight);


if (capSetCallbackOnFrame(hVFW,FramInfo) == FALSE)

{

return FALSE;

}

ReleaseDC(hWndMain, hdc);

return 0;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

EndPaint(hWnd, &ps);

return 0;

case WM_DESTROY:

free(BMbuf);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}

LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr)

{

HDC hdc;

HDC hMemDC;

HBITMAP OldBitmap;

int iCntX;

int iCntY;

int Jump;

int avr;

unsigned char * ucpPixel;


hdc = GetDC(hWndMain);

hMemDC = CreateCompatibleDC(hdc);

OldBitmap = (HBITMAP)SelectObject(hMemDC, hBit);


ucpPixel = BMbuf + stpBFH->bfOffBits;

Jump=0;

for (iCntY=0 ; iCntY < Bm.bmiHeader.biHeight ; ++iCntY)

{

//for (iCntX=0 ; iCntX < Bm.bmiHeader.biWidth ; ++iCntX)

for (iCntX=Bm.bmiHeader.biWidth-1 ; 0 <= iCntX ; --iCntX)

{


// 특정 색 전환

if(VideoHdr->lpData[Jump+2] > 150)// && VideoHdr->lpData[Jump+2] > 40)

{

if(VideoHdr->lpData[Jump+1] > 150)// && VideoHdr->lpData[Jump+1] > 70)

{

if(VideoHdr->lpData[Jump+0] > 150)// && VideoHdr->lpData[Jump+0] > 70)

{

VideoHdr->lpData[Jump+2] = *(ucpPixel+2);

VideoHdr->lpData[Jump+1] = *(ucpPixel+1);

VideoHdr->lpData[Jump+0] = *(ucpPixel+0);

}

}

}


//원영상

SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1,

RGB(VideoHdr->lpData[Jump+2], VideoHdr->lpData[Jump+1], VideoHdr->lpData[Jump]));

//   SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1, RGB(0xC3&(VideoHdr->lpData[Jump+2]), 0xC3&(VideoHdr->lpData[Jump+1]), 0xC3&(VideoHdr->lpData[Jump])));

// avr = (VideoHdr->lpData[Jump+2]+VideoHdr->lpData[Jump+1]+VideoHdr->lpData[Jump])/3;

// if(avr < 200)

// avr = 0;

// SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1, RGB(avr, avr, avr));


Jump+=3;

ucpPixel = ucpPixel + 3;

}

ucpPixel = ucpPixel + uiPad;

}


//------------------------------------------------------------------------------

BitBlt(hdc, 320,0,Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight,hMemDC,0,0,SRCCOPY);

SelectObject(hMemDC,OldBitmap);

ReleaseDC(hWndMain,hdc);

return 0;

}

*/


결과





왼쪽이 원본, 오른쪽이 특정 색은 다른 영상 이미지로 대체












-------------------------

웹캠 영상 처리

-------------------------


------- 웹캠으로 촬영된 영상을 스트레칭, 평활화등 영상 처리한 소스이다.



#include <windows.h>

#include <Vfw.h>

#pragma comment (lib,"vfw32.lib")


#define BITMAP_MAXSIZE (1024*768*3+10)


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

HWND hWndMain;

LPCTSTR lpszClass=TEXT("Class");

//WinMain 시작


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance

,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;

//1. 윈도우 속성값 등록

WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);  //주소에 Write


//2. 윈도우 생성

hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

NULL, (HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow); //화면에 뿌려줌


//3. 메시지 처리(무한 반복)

while (GetMessage(&Message,NULL,0,0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr);

HBITMAP hBit;

BITMAPINFO Bm;

unsigned char * ucpPixel;

BITMAPFILEHEADER * stpBFH;

BITMAPINFOHEADER * stpBIH;

unsigned char * BMbuf;

unsigned int uiPad;


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

HWND hVFW;

PAINTSTRUCT ps;

HANDLE hFile;

static unsigned int uiX;

static unsigned int uiY;

unsigned int uiXcount;

unsigned int uiYcount;

DWORD dwRead;

HBITMAP OldBitmap;


switch(iMessage)

{

case WM_CREATE:

hWndMain=hWnd;

hdc=GetDC(hWndMain);


// Bitmap 열고 설정 //시작-----------------------------------------------------

hFile = CreateFile(TEXT("image.bmp"),

GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if(INVALID_HANDLE_VALUE != hFile)

{

BMbuf = (unsigned char *)malloc(BITMAP_MAXSIZE);

ReadFile(hFile, BMbuf, BITMAP_MAXSIZE, &dwRead, NULL);

CloseHandle(hFile);


stpBFH = (BITMAPFILEHEADER *)BMbuf;

stpBIH = (BITMAPINFOHEADER *)(BMbuf + sizeof(BITMAPFILEHEADER));


uiX = (unsigned int)(stpBIH->biWidth);

uiPad = uiX%4;

uiY = (unsigned int)(stpBIH->biHeight);

}

// Bitmap 열고 설정 //끝-------------------------------------------------------


    // 웹캠 설정 // 시작----------------------------------------------------------------------

hVFW=capCreateCaptureWindow(TEXT("VFW"),WS_CHILD | WS_VISIBLE , 0,0,320,240,hWnd,0);

capDriverConnect(hVFW,0);

capPreviewRate(hVFW,10);

capGetVideoFormat(hVFW,&Bm, sizeof(Bm));

Bm.bmiHeader.biWidth = 320;

Bm.bmiHeader.biHeight = 240;

capSetVideoFormat(hVFW, &Bm, sizeof(Bm));

capPreview(hVFW,TRUE);


hBit = CreateCompatibleBitmap(hdc, Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight);


if (capSetCallbackOnFrame(hVFW,FramInfo) == FALSE)

{

return FALSE;

}

    // 웹캠 설정 // 끝------------------------------------------------------------------------


ReleaseDC(hWndMain, hdc);

return 0;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

EndPaint(hWnd, &ps);

return 0;

case WM_DESTROY:

free(BMbuf);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}

LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr)

{

HDC hdc;

HDC hMemDC;

HBITMAP OldBitmap;

int iCntX;

int iCntY;

int Jump;

int avr;

int Rhigh, Ghigh, Bhigh;

int Rlow, Glow, Blow;

unsigned int uiXcount;

unsigned int uiYcount;

unsigned int uiRcnt[256];

unsigned int uiGcnt[256];

unsigned int uiBcnt[256];

unsigned int uiRhisto[256];  // 평활화로 사용할 변수

unsigned int uiGhisto[256];  // 평활화로 사용할 변수

unsigned int uiBhisto[256];  // 평활화로 사용할 변수


hdc = GetDC(hWndMain);

hMemDC = CreateCompatibleDC(hdc);

OldBitmap = (HBITMAP)SelectObject(hMemDC, hBit);


// 원본 영상 그리기 // 시작------------------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits;

Jump=0;

for (iCntY=0 ; iCntY < Bm.bmiHeader.biHeight ; ++iCntY)

{

//for (iCntX=0 ; iCntX < Bm.bmiHeader.biWidth ; ++iCntX)    // 좌우 반전

for (iCntX=Bm.bmiHeader.biWidth-1 ; 0 <= iCntX ; --iCntX)

{

// 영상 정보 수집 // 시작-------------

++uiRcnt[VideoHdr->lpData[Jump+2]];

++uiGcnt[VideoHdr->lpData[Jump+1]];

++uiBcnt[VideoHdr->lpData[Jump+0]];

// 영상 정보 수집 // 끝---------------


// 영상 출력

SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1,

RGB(VideoHdr->lpData[Jump+2], VideoHdr->lpData[Jump+1], VideoHdr->lpData[Jump]));


// 영상 흑백으로 전환 // 시작----------------------------------------------------------------

// avr = (VideoHdr->lpData[Jump+2]+VideoHdr->lpData[Jump+1]+VideoHdr->lpData[Jump])/3;

// if(avr < 200)

// avr = 0;

// SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1, RGB(avr, avr, avr));

// 영상 흑백으로 전환 // 끝------------------------------------------------------------------


Jump+=3;    // 다음 픽셀

ucpPixel = ucpPixel + 3;    // 다음 픽셀

}

ucpPixel = ucpPixel + uiPad;    // 패드만큼 다음 가리킴

}

// 원본 영상 그리기 // 끝--------------------------------------------------------


// 영상 정보 처리 // 시작-----------------------------

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiRcnt[uiXcount] != 0)

{

Rlow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiRcnt[uiXcount] != 0)

{

Rhigh = uiXcount;

break;

}

}


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiGcnt[uiXcount] != 0)

{

Glow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiGcnt[uiXcount] != 0)

{

Ghigh = uiXcount;

break;

}

}


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiBcnt[uiXcount] != 0)

{

Blow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiBcnt[uiXcount] != 0)

{

Bhigh = uiXcount;

break;

}

}


// 누적

uiXcount = 0;

uiRhisto[uiXcount] = uiRcnt[uiXcount];

uiGhisto[uiXcount] = uiGcnt[uiXcount];

uiBhisto[uiXcount] = uiBcnt[uiXcount];

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

uiRhisto[uiXcount] = (uiRhisto[uiXcount-1] + uiRcnt[uiXcount]);

uiGhisto[uiXcount] = (uiGhisto[uiXcount-1] + uiGcnt[uiXcount]);

uiBhisto[uiXcount] = (uiBhisto[uiXcount-1] + uiBcnt[uiXcount]);

}


// 계산

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRhisto[uiXcount] = (uiRhisto[uiXcount] * 255) / (Bm.bmiHeader.biHeight*Bm.bmiHeader.biWidth);

uiGhisto[uiXcount] = (uiGhisto[uiXcount] * 255) / (Bm.bmiHeader.biHeight*Bm.bmiHeader.biWidth);

uiBhisto[uiXcount] = (uiBhisto[uiXcount] * 255) / (Bm.bmiHeader.biHeight*Bm.bmiHeader.biWidth);

}

// 영상 정보 처리 // 끝-------------------------------


  // 편집된 영상 이미지 정보 수정 // 시작-------------------------------

Jump=0;

for (iCntY=0 ; iCntY < Bm.bmiHeader.biHeight ; ++iCntY)

{

for (iCntX=0 ; iCntX < Bm.bmiHeader.biWidth ; ++iCntX)

{

/*

//평활화 + 스트레칭

VideoHdr->lpData[Jump+2] = uiRhisto[(((VideoHdr->lpData[Jump+2])-Rlow) * 255) / (Rhigh - Rlow)];

VideoHdr->lpData[Jump+1] = uiGhisto[(((VideoHdr->lpData[Jump+1])-Glow) * 255) / (Ghigh - Glow)];

VideoHdr->lpData[Jump] = uiBhisto[(((VideoHdr->lpData[Jump+1])-Blow) * 255) / (Bhigh - Blow)];

*/


//스트레칭

VideoHdr->lpData[Jump+2] = (((VideoHdr->lpData[Jump+2])-Rlow) * 255) / (Rhigh - Rlow);

VideoHdr->lpData[Jump+1] = (((VideoHdr->lpData[Jump+1])-Glow) * 255) / (Ghigh - Glow);

VideoHdr->lpData[Jump] = (((VideoHdr->lpData[Jump+1])-Blow) * 255) / (Bhigh - Blow);


/*

//평활화

VideoHdr->lpData[Jump+2] = uiRhisto[VideoHdr->lpData[Jump+2]];

VideoHdr->lpData[Jump+1] = uiGhisto[VideoHdr->lpData[Jump+1]];

VideoHdr->lpData[Jump] = uiBhisto[VideoHdr->lpData[Jump+1]];

*/

//원영상

//   SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1,

// RGB(VideoHdr->lpData[Jump+2], VideoHdr->lpData[Jump+1], VideoHdr->lpData[Jump]));

Jump+=3;

}

}

// 편집된 영상 이미지 정보 수정 // 시작-------------------------------


BitBlt(hdc, 320,0,Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight,hMemDC,0,0,SRCCOPY);

SelectObject(hMemDC,OldBitmap);

ReleaseDC(hWndMain,hdc);

// Sleep(100);

return 0;

}



/*

// 선생님 소스

#include <windows.h>

#include "vfw.h"

#pragma comment(lib, "vfw32.lib")


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK FramInfo(HWND, LPVIDEOHDR);


HINSTANCE g_hInst;

HWND hWndMain;

HWND hVFW;

HWND Hwndmain;

HBITMAP hBit;

BITMAPINFO Bm;

LPCTSTR lpszClass=TEXT("VFW");



LRESULT CALLBACK WndProc( HWND hWnd

, UINT iMessage

, WPARAM wParam

, LPARAM lParam)

{

HDC Hdc;


switch(iMessage)

{

case WM_CREATE:

Hwndmain = hWnd;

Hdc  = GetDC(hWnd);

hVFW = capCreateCaptureWindow(

TEXT("VFW"),

WS_CHILD | WS_VISIBLE,

0,

0,

1,

1,

hWnd,

0);

capDriverConnect(hVFW, 0);

capPreviewRate(hVFW, 1);

capPreview(hVFW, TRUE);

capGetVideoFormat(hVFW, &Bm, sizeof(Bm));

hBit = CreateCompatibleBitmap( Hdc

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight);

if (capSetCallbackOnFrame(hVFW, FramInfo) == FALSE)

{

return FALSE;

}

ReleaseDC(hWnd, Hdc);

return 0;


case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return (DefWindowProc(hWnd, iMessage, wParam, lParam));

}


LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr)

{

HDC Hdc;

HDC hMemDC;

HBITMAP OldBitmap;


Hdc = GetDC(Hwndmain);

hMemDC = CreateCompatibleDC(Hdc);

OldBitmap = (HBITMAP)SelectObject(hMemDC, hBit);


BitBlt( Hdc

, 0

, 0

, Bm.bmiHeader.biWidth

, Bm.bmiHeader.biHeight

, hMemDC

, 0

, 0

, SRCCOPY);

SelectObject(hMemDC,OldBitmap);

DeleteDC(hMemDC);

ReleaseDC(Hwndmain, Hdc);


return 0;

}

*/


결과




왼쪽이 원본 영상, 오른쪽이 평활화 & 스트레칭 처리된 영상이다.

원본의 왼쪽에 어두워 잘 안보이는데,

처리된 영상을 보면 어두운 부분이 보이게 처리된 것을 확인할 수 있다.











----------------

이미지 평활화

----------------


------- 비트맵 이미지를 평활화 처리하는 소스


#include <windows.h>


#define BITMAP_MAXSIZE (1024*768*3+10)

#define XOFFSET 280

#define X_WIDTH_SIZE 100

#define ALPA

#define SCREEN_Y_SIZE 270*2

#define SCROLL_SIZE 100*6


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass=TEXT("First");


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

 ,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;

WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);


hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW | WS_VSCROLL,

100,40,800,660,

NULL,(HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow);

while (GetMessage(&Message,NULL,0,0)) {

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


unsigned char * BMbuf;

unsigned int uiRcnt[256];

unsigned int uiGcnt[256];

unsigned int uiBcnt[256];

unsigned int uiRcnt2[256];

unsigned int uiGcnt2[256];

unsigned int uiBcnt2[256];

unsigned int uiRhisto[256];  // 평활화로 사용할 변수

unsigned int uiGhisto[256];  // 평활화로 사용할 변수

unsigned int uiBhisto[256];  // 평활화로 사용할 변수

HBITMAP Screen;

HBITMAP Histo;


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc;

static HDC MemDC;

PAINTSTRUCT ps;

HANDLE hFile;

DWORD dwRead;

TCHAR str[1024];

static BITMAPFILEHEADER * stpBFH;

static BITMAPINFOHEADER * stpBIH;

static unsigned int uiX;

static unsigned int uiY;

unsigned int uiXcount;

unsigned int uiYcount;

unsigned char * ucpPixel;

static unsigned int uiPad;

HBITMAP OldBitmap;

static int yPos;

int yInc;

int iBorW;

HPEN OldPen, MyPen;

switch (iMessage)

{

case WM_CREATE:

SetScrollRange(hWnd, SB_VERT, 0, SCROLL_SIZE, TRUE);

SetScrollPos(hWnd, SB_VERT, 0, TRUE);

hFile = CreateFile(TEXT("image4.bmp"),

GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


BMbuf = (unsigned char *)malloc(BITMAP_MAXSIZE);

if(0 == BMbuf)

{

MessageBox(hWnd, TEXT("동적할당을 받을 수 없습니다."), TEXT("오류"), MB_OK);

DestroyWindow(hWnd);

}

if(INVALID_HANDLE_VALUE != hFile)

{

ReadFile(hFile, BMbuf, BITMAP_MAXSIZE, &dwRead, NULL);

CloseHandle(hFile);


stpBFH = (BITMAPFILEHEADER *)BMbuf;

stpBIH = (BITMAPINFOHEADER *)(BMbuf + sizeof(BITMAPFILEHEADER));


// 정보 출력 //시작------------------------------------------------------

wsprintf(str, TEXT("[%c][%c]"), BMbuf[0], BMbuf[1]);

CreateWindow(TEXT("static"), TEXT("파일 타입 : "), WS_CHILD | WS_VISIBLE,

0, 0, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 0, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d byte]"), stpBFH->bfSize);

CreateWindow(TEXT("static"), TEXT("파일 크기 : "), WS_CHILD | WS_VISIBLE,

0, 20, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 20, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%u]"), stpBIH->biWidth);

CreateWindow(TEXT("static"), TEXT("가로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 40, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 40, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%u]"), stpBIH->biHeight);

CreateWindow(TEXT("static"), TEXT("세로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 60, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 60, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%08X]"), stpBFH->bfOffBits);

CreateWindow(TEXT("static"), TEXT("bfOffbits : "), WS_CHILD | WS_VISIBLE,

0, 80, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 80, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d bytes]"), stpBIH->biSize);

CreateWindow(TEXT("static"), TEXT("헤더 크기 : "), WS_CHILD | WS_VISIBLE,

0, 100, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 100, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d bytes]"), stpBIH->biSizeImage);

CreateWindow(TEXT("static"), TEXT("그림 크기 : "), WS_CHILD | WS_VISIBLE,

0, 120, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 120, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d pixel]"), stpBIH->biXPelsPerMeter);

CreateWindow(TEXT("static"), TEXT("이미지 가로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 140, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 140, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d pixel]"), stpBIH->biYPelsPerMeter);

CreateWindow(TEXT("static"), TEXT("이미지 세로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 160, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 160, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);

// 정보 출력 //끝-----------------------------------------------------------

uiX = (unsigned int)(stpBIH->biWidth);

uiPad = uiX%4;

uiY = (unsigned int)(stpBIH->biHeight);


hdc = GetDC(hWnd);

MemDC = CreateCompatibleDC(hdc);


Screen = CreateCompatibleBitmap(hdc, uiX, uiY * 2);

OldBitmap = (HBITMAP)SelectObject(MemDC, Screen);

// Bitmap 그리기 //시작---------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits;

for(uiYcount = uiY; 0 < uiYcount; --uiYcount)

{

for(uiXcount = uiX; 0 < uiXcount; --uiXcount)

{

// 흑백 처리 // 시작--------------------------------------

iBorW = ((*(ucpPixel+2)) + (*(ucpPixel+1)) + (*ucpPixel))/3;

*(ucpPixel+2)= iBorW;

*(ucpPixel+1)= iBorW;

*(ucpPixel+0)= iBorW;

// 흑백 처리 // 끝----------------------------------------

++uiRcnt[*(ucpPixel+2)];

++uiGcnt[*(ucpPixel+1)];

++uiBcnt[*ucpPixel];


SetPixel(MemDC, uiXcount-1, uiYcount-1,

RGB(*(ucpPixel+2), *(ucpPixel+1), *ucpPixel));

ucpPixel = ucpPixel + 3;

}

ucpPixel = ucpPixel + uiPad;

}



// Bitmap 그리기 //끝-----------------------------------------------


uiYcount = 0;

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiYcount < uiRcnt[uiXcount])

{

uiYcount = uiRcnt[uiXcount];

}

if(uiYcount < uiGcnt[uiXcount])

{

uiYcount = uiGcnt[uiXcount];

}

if(uiYcount < uiBcnt[uiXcount])

{

uiYcount = uiBcnt[uiXcount];

}

}


// 히스토그램 세로축 값 변환 //시작----------------------------------

// 평활화 누적값 // 시작--------------------------------------------


// 누적

uiXcount = 0;

uiRhisto[uiXcount] = uiRcnt[uiXcount];

uiGhisto[uiXcount] = uiGcnt[uiXcount];

uiBhisto[uiXcount] = uiBcnt[uiXcount];

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

uiRhisto[uiXcount] = (uiRhisto[uiXcount-1] + uiRcnt[uiXcount]);

uiGhisto[uiXcount] = (uiGhisto[uiXcount-1] + uiGcnt[uiXcount]);

uiBhisto[uiXcount] = (uiBhisto[uiXcount-1] + uiBcnt[uiXcount]);

}

// 계산

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRhisto[uiXcount] = (uiRhisto[uiXcount] * 255) / (uiX*uiY);

uiGhisto[uiXcount] = (uiGhisto[uiXcount] * 255) / (uiX*uiY);

uiBhisto[uiXcount] = (uiBhisto[uiXcount] * 255) / (uiX*uiY);

}


//n[i]=sum[i]*(1/16)*7 


// 평활화 누적값 // 끝----------------------------------------------


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRcnt[uiXcount] = (uiRcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiGcnt[uiXcount] = (uiGcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiBcnt[uiXcount] = (uiBcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

}

// 히스토그램 세로축 값 변환 //끝------------------------------------


// 편집된 Bitmap 그리기 //시작---------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits;

for(uiYcount = uiY*2; uiY < uiYcount; --uiYcount)

{

for(uiXcount = uiX; 0 < uiXcount; --uiXcount)

{

++uiRcnt2[uiRhisto[*(ucpPixel+2)]];

++uiGcnt2[uiGhisto[*(ucpPixel+1)]];

++uiBcnt2[uiBhisto[*ucpPixel]];


SetPixel(MemDC, uiXcount-1, uiYcount-1,

RGB(uiRhisto[*(ucpPixel+2)], uiGhisto[*(ucpPixel+1)], uiBhisto[*ucpPixel]));

ucpPixel = ucpPixel + 3;

}

ucpPixel = ucpPixel + uiPad;

}

// 편집된 Bitmap 그리기 //끝-----------------------------------------------


uiYcount = 0;

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiYcount < uiRcnt2[uiXcount])

{

uiYcount = uiRcnt2[uiXcount];

}

if(uiYcount < uiGcnt2[uiXcount])

{

uiYcount = uiGcnt2[uiXcount];

}

if(uiYcount < uiBcnt2[uiXcount])

{

uiYcount = uiBcnt2[uiXcount];

}

}


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRcnt2[uiXcount] = (uiRcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiGcnt2[uiXcount] = (uiGcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiBcnt2[uiXcount] = (uiBcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

}


//히스토그램 그리기 //시작-------------------------------------------

Histo = CreateCompatibleBitmap(hdc, 260, SCREEN_Y_SIZE);

SelectObject(MemDC, Histo);

/*

SetBkColor(MemDC, RGB(100,100,100));

aaa = GetBkColor(MemDC);

wsprintf(str, TEXT("[%08X]"), aaa);

MessageBox(hWnd, str, TEXT("hi"), MB_OK);

*/

// SCREEN 바탕 흰색으로 그리기// 시작--------------------------------

for(uiYcount = 0; SCREEN_Y_SIZE > uiYcount; ++uiYcount)

{

for(uiXcount = 0; 260 > uiXcount; ++uiXcount)

{

SetPixel(MemDC, uiXcount, uiYcount, RGB(255, 255, 255));

}

}

// SCREEN 바탕 흰색으로 그리기// 끝----------------------------------


MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

OldPen = (HPEN)SelectObject(MemDC, MyPen);

MoveToEx(MemDC, 0, 0, NULL);

LineTo(MemDC, 0, 256);

LineTo(MemDC, 259, 256);


MoveToEx(MemDC, 0, 270, NULL);

LineTo(MemDC, 0, 270+256);

LineTo(MemDC, 259, 270+256);

// 빨강색 그래프

MoveToEx(MemDC, 3, 256-uiRcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiRcnt[uiXcount]);

}


// 초록색 그래프

MoveToEx(MemDC, 3, 256-uiGcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiGcnt[uiXcount]);

}


// 파랑색 그래프

MoveToEx(MemDC, 3, 256-uiBcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiBcnt[uiXcount]);

}




MoveToEx(MemDC, 3, 270+256-uiRcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiRcnt2[uiXcount]);

}


MoveToEx(MemDC, 3, 270+256-uiGcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiGcnt2[uiXcount]);

}


MoveToEx(MemDC, 3, 270+256-uiBcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiBcnt2[uiXcount]);

}

//히스토그램 그리기 //끝---------------------------------------------


SelectObject(hdc, OldPen);

SelectObject(MemDC, OldBitmap);

DeleteDC(MemDC);

DeleteObject(MyPen);

ReleaseDC(hWnd, hdc);

free(BMbuf);

}

else

{

MessageBox(hWnd, TEXT("파일을 열 수 없습니다."), TEXT("오류"), MB_OK);

DestroyWindow(hWnd);

}

return 0;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

MemDC = CreateCompatibleDC(hdc);

OldBitmap = (HBITMAP)SelectObject(MemDC, Screen);


BitBlt(hdc, XOFFSET, -yPos, uiX, uiY*2, MemDC, 0, 0, SRCCOPY);


(HBITMAP)SelectObject(MemDC, Histo);


BitBlt(hdc, 5, 180-yPos, 260, SCREEN_Y_SIZE, MemDC, 0, 0, SRCCOPY);


SelectObject(MemDC, OldBitmap);

DeleteDC(MemDC);

EndPaint(hWnd, &ps);

return 0;

case WM_VSCROLL:

yInc = 0;

switch(LOWORD(wParam))

{

case SB_LINEUP:

yInc = -40;

break;

case SB_LINEDOWN:

yInc = 40;

break;

case SB_PAGEUP:

yInc = -200;

break;

case SB_PAGEDOWN:

yInc = 200;

break;

case SB_THUMBTRACK:

yInc = HIWORD(wParam)-yPos;

break;

}

if(yPos+yInc < 0)

yInc = -yPos;

if(yPos+yInc > SCROLL_SIZE)

yInc = SCROLL_SIZE - yPos;


yPos = yPos + yInc;

ScrollWindow(hWnd, 0, -yInc, NULL, NULL);

SetScrollPos(hWnd, SB_VERT, yPos, TRUE);

return 0;

case WM_KEYDOWN:

yInc = 0;

switch(wParam)

{

case VK_UP:

yInc = -40;

break;

case VK_DOWN:

yInc = 40;

break;

}

if(yPos+yInc < 0)

yInc = -yPos;

if(yPos+yInc > SCROLL_SIZE)

yInc = SCROLL_SIZE - yPos;


yPos = yPos + yInc;

ScrollWindow(hWnd, 0, -yInc, NULL, NULL);

SetScrollPos(hWnd, SB_VERT, yPos, TRUE);

return 0;

case WM_DESTROY:

DeleteObject(Screen);

DeleteObject(Histo);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}




결과


흑백이미지일 경우,

위에 그림이 원본, 밑에 그림이 평활화된 이미지.





색깔이미지일 경우,

위에 그림이 원본, 밑에 그림이 평활화된 이미지.












--------------------

이미지 스트레칭

--------------------


------- 비트맵 이미지를 스트레칭 처리하는 소스



#include <windows.h>


#define BITMAP_MAXSIZE (1024*768*3+10)

#define XOFFSET 280

#define X_WIDTH_SIZE 100

#define ALPA

#define SCREEN_Y_SIZE 270*2

#define SCROLL_SIZE 100*6


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass=TEXT("First");


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

 ,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;

WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);


hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW | WS_VSCROLL,

100,40,800,660,

NULL,(HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow);

while (GetMessage(&Message,NULL,0,0)) {

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


unsigned char * BMbuf;

unsigned int uiRcnt[256];

unsigned int uiGcnt[256];

unsigned int uiBcnt[256];

unsigned int uiRcnt2[256];

unsigned int uiGcnt2[256];

unsigned int uiBcnt2[256];

unsigned int uiRStretch[256];  // 스트레칭으로 사용할 변수

unsigned int uiGStretch[256];  // 스트레칭으로 사용할 변수

unsigned int uiBStretch[256];  // 스트레칭으로 사용할 변수

HBITMAP Screen;

HBITMAP Stretch;


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

HDC hdc;

static HDC MemDC;

PAINTSTRUCT ps;

HANDLE hFile;

DWORD dwRead;

TCHAR str[1024];

static BITMAPFILEHEADER * stpBFH;

static BITMAPINFOHEADER * stpBIH;

static unsigned int uiX;

static unsigned int uiY;

unsigned int uiXcount;

unsigned int uiYcount;

unsigned char * ucpPixel;

static unsigned int uiPad;

HBITMAP OldBitmap;

static int yPos;

int yInc;

int iBorW;

HPEN OldPen, MyPen;

int Rhigh, Ghigh, Bhigh;

int Rlow, Glow, Blow;

switch (iMessage)

{

case WM_CREATE:

SetScrollRange(hWnd, SB_VERT, 0, SCROLL_SIZE, TRUE);

SetScrollPos(hWnd, SB_VERT, 0, TRUE);

hFile = CreateFile(TEXT("image6.bmp"),

GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


BMbuf = (unsigned char *)malloc(BITMAP_MAXSIZE);

if(0 == BMbuf)

{

MessageBox(hWnd, TEXT("동적할당을 받을 수 없습니다."), TEXT("오류"), MB_OK);

DestroyWindow(hWnd);

}

if(INVALID_HANDLE_VALUE != hFile)

{

ReadFile(hFile, BMbuf, BITMAP_MAXSIZE, &dwRead, NULL);

CloseHandle(hFile);


stpBFH = (BITMAPFILEHEADER *)BMbuf;

stpBIH = (BITMAPINFOHEADER *)(BMbuf + sizeof(BITMAPFILEHEADER));


// 정보 출력 //시작------------------------------------------------------

wsprintf(str, TEXT("[%c][%c]"), BMbuf[0], BMbuf[1]);

CreateWindow(TEXT("static"), TEXT("파일 타입 : "), WS_CHILD | WS_VISIBLE,

0, 0, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 0, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d byte]"), stpBFH->bfSize);

CreateWindow(TEXT("static"), TEXT("파일 크기 : "), WS_CHILD | WS_VISIBLE,

0, 20, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 20, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%u]"), stpBIH->biWidth);

CreateWindow(TEXT("static"), TEXT("가로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 40, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 40, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%u]"), stpBIH->biHeight);

CreateWindow(TEXT("static"), TEXT("세로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 60, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 60, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%08X]"), stpBFH->bfOffBits);

CreateWindow(TEXT("static"), TEXT("bfOffbits : "), WS_CHILD | WS_VISIBLE,

0, 80, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 80, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d bytes]"), stpBIH->biSize);

CreateWindow(TEXT("static"), TEXT("헤더 크기 : "), WS_CHILD | WS_VISIBLE,

0, 100, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 100, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d bytes]"), stpBIH->biSizeImage);

CreateWindow(TEXT("static"), TEXT("그림 크기 : "), WS_CHILD | WS_VISIBLE,

0, 120, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 120, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d pixel]"), stpBIH->biXPelsPerMeter);

CreateWindow(TEXT("static"), TEXT("이미지 가로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 140, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 140, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);


wsprintf(str, TEXT("[%d pixel]"), stpBIH->biYPelsPerMeter);

CreateWindow(TEXT("static"), TEXT("이미지 세로 크기 : "), WS_CHILD | WS_VISIBLE,

0, 160, 130, 20, hWnd, (HMENU)-1, g_hInst, NULL);

CreateWindow(TEXT("static"), str, WS_CHILD | WS_VISIBLE,

130, 160, X_WIDTH_SIZE, 20, hWnd, (HMENU)-1, g_hInst, NULL);

// 정보 출력 //끝-----------------------------------------------------------

uiX = (unsigned int)(stpBIH->biWidth);

uiPad = uiX%4;

uiY = (unsigned int)(stpBIH->biHeight);


hdc = GetDC(hWnd);

MemDC = CreateCompatibleDC(hdc);


Screen = CreateCompatibleBitmap(hdc, uiX, uiY * 2);

OldBitmap = (HBITMAP)SelectObject(MemDC, Screen);

// Bitmap 그리기 //시작---------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits;

for(uiYcount = uiY; 0 < uiYcount; --uiYcount)

{

for(uiXcount = 0; uiX > uiXcount; ++uiXcount)

{

/*iBorW = ((*(ucpPixel+2)) + (*(ucpPixel+1)) + (*ucpPixel))/3;

*(ucpPixel+2)= iBorW;

*(ucpPixel+1)= iBorW;

*(ucpPixel+0)= iBorW;*/

++uiRcnt[*(ucpPixel+2)];

++uiGcnt[*(ucpPixel+1)];

++uiBcnt[*ucpPixel];


SetPixel(MemDC, uiXcount, uiYcount-1,

RGB(*(ucpPixel+2), *(ucpPixel+1), *ucpPixel));

ucpPixel = ucpPixel + 3;

}

ucpPixel = ucpPixel + uiPad;

}



// Bitmap 그리기 //끝-----------------------------------------------


// 히스토그램 세로축 값 변환 //시작----------------------------------



for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiRcnt[uiXcount] != 0)

{

Rlow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiRcnt[uiXcount] != 0)

{

Rhigh = uiXcount;

break;

}

}

// wsprintf(str, TEXT("Rhigh : %d, Rlow : %d"), Rhigh, Rlow);

// MessageBox(hWnd, str, TEXT("test"), MB_OK);



for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiGcnt[uiXcount] != 0)

{

Glow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiGcnt[uiXcount] != 0)

{

Ghigh = uiXcount;

break;

}

}

// wsprintf(str, TEXT("Ghigh : %d, Glow : %d"), Ghigh, Glow);

// MessageBox(hWnd, str, TEXT("test"), MB_OK);



for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiBcnt[uiXcount] != 0)

{

Blow = uiXcount;

break;

}

}

for(uiXcount = 255; 0 <= uiXcount; --uiXcount)

{

if(uiBcnt[uiXcount] != 0)

{

Bhigh = uiXcount;

break;

}

}

// wsprintf(str, TEXT("Bhigh : %d, Blow : %d"), Bhigh, Blow);

// MessageBox(hWnd, str, TEXT("test"), MB_OK);


/*

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRStretch[uiXcount] = ((uiRcnt[uiXcount]-Rlow) * 255) / (Rhigh - Rlow);

uiGStretch[uiXcount] = ((uiGcnt[uiXcount]-Glow) * 255) / (Ghigh - Glow);

uiBStretch[uiXcount] = ((uiBcnt[uiXcount]-Blow) * 255) / (Bhigh - Blow);

}*/



uiYcount = 0;

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiYcount < uiRcnt[uiXcount])

{

uiYcount = uiRcnt[uiXcount];

}

if(uiYcount < uiGcnt[uiXcount])

{

uiYcount = uiGcnt[uiXcount];

}

if(uiYcount < uiBcnt[uiXcount])

{

uiYcount = uiBcnt[uiXcount];

}

}


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRcnt[uiXcount] = (uiRcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiGcnt[uiXcount] = (uiGcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiBcnt[uiXcount] = (uiBcnt[uiXcount] * 255) / uiYcount;//(uiX*uiY);

}

// 히스토그램 세로축 값 변환 //끝------------------------------------


// Bitmap 그리기 //시작---------------------------------------------

ucpPixel = BMbuf + stpBFH->bfOffBits;

for(uiYcount = uiY*2; uiY < uiYcount; --uiYcount)

{

for(uiXcount = 0; uiX > uiXcount; ++uiXcount)

{

*(ucpPixel+2) = (((*(ucpPixel+2))-Rlow) * 255) / (Rhigh - Rlow);

*(ucpPixel+1) = (((*(ucpPixel+1))-Glow) * 255) / (Ghigh - Glow);

*(ucpPixel+0) = (((*(ucpPixel+0))-Blow) * 255) / (Bhigh - Blow);


++uiRcnt2[*(ucpPixel+2)];

++uiGcnt2[*(ucpPixel+1)];

++uiBcnt2[*ucpPixel];


SetPixel(MemDC, uiXcount, uiYcount-1,

RGB(*(ucpPixel+2), *(ucpPixel+1), *ucpPixel));

ucpPixel = ucpPixel + 3;

}

ucpPixel = ucpPixel + uiPad;

}

// Bitmap 그리기 //끝-----------------------------------------------


uiYcount = 0;

for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

if(uiYcount < uiRcnt2[uiXcount])

{

uiYcount = uiRcnt2[uiXcount];

}

if(uiYcount < uiGcnt2[uiXcount])

{

uiYcount = uiGcnt2[uiXcount];

}

if(uiYcount < uiBcnt2[uiXcount])

{

uiYcount = uiBcnt2[uiXcount];

}

}


for(uiXcount = 0; 256 > uiXcount; ++uiXcount)

{

uiRcnt2[uiXcount] = (uiRcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiGcnt2[uiXcount] = (uiGcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

uiBcnt2[uiXcount] = (uiBcnt2[uiXcount] * 255) / uiYcount;//(uiX*uiY);

}


//히스토그램 그리기 //시작-------------------------------------------

Stretch = CreateCompatibleBitmap(hdc, 260, SCREEN_Y_SIZE);

SelectObject(MemDC, Stretch);

/*

SetBkColor(MemDC, RGB(100,100,100));

aaa = GetBkColor(MemDC);

wsprintf(str, TEXT("[%08X]"), aaa);

MessageBox(hWnd, str, TEXT("hi"), MB_OK);

*/

// SCREEN 바탕 흰색으로 그리기// 시작--------------------------------

for(uiYcount = 0; SCREEN_Y_SIZE > uiYcount; ++uiYcount)

{

for(uiXcount = 0; 260 > uiXcount; ++uiXcount)

{

SetPixel(MemDC, uiXcount, uiYcount, RGB(255, 255, 255));

}

}

// SCREEN 바탕 흰색으로 그리기// 끝----------------------------------


MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

OldPen = (HPEN)SelectObject(MemDC, MyPen);

MoveToEx(MemDC, 0, 0, NULL);

LineTo(MemDC, 0, 256);

LineTo(MemDC, 259, 256);


MoveToEx(MemDC, 0, 270, NULL);

LineTo(MemDC, 0, 270+256);

LineTo(MemDC, 259, 270+256);

// 빨강색 그래프

MoveToEx(MemDC, 3, 256-uiRcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiRcnt[uiXcount]);

}


// 초록색 그래프

MoveToEx(MemDC, 3, 256-uiGcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiGcnt[uiXcount]);

}


// 파랑색 그래프

MoveToEx(MemDC, 3, 256-uiBcnt[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 256-uiBcnt[uiXcount]);

}



MoveToEx(MemDC, 3, 270+256-uiRcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiRcnt2[uiXcount]);

}


MoveToEx(MemDC, 3, 270+256-uiGcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiGcnt2[uiXcount]);

}


MoveToEx(MemDC, 3, 270+256-uiBcnt2[0], NULL);

for(uiXcount = 1; 256 > uiXcount; ++uiXcount)

{

MyPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

SelectObject(MemDC, MyPen);

LineTo(MemDC, uiXcount+3, 270+256-uiBcnt2[uiXcount]);

}

//히스토그램 그리기 //끝---------------------------------------------


SelectObject(hdc, OldPen);

SelectObject(MemDC, OldBitmap);

DeleteDC(MemDC);

DeleteObject(MyPen);

ReleaseDC(hWnd, hdc);

free(BMbuf);

}

else

{

MessageBox(hWnd, TEXT("파일을 열 수 없습니다."), TEXT("오류"), MB_OK);

DestroyWindow(hWnd);

}

return 0;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

MemDC = CreateCompatibleDC(hdc);

OldBitmap = (HBITMAP)SelectObject(MemDC, Screen);


BitBlt(hdc, XOFFSET, -yPos, uiX, uiY*2, MemDC, 0, 0, SRCCOPY);


(HBITMAP)SelectObject(MemDC, Stretch);


BitBlt(hdc, 5, 180-yPos, 260, SCREEN_Y_SIZE, MemDC, 0, 0, SRCCOPY);


SelectObject(MemDC, OldBitmap);

DeleteDC(MemDC);

EndPaint(hWnd, &ps);

return 0;

case WM_VSCROLL:

yInc = 0;

switch(LOWORD(wParam))

{

case SB_LINEUP:

yInc = -40;

break;

case SB_LINEDOWN:

yInc = 40;

break;

case SB_PAGEUP:

yInc = -200;

break;

case SB_PAGEDOWN:

yInc = 200;

break;

case SB_THUMBTRACK:

yInc = HIWORD(wParam)-yPos;

break;

}

if(yPos+yInc < 0)

yInc = -yPos;

if(yPos+yInc > SCROLL_SIZE)

yInc = SCROLL_SIZE - yPos;


yPos = yPos + yInc;

ScrollWindow(hWnd, 0, -yInc, NULL, NULL);

SetScrollPos(hWnd, SB_VERT, yPos, TRUE);

return 0;

case WM_KEYDOWN:

yInc = 0;

switch(wParam)

{

case VK_UP:

yInc = -40;

break;

case VK_DOWN:

yInc = 40;

break;

}

if(yPos+yInc < 0)

yInc = -yPos;

if(yPos+yInc > SCROLL_SIZE)

yInc = SCROLL_SIZE - yPos;


yPos = yPos + yInc;

ScrollWindow(hWnd, 0, -yInc, NULL, NULL);

SetScrollPos(hWnd, SB_VERT, yPos, TRUE);

return 0;

case WM_DESTROY:

DeleteObject(Screen);

DeleteObject(Stretch);

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}



결과



위에 이미지가 원본영상으로 전체적으로 어둡다.

밑에 이미지가 스트레칭 처리된 이미지로 밝아졌다.





전체적으로 밝기만 올렸을 경우,





설정

트랙백

댓글

[WinApi]VFW함수 자료정리

프로그래밍 언어/WinApi 2014. 9. 18. 12:08

비디오 캡쳐를 하기 위해서 먼저 vfw32.lib 를 프로젝트에서 포함 해주어야 하며 작성 소스 상단에 Vfw.h를

include 해주어야 한다.

 

비디오 캡쳐를 위한 작업 순서를 간단히 다음과 같다.

 

캡쳐 윈도우를 생성

윈도우와 캡쳐 드라이버를 연결

캡쳐된 비디오 프레임을 캡쳐 윈도우를 통해서 출력

캡쳐된 비디오 프레임을 캡쳐 윈도우에 보여주기 위해서는 한 프레임이 캡쳐 될 때마다 특정 함수를 호출하도록 한 다음에 호출된 함수에서 그 프레임을 화면에 출력한다.

이러한 작업을 수행하기 위해서는 VFW 라이브러리 중에서 다음과 같은 함수들을 사용해야 한다.

capGetDriverDescription()

CapCreateCaptureWindow()

capDriverConnect()

capPreviewRate()

capSetVideoFormat()

capDriverDisconnect()

capSetCallbackOnFrame()

 

 - 함수의 원형

 

HWND VFWAPI capCreateCaptureWindowW (
        LPCWSTR lpszWindowName,
        DWORD dwStyle,
        int x, int y, int nWidth, int nHeight,
        HWND hwndParent, int nID);

이 함수는 캡쳐 윈도우를 생성한다. name 에는 윈도우위 이름을 지정한다. style 윈도우위 스타일을 지정한다. (x,y)에는 캡쳐 윈도우의 좌측 상단의 좌표를 지정한다. width height에는 캡쳐 윈도우의 크기를 지정한다. hWnd에는 부모윈도우의 핸들값을 입력한다. id에는 윈도우의 식별 번호를 입력한다. 캡쳐 윈도우가 정상적으로 생성되면 캡쳐 윈도우의 핸들의 함수 결과값으로 반환되고 그렇지 않으면 NULL값이 반환된다.

 

 

BOOL VFWAPI capGetDriverDescriptionW (UINT wDriverIndex,
        LPWSTR lpszName, int cbName,
        LPWSTR lpszVer, int cbVer);

이 함수는 캡쳐 드라이버의 이름 및 버전 정보를 검색한다. 첫 번째 매개변수인 index는 검색하고자 하는 드라이버의 번호를 나타내는데, 0부터 9까지의 값을 가질 수 있다.

즉, 한 컴퓨터에서 9대의 캡쳐 장치가 사용될 수 있다고 가정하고 있다. 검색하고자 하는 번호의 드라이버가 존재하면 이 함수는 name에 드라이버의 이름을 저장하고 version에 드라이버 버전을 저장한 다음에 함수 결과값으로 TURE 값을 반환한다. 

 

capDriverConnect(hWnd, index);

이 함수는 아래 처럼 디파인 되어 있으며

#define capDriverConnect(hwnd, i)          ((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_CONNECT, (WPARAM)(i), 0L))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_DRIVER_CONNECT 를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 캡쳐 윈도우를 캡쳐 드라이버에 연결한다. hWnd는 캡쳐 윈도우의 핸들을 나타내고 index는 캡쳐 드라이버의 번호를 나타낸다. 이 함수는 캡쳐 장치가 정상적으로 작동하여 연결이 성공되면 TURE 값을 반환하고 그렇지 않으면 FALSE 값을 반환한다.

 

 

capPreviewRate(hWnd, rate);

이 함수는 아래 처럼 디파인 되어 있으며

#define capPreviewRate(hwnd, wMS)   ((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEWRATE, (WPARAM)(wMS), 0))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_PREVIEWRATE를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 미리보기 (preview)모드에서의 프레임 재생 속도를 설정한다. 여기에서 미리보기란 카메라에서 입력된 비디오를 파일에 저장하는 것이 아니라 화면에 보여준다는 것을 의미한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 rate는 밀리초(ms) 단 위의 시간으로 설정한다. 예를 들어, rate 값을 66으로 설정하면 0.066초마다 새로운 비디오 프레임을 캡쳐해서 디스플레이 하게 도니다. 이와 같은 속도로 재생을 하면 1초에 15개의 비디오 프레임이 디스플레이된다.

 

capSetVideoFormat(hWnd, videoFormat, videoFormat_size);

#define capSetVideoFormat(hwnd,s,wSize)          ((BOOL)AVICapSM(hwnd,WM_CAP_SET_VIDEOFORMAT, (WPARAM)(wSize), (LPARAM)(LPVOID)(s)))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_VIDEOFORMAT를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 캡쳐된 비디오 데이터 형식을 설정한다. 사용자가 원하는 비디오 데이터형식이 캡쳐 장치에서 지원이 되면 이 함수는 TRUE 값을 반환하고 그렇지 않으면 FALSE 값을 반환하므로 반드시 이 함수의 결과값이 TRUE 인지 검사한 다음에 다음단계로 넘어가야 한다.

hWnd는 캡쳐 윈도우의 핸들 값으로 설정한다. videoFormat은 설정하고자 하는 비디오 데이터 형식을 나타내는데, 비디오 데이터의 각 프레임에 대한 비트맵 형식을 BITMAPINFO 구조로 기술한다.

 

BITMAPINFO 구조는 다음과 같다.

typedef struct tagBITMAPINFO{

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColrs[1];

BITMAPINFO;  

BITMAPINFO는 BITMAPINFOHEADER와 RGBQUAD로 구성되는데, 여기에서는 다음과 같이 정의되는 BITMAPINFOHEADER 구조체의 값만 사용하면 된다.


pBmiInfo->bmiHeader.biSize = 40;  //BITMAPINFOHEADER 구조체의 크기

pBmiInfo->bmiHeader.biHeight = 480; //영상의 가로크기

pBmiInfo->bmiHeader.biWidth = 640;

//영상의 세로크기(양수:좌측 하단이 원점   음수:좌측 상단이 원점)

pBmiInfo->bmiHeader.biPlanes = 1; //목표 장치의 플레인 수(1로 설정해야함)

pBmiInfo->bmiHeader.biBitCount = (short) 24; //각 픽셀의 비트수

pBmiInfo->bmiHeader.biCompression = 0; //압축 방법(bi_rgb 또는 0:무압축 비트맵)

pBmiInfo->bmiHeader.biSizeImage =0; //비트맵 영상 크기(무압축인 경우 0으로 설정)

pBmiInfo->bmiHeader.biClrImportant = 0; // 비트맵 디스플레이에 사용되는 컬러수

pBmiInfo->bmiHeader.biClrUsed = 0; // 사용된 컬러의 수

pBmiInfo->bmiHeader.biXPelsPerMeter = 10000; //수평 해상도 (미터당 픽셀수)

pBmiInfo->bmiHeader.biYPelsPerMeter = 10000; //수직 해상도 (미터당 픽셀 수)

}BITMAPINFOHEADER;

비디오 캡쳐를 위해서는 다음과 같이 BITMAPINFOHEADER 구조체에서 biSize,biWidth, biHeight, biPlanes, biBitCount 값을 설정하고 나머지 값을은 0으로 설정하면된다.

BITMAPINFO bmi;

memset(&bmi.bmiHeader, 0, sizeof(bmiHeader));

pBmiInfo->bmiHeader.biSize = sizeof(bmi.bmiHeader);

pBmiInfo->bmiHeader.biWidth = 640;//영상의 세로크기(양수:좌측 하단이 원점

pBmiInfo->bmiHeader.biHeight = 480;//영상의 가로크기

pBmiInfo->bmiHeader.biPlanes = 1;//목표 장치의 플레인 수(1로 설정해야함)

pBmiInfo->bmiHeader.biBitCount =  24;//각 픽셀의 비트수

 

capDriverDisconnect(hWnd);

#define capDriverDisconnect(hwnd)       ((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_DISCONNECT, (WPARAM)0, 0L))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_DRIVER_DISCONNECT를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 carDriverConnect() 함수에 의하여 연결한 캡쳐 윈도우와 캡쳐 장치를 분리하는 함수이다. hWnd에는 분리하고자 하는 캡쳐 윈도우의 핸들 값을 설정한다.

 

BOOL capSetCallbackOnFrame(hWnd, func);

#define capSetCallbackOnFrame(hwnd, fpProc)        ((BOOL)AVICapSM(hwnd,WM_CAP_SET_CALLBACK_FRAME, 0, (LPARAM)(LPVOID)(fpProc)))

AVICapSM 역시 아래 처럼 디파인 되어 있다.( 윈도우 메시지인 WM_CAP_SET_CALLBACK_FRAME를 사용 하는듯 하다.)

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

VFW 라이브러리에서는 캡쳐된 비디오 프레임을 화면에 보여주는 작업을 callback함수를 사용해서 처리하도록 하고 있다. capSetCallbackOnFrame()함수는 캡쳐 장치로부터 비디오 프레임이 캡쳐되었을때에 이를 화면에 보여주기 위해서 호출되는 callback 함수를 설정한다. hWnd는 캡쳐 윈도우의 핸들 값으로 설정하고 func는 호출될 함수 이름으로 설정한다.  

 

 

BOOL capOverlay(hWnd, f);
#define capOverlay(hwnd, f)          ((BOOL)AVICapSM(hwnd, WM_CAP_SET_OVERLAY, (WPARAM)(BOOL)(f), 0L))

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 비디오 오버레이를 사용 할것인지 아닌지를 설정한다. 설정 성공시 TRUE, 그렇지 않을경우 FALSE를 반환.
매개변수는 순서대로 윈도우 핸들, 설정시-true 미설정시 -false
비디오 오버레이를 사용하면 CPU자원이 요구 되지 않아 효과적이라고 한다. 
 
BOOL capPreview(hWnd, f);
#define capPreview(hwnd, f)       ((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEW, (WPARAM)(BOOL)(f), 0L))

#define AVICapSM(hwnd,m,w,l) ( (::IsWindow(hwnd)) ? ::SendMessage(hwnd,m,w,l) : 0)

이 함수는 미리보기 모드를 동작 시킬 것인지 아닌지를 설정한다. 설정을 하게 되면 영상 프레임이 시스템의 메모리로 전달되어 GDI함수를 사용하여 캠처 윈도우에 영상을 출력하게 된다.
매개 변수는 순서대로 윈도우 핸들, 설정시 -true, 미설정시 -false 










예제 소스>


#include "windows.h"
#include "Vfw.h"
#pragma comment (lib,"vfw32.lib")
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr);
HINSTANCE g_hInst;
HWND hWndMain;
HBITMAP hBit;
BITMAPINFO Bm;
LPCSTR lpszClass=TEXT("Class");
//WinMain 시작
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
      ,LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;
 //1. 윈도우 속성값 등록
 WndClass.cbClsExtra=0;
 WndClass.cbWndExtra=0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);
 WndClass.hInstance=hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);  //주소에 Write
 
 //2. 윈도우 생성
 hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
  NULL, (HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow); //화면에 뿌려줌
 
 //3. 메시지 처리(무한 반복)
 while (GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 HWND hVFW;
 PAINTSTRUCT ps;
 switch(iMessage)
 {
 case WM_CREATE:
  hWndMain=hWnd;
  hdc=GetDC(hWndMain);
  hVFW=capCreateCaptureWindow(TEXT("VFW"),WS_CHILD | WS_VISIBLE , 0,0,1,1,hWnd,0);
  capDriverConnect(hVFW,0);
  capPreviewRate(hVFW,1);
  capPreview(hVFW,TRUE);
  capGetVideoFormat(hVFW,&Bm, sizeof(Bm));
  hBit=CreateCompatibleBitmap(hdc, Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight);
  if (capSetCallbackOnFrame(hVFW,FramInfo)==FALSE)
  {
   return FALSE;
  }
  ReleaseDC(hWndMain, hdc);
  return 0;
 case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  EndPaint(hWnd, &ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
LRESULT CALLBACK FramInfo(HWND hVFW, LPVIDEOHDR VideoHdr)
{
 HDC hdc;
 HDC hMemDC;
 HBITMAP OldBitmap;
 int iCntX;
 int iCntY;
 int Jump;
 
 hdc = GetDC(hWndMain);
 hMemDC = CreateCompatibleDC(hdc);
 OldBitmap = (HBITMAP)SelectObject(hMemDC, hBit);
 
 Jump=0;
 for (iCntY=0 ; iCntY < Bm.bmiHeader.biHeight ; ++iCntY)
 {
  for (iCntX=0 ; iCntX < Bm.bmiHeader.biWidth ; ++iCntX)
  {
   //원영상
   SetPixel(hMemDC, iCntX,(Bm.bmiHeader.biHeight - iCntY)-1, RGB(VideoHdr->lpData[Jump+2],VideoHdr->lpData[Jump+1],VideoHdr->lpData[Jump]));
   Jump+=3;
  }
 }
 BitBlt(hdc, 0,0,Bm.bmiHeader.biWidth,Bm.bmiHeader.biHeight,hMemDC,0,0,SRCCOPY);
 SelectObject(hMemDC,OldBitmap);
 ReleaseDC(hWndMain,hdc);
 return 0;
}

설정

트랙백

댓글

윈도우즈 API 정복 예제 소스

프로그래밍 언어/WinApi 2014. 7. 13. 02:02

http://kikillers.tistory.com/2

설정

트랙백

댓글