본문으로 바로가기
#include <windows.h>

//오른쪽으로 띄울 공간
#define  X_POS    20      
#define  Y_POS    20    

//스태틱의 가로, 세로 크기
#define  S_WIDTH    200    
#define  S_HEIGHT  25

//줄과 줄 사이의 공간(세로)
#define  X_GAP    5  
#define  Y_GAP    5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(WPARAM, LPARAM);
LRESULT On_Create(WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(WPARAM wParam, LPARAM lParam);
void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

//ReadFile()로 읽어들이기 위한 동적할당 받을 buf 변수 생성.
unsigned char *ucpData;      //동적할당용

HWND hWnd;
HWND hHandle;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  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)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, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWpWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(WPARAM wParam, LPARAM lParam)
{
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BITMAPFILEHEADER stBfHead;    //비트맵 헤더용
  BITMAPINFOHEADER stBinfoHead;
  BOOL bRet;
  DWORD dwCount;

  /*bmp 읽어 들이기*/
  hFile = CreateFile(L"C:\\1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    //SendMessage(hWnd, WM_DESTROY, 0, 0);  // 못 쓰겠끔 프로그램 종료.
    return 0;
  }
  
  /*헤더 읽기*/
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  PrintBmpInfo(&stBfHead, &stBinfoHead);

  MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(WPARAM wParam, LPARAM lParam)
{
  //MessageBox(hWnd, L"디스트로이", L"Button", MB_OK);  //디스트로이가 호출안되고 바로 종료
  //파일 열때 디스트로이 함수를 생성해버리면 동적할당이 실패했는데도 불구하고 동적할당을 해제하려 들 것이다. 이것을 
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;

  BeginPaint(hWnd, &ps);
  EndPaint(hWnd, &ps);

  return 0;
}

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{//이제 헤더출력을 위해 윈도우를 만들어 스태틱을 만들 것이다.
  
  int iYCount;
  WCHAR *ucTitle[30=
  {
    L"Magic Number    :",
    L"
File Byte Size    :",
    L"
Data Position    :",
    L"
Info Header Size    :",
    L"
Width Size    :",
    L"
Height Size    :",
    L"
Bit Planes Number    :",
    L"
Pixel Bit Count    :",
    L"
Compression Boolen  :",
    L"
Image Size    :",
    L"
X Pelsper Meter    :",
    L"
Y Pelsper Meter    :",
    L"
Used Color Number    :",
    L"
Important Color Index  :",
  };
  
  WCHAR ucValue[14][30];
  WCHAR wStr[20] = { 0, };
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  wsprintf(ucValue[8], L"%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);


  //출력
  for (iYCount = 0; iYCount < 14; ++iYCount)
  {
    CreateWindow(
      L"static",
      ucTitle[iYCount],
      WS_CHILD | WS_VISIBLE,
      X_POS, Y_POS + (S_HEIGHT*iYCount) + (Y_GAP*iYCount), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

    hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (X_POS + S_WIDTH + X_GAP), (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );
  }

  MessageBox(hWnd, L"PrintBmpInfo()의 CreateWindow()가 성공적으로 열렸습니다.", L"Button", MB_OK);

  return;
}


10:02  현재까지 완료된 소스다. 변화된 구간은

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{//이제 헤더출력을 위해 윈도우를 만들어 스태틱을 만들 것이다.
  
  int iYCount;
  WCHAR *ucTitle[30=
  {
    L"Magic Number    :",
    L"
File Byte Size    :",
    L"
Data Position    :",
    L"
Info Header Size    :",
    L"
Width Size    :",
    L"
Height Size    :",
    L"
Bit Planes Number    :",
    L"
Pixel Bit Count    :",
    L"
Compression Boolen  :",
    L"
Image Size    :",
    L"
X Pelsper Meter    :",
    L"
Y Pelsper Meter    :",
    L"
Used Color Number    :",
    L"
Important Color Index  :",
  };
  
  WCHAR ucValue[14][30];
  WCHAR wStr[20] = { 0, };
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  wsprintf(ucValue[8], L"%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);


  //출력
  for (iYCount = 0; iYCount < 14; ++iYCount)
  {
    CreateWindow(
      L"static",
      ucTitle[iYCount],
      WS_CHILD | WS_VISIBLE,
      X_POS, Y_POS + (S_HEIGHT*iYCount) + (Y_GAP*iYCount), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

    hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (X_POS + S_WIDTH + X_GAP), (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );
  }

  MessageBox(hWnd, L"PrintBmpInfo()의 CreateWindow()가 성공적으로 열렸습니다.", L"Button", MB_OK);

  return;
}


이 PrintfBmpInfo() 인 bmp 사진의 헤더 정보를 출력하는 함수다.


주목해야할 부분은


WCHAR ucValue[14][30];
  WCHAR wStr[20] = { 0, };
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  wsprintf(ucValue[8], L"%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);



이 부분이며

가장 주목해야할 부분은

  wsprintf(ucValue[0], L"[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));

이 부분에 대한 처리이다.

*(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1)

을 보면, 우선 한 글자를 출력할 자료형으로 캐스팅 연산 해준뒤, stpFH 포인터로 가르키지 않고 그냥 그 자체가 가르키는 부분 ( + 0 , *(...) 이런식 ) 을 해주고 있다.

나는 계속 %s로만 하려다가 실패했는데 %c 생각을 왜 못했을까.

어찌됬건 출력해보니



이와 같이 아주 예쁘게 뜬다 라는 것을 확인 할 수 있다.

아마 저 뒤 옵션을

 hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (X_POS + S_WIDTH + X_GAP), (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

이와같이       L"edit",

로 둔 것은 저 뒤의 값을 수정하고 엔터를 쳤을 시, 값이 수정되며 실제 bmp 사진 또한 그 값으로 변경 시키려 하는 느낌이다.


음.. 단순히 우리가 dos 용으로 노가다 했던 작업이 WinAPI를 활용하면 얼마나 쉽게 함수로 처리할 수 있는지에 대해서 알려주려하는 느낌의 프로젝트이다.

이거 다음에 바로 캠의 영상처리를 WinAPI 함수를 활용하여 또 얼마나 간단히 처리가 되는지 알려주려 하시는 느낌이다.






이런 작업을 도스용에서 윈도우용으로 코팅 했다. 라고 한다. 꼭 기억해놓자.


우리가 도스용으로 계산했던것을 잠시 복습해보면,


흑백 처리 : RGB, 3가지 색깔의 평균을 낸다음 그 값을 저장하는 변수 추가하고 그 값을 출력할 동적할당받은 포인터에 값을 넣어준다.

-> 이용 -> 밝기 처리 : 사용자가 원하는 색깔 중 ( RGB ) 한가지 색깔에 값을  <+ 변수> 이처럼 처리 해 둔뒤 값을 처리한다.


회전 처리 : 사용자가 원하는 회전의 head 위치를 파악하여 계산. 즉, 

-> 이용 -> 미러 회전 : 단순히 미러 회전으로 원본 사진과 대칭하도록 처리하려 한다면 y축 값들은 같으며, x축 값들이 거꾸로 대입 된다.

-> 이용 -> 물에 비친 모습 회전 : 미러 회전에서는 y축은 그대로, x축은 끝에서부터 넣었다. 이것은 y축 또한 끝에서, x축도 끝에서 처리를 해주면 마치 물에                                 비친 모습이 연출 된다.

-> 이용 -> 90도 회전 : 이것부터가 문제다. 우선은 일반화 처리 계산을 해야한다. 일반화가 필요한 이유는 90도 회전시 세로가 즉 가로가 되며, 가로가 세로가                         되므로 해당하는 모든 bmp 넓이(?) 값은 같아야 하며 말그대로 bmp RGB 값들만 바뀐 각도로 넣어야 한다.

일반화가 정말 필요한 이유는 패딩 값 때문이다. 비트맵은 4의 배수로만 처리되도록 만들어졌기 때문에 4의 배수를 무조건 맞추려 든다. 즉, 각 RGB 값 마다 패딩 값을 더해줘야 한다.



#include <windows.h>
//오른쪽으로 띄울 공간
#define  X_POS    20      
#define  Y_POS    20    

//스태틱의 가로, 세로 크기
#define  S_WIDTH    200    
#define  S_HEIGHT  25

//줄과 줄 사이의 공간(세로)
#define  X_GAP    5  
#define  Y_GAP    5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(WPARAM, LPARAM);
LRESULT On_Create(WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(WPARAM wParam, LPARAM lParam);
void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

BITMAPFILEHEADER stBfHead;
BITMAPINFOHEADER stBinfoHead;

unsigned char *ucpData;      //동적할당용
unsigned int uiPad;

HWND hWnd;
HWND hHandle;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  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)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, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWpWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(WPARAM wParam, LPARAM lParam)
{
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BOOL bRet;
  DWORD dwCount;

  //bmp read
  hFile = CreateFile(L"C:\\1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }
  //헤더 read
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  uiPad = stBinfoHead.biWidth % 4;

  PrintBmpInfo(&stBfHead, &stBinfoHead);

  ucpData = malloc(stBinfoHead.biSizeImage);
  if (ucpData == 0)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 malloc() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, ucpData, stBinfoHead.biSizeImage, &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    free(ucpData);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - .bmp 파일 읽기 실패", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  MessageBox(hWnd, L"On_Create()가 성공적으로 호출 되었습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(WPARAM wParam, LPARAM lParam)
{
  free(ucpData);
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iCountX, iCountY;

  hdc = BeginPaint(hWnd, &ps);

  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(hdc, int X, int Y, COLORREF crColor);
    }
  }

  EndPaint(hWnd, &ps);

  return 0;
}

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{
  int iYCount;
  WCHAR *ucTitle[30=
  {
    L"Magic Number    :",
    L"
File Byte Size    :",
    L"
Data Position    :",
    L"
Info Header Size    :",
    L"
Width Size    :",
    L"
Height Size    :",
    L"
Bit Planes Number    :",
    L"
Pixel Bit Count    :",
    L"
Compression Boolen  :",
    L"
Image Size    :",
    L"
X Pelsper Meter    :",
    L"
Y Pelsper Meter    :",
    L"
Used Color Number    :",
    L"
Important Color Index  :",
  };
  WCHAR ucValue[14][30];
  WCHAR wStr[20] = { 0, };
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  
  switch (stpIH->biCompression)
  {
    case 0:
      wsprintf(ucValue[8], L"[BI_RGB]");
      break;
    case 1:
      wsprintf(ucValue[8], L"
[BI_RLE8]");
      break;
    case 2:
      wsprintf(ucValue[8], L"
[BI_RLE4]");
      break;
    case 3:
      wsprintf(ucValue[8], L"
[BI_BITFIELDS]");
      break;
    default:
      break;
  }
  wsprintf(ucValue[8], L"
%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);


  //출력
  for (iYCount = 0; iYCount < 14; ++iYCount)
  {
    CreateWindow(
      L"static",
      ucTitle[iYCount],
      WS_CHILD | WS_VISIBLE,
      X_POS, Y_POS + (S_HEIGHT*iYCount) + (Y_GAP*iYCount), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

    hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (X_POS + S_WIDTH + X_GAP), (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );
  }

  return;
}




현재까지 완성된 소스는 이러하며, 주목해야 할 부분으로는


LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iCountX, iCountY;

  hdc = BeginPaint(hWnd, &ps);

  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(hdc, int X, int Y, COLORREF crColor);
    }
  }

  EndPaint(hWnd, &ps);

  return 0;
}


이부분이다.


ui는 좌표값의 싸움이다.

폴더폰은 화면이 있는데 배터리 크기가 어떻다 저렇다 디자이너가 던져주면

우리 프로그래머가 그 좌표값 처리를 알고리즘 구현 해야한다.


우리의 고정관념은 그림은 똑바로 저장된다고 생각하지만 저장할때는 그림이 이미 뒤집혀서 저장된 상태이다.

인텔이 리틀 엔디안인지 빅 엔디안인지 어떻게 알 수 있을까? 이것을 미리 명시도 안 해놨기 때문에 그렇다. 즉 ! 분석해보자면,

이 '가' 라는 글자는 현재 인텔이 의해서 인텔 특유의 저장 방식인 리틀 엔디안 방식으로 저장된 것이고,

우리는 이것을 명시하지 않고 저장된 그대로 색깔만 바꿔서 출력했기 때문에 저장된 그대로 뒤집혀서 '가'라는 글자는 빨간색으로 뒤집혀서 출력되는 것이다.






현재까지의 소스를 보면


#include <windows.h>
//오른쪽으로 띄울 공간
#define  X_POS    20      
#define  Y_POS    20    

//스태틱의 가로, 세로 크기
#define  S_WIDTH    200    
#define  S_HEIGHT  25

//줄과 줄 사이의 공간(세로)
#define  X_GAP    5  
#define  Y_GAP    5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(WPARAM, LPARAM);
LRESULT On_Create(WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(WPARAM wParam, LPARAM lParam);
void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

BITMAPFILEHEADER stBfHead;
BITMAPINFOHEADER stBinfoHead;

unsigned char *ucpData;      //동적할당용
unsigned int uiPad;
unsigned int uiPad2;

HWND hWnd;
HWND hHandle;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  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)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, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWpWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(WPARAM wParam, LPARAM lParam)
{
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BOOL bRet;
  DWORD dwCount;

  //bmp read
  hFile = CreateFile(L"C:\\156.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }
  //헤더 read
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  uiPad = stBinfoHead.biWidth % 4;
  uiPad2 = stBinfoHead.biHeight % 4;

  PrintBmpInfo(&stBfHead, &stBinfoHead);

  ucpData = malloc(stBinfoHead.biSizeImage);
  if (ucpData == 0)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 malloc() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, ucpData, stBinfoHead.biSizeImage, &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    free(ucpData);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - .bmp 파일 읽기 실패", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  MessageBox(hWnd, L"On_Create()가 성공적으로 호출 되었습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(WPARAM wParam, LPARAM lParam)
{
  free(ucpData);
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iCountX, iCountY;

  hdc = BeginPaint(hWnd, &ps);

  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(hdc, iCountX, iCountY, RGB(ucpData[(iCountY * stBinfoHead.biWidth * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)], 00));
    }
  }

  EndPaint(hWnd, &ps);

  return 0;
}

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{
  int iYCount;
  WCHAR *ucTitle[30=
  {
    L"Magic Number    :",
    L"
File Byte Size    :",
    L"
Data Position    :",
    L"
Info Header Size    :",
    L"
Width Size    :",
    L"
Height Size    :",
    L"
Bit Planes Number    :",
    L"
Pixel Bit Count    :",
    L"
Compression Boolen  :",
    L"
Image Size    :",
    L"
X Pelsper Meter    :",
    L"
Y Pelsper Meter    :",
    L"
Used Color Number    :",
    L"
Important Color Index  :",
  };
  WCHAR ucValue[14][30];
  WCHAR wStr[20] = { 0, };
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  
  switch (stpIH->biCompression)
  {
    case 0:
      wsprintf(ucValue[8], L"[BI_RGB]");
      break;
    case 1:
      wsprintf(ucValue[8], L"
[BI_RLE8]");
      break;
    case 2:
      wsprintf(ucValue[8], L"
[BI_RLE4]");
      break;
    case 3:
      wsprintf(ucValue[8], L"
[BI_BITFIELDS]");
      break;
    default:
      break;
  }
  wsprintf(ucValue[8], L"
%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);


  //출력
  for (iYCount = 0; iYCount < 14; ++iYCount)
  {
    CreateWindow(
      L"static",
      ucTitle[iYCount],
      WS_CHILD | WS_VISIBLE,
      (stpIH->biWidth) + X_POS, Y_POS + (S_HEIGHT*iYCount) + (Y_GAP*iYCount), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

    hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (stpIH->biWidth) + X_POS + S_WIDTH + X_GAP, (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );
  }

  return;
}




LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iCountX, iCountY;

  hdc = BeginPaint(hWnd, &ps);

  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(hdc, iCountX, iCountY, RGB(ucpData[(iCountY * stBinfoHead.biWidth * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)], 00));
    }
  }

  EndPaint(hWnd, &ps);

  return 0;
}




NTSC

PAL

이걸 합해서 주사 방식이라고 한다. 화면에 뿌린다고 하는. 화면에 주사 한다고 표현한다.

미국하고 우리나라 식은 NTSC 방식

유럽 식 PAL 방식


주사 방식은 화면을 어떻게 찍을지. 찍는 방식은 지그재그 찍고 있다 현재는

이 찍는 방식을 달리할 수 있는 것이다. 역 지그재그, 지렁이 방식 , 무엇을 쓰느냐 다 똑같다.

리틀 엔디안 이 좋냐 빅 엔디안 좋냐. 이럴때는 저게 좋고 이게 좋고 이렇기 때문이다.


현재 우리의 주사방식을 바꿔서 화면에 출력하게끔 하는 것이다.


1초에 이런 사진을 한장 보여주면 1프레임이라 부른다.


점이 아직 덜 찍혔는데 화면에 나타나니까 공백 부분에 검은 줄이 생긴다. 우리 눈에는 


완전 초고속 카메라로 찍으면 한 점 한 점 찍는게 보일 것이다. 허나 빛의 속도로 찍고 있기에 거의 불가능 하다.






현재 난관에 봉착했다. memDC에 비트맵 사진을 올렸고 그것을 출력한다고 생각했는데 뜻대로 되지 않았다 selectObject() 를 사용하여 해당하는 비트맵을

선택해주는 부분은 필수 인듯 하다. 이걸위해서 어떤 작업이 필요할 듯하다.



실제로는 hbmScreen이 가지고 있는 것 같다. 선생님의 소스는 근데 왜 안뜰까.. 우선 paint 쪽으로 옮겨서 다시 해보자.

아 된다.




다시 잘 뜨기 시작했다.

지금 현재 선생님이 주신 소스를 붙인 최종 소스로는



#include <windows.h>
//오른쪽으로 띄울 공간
#define  X_POS    20      
#define  Y_POS    20    

//스태틱의 가로, 세로 크기
#define  S_WIDTH    200    
#define  S_HEIGHT  25

//줄과 줄 사이의 공간(세로)
#define  X_GAP    5  
#define  Y_GAP    5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(WPARAM, LPARAM);
LRESULT On_Create(WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(WPARAM wParam, LPARAM lParam);
void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH);
void DrawBmp();//memDC에 원본 그림 복사;

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

BITMAPFILEHEADER stBfHead;
BITMAPINFOHEADER stBinfoHead;

unsigned char *ucpData;      //동적할당용
unsigned int uiPad;
unsigned int uiPad2;

HWND hWnd;
HWND hHandle;

static HBITMAP hbmScreen;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  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)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, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWpWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(WPARAM wParam, LPARAM lParam)//동적활당
{
  HDC hdc; // 지역
  HDC memDC; // 지역
  PAINTSTRUCT ps;
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BOOL bRet;
  DWORD dwCount;

  //bmp read
  hFile = CreateFile(L"C:\\156.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }
  //헤더 read
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  uiPad = stBinfoHead.biWidth % 4;
  uiPad2 = stBinfoHead.biHeight % 4;

  PrintBmpInfo(&stBfHead, &stBinfoHead);

  ucpData = malloc(stBinfoHead.biSizeImage);
  if (ucpData == 0)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 malloc() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, ucpData, stBinfoHead.biSizeImage, &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    free(ucpData);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - .bmp 파일 읽기 실패", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  DrawBmp();

  MessageBox(hWnd, L"On_Create()가 성공적으로 호출 되었습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(WPARAM wParam, LPARAM lParam)
{
  //DeleteDC(memDC);
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(WPARAM wParam, LPARAM lParam)//화면에 그리는 역활만.
{
  HDC hdc;
  HDC memDC;
  HBITMAP myBitmap;
  PAINTSTRUCT ps;

  hdc = BeginPaint(hWnd, &ps);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);

  BitBlt(hdc, 00, stBinfoHead.biWidth, stBinfoHead.biHeight, memDC, 00, SRCCOPY);

  EndPaint(hWnd, &ps);
  DeleteDC(memDC);
  //InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{
  int iYCount;
  WCHAR *ucTitle[30=
  {
    L"Magic Number    :",
    L"
File Byte Size    :",
    L"
Data Position    :",
    L"
Info Header Size    :",
    L"
Width Size    :",
    L"
Height Size    :",
    L"
Bit Planes Number    :",
    L"
Pixel Bit Count    :",
    L"
Compression Boolen  :",
    L"
Image Size    :",
    L"
X Pelsper Meter    :",
    L"
Y Pelsper Meter    :",
    L"
Used Color Number    :",
    L"
Important Color Index  :",
  };
  WCHAR ucValue[14][30];
  
  wsprintf(ucValue[0], L"
[%c][%c]", *(((unsigned char *)(stpFH)) + 0), *(((unsigned char *)(stpFH)) + 1));
  wsprintf(ucValue[1], L"
[%d] Bytes", stpFH->bfSize);
  wsprintf(ucValue[2], L"%d", stpFH->bfOffBits);
  wsprintf(ucValue[3], L"%d", stpIH->biSize);
  wsprintf(ucValue[4], L"[%d] pixel", stpIH->biWidth);
  wsprintf(ucValue[5], L"[%d] pixel", stpIH->biHeight);
  wsprintf(ucValue[6], L"%d", stpIH->biPlanes);
  wsprintf(ucValue[7], L"%d", stpIH->biBitCount);
  
  switch (stpIH->biCompression)
  {
    case 0:
      wsprintf(ucValue[8], L"[BI_RGB]");
      break;
    case 1:
      wsprintf(ucValue[8], L"
[BI_RLE8]");
      break;
    case 2:
      wsprintf(ucValue[8], L"
[BI_RLE4]");
      break;
    case 3:
      wsprintf(ucValue[8], L"
[BI_BITFIELDS]");
      break;
    default:
      break;
  }
  wsprintf(ucValue[8], L"
%d", stpIH->biCompression);
  wsprintf(ucValue[9], L"[%d] Bytes", stpIH->biSizeImage);
  wsprintf(ucValue[10], L"%d", stpIH->biXPelsPerMeter);
  wsprintf(ucValue[11], L"%d", stpIH->biYPelsPerMeter);
  wsprintf(ucValue[12], L"%d", stpIH->biClrUsed);
  wsprintf(ucValue[13], L"%d", stpIH->biClrImportant);


  //출력
  for (iYCount = 0; iYCount < 14; ++iYCount)
  {
    CreateWindow(
      L"static",
      ucTitle[iYCount],
      WS_CHILD | WS_VISIBLE,
      (stpIH->biWidth) + X_POS, Y_POS + (S_HEIGHT*iYCount) + (Y_GAP*iYCount), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );

    hHandle = CreateWindow(
      L"edit",
      ucValue[iYCount],
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
      (stpIH->biWidth) + X_POS + S_WIDTH + X_GAP, (Y_POS + (S_HEIGHT * iYCount) + (Y_GAP * iYCount)), S_WIDTH, S_HEIGHT,
      hWnd,
      (HMENU)-1,
      g_hInst,
      NULL
      );
  }

  return;
}

void DrawBmp()//memDC에 원본 그림 복사
{
  HDC hdc;
  HDC memDC;
  int iCountY; 
  int iCountX;

  hdc = GetDC(hWnd);
  hbmScreen = CreateCompatibleBitmap(hdc, stBinfoHead.biWidth, stBinfoHead.biWidth);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);
  
  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(memDC, iCountX, stBinfoHead.biWidth - iCountY, RGB(ucpData[(iCountY * stBinfoHead.biWidth * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)], 00));
    }
  }
  
  ReleaseDC(hWnd, hdc);
  DeleteDC(memDC);
  free(ucpData);

  return;
}


이러하며, 주요하게 봐야할 구간은

void DrawBmp()//memDC에 원본 그림 복사
{
  HDC hdc;
  HDC memDC;
  int iCountY; 
  int iCountX;

  hdc = GetDC(hWnd);
  hbmScreen = CreateCompatibleBitmap(hdc, stBinfoHead.biWidth, stBinfoHead.biWidth);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);
  
  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(memDC, iCountX, stBinfoHead.biWidth - iCountY, RGB(ucpData[(iCountY * stBinfoHead.biWidth * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)], 00));
    }
  }
  
  ReleaseDC(hWnd, hdc);
  DeleteDC(memDC);
  free(ucpData);

  return;
}


이 부분과



LRESULT On_Paint(WPARAM wParam, LPARAM lParam)//화면에 그리는 역활만.
{
  HDC hdc;
  HDC memDC;
  HBITMAP myBitmap;
  PAINTSTRUCT ps;

  hdc = BeginPaint(hWnd, &ps);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);

  BitBlt(hdc, X_POS, Y_POS, stBinfoHead.biWidth, stBinfoHead.biHeight, memDC, 00, SRCCOPY);

  EndPaint(hWnd, &ps);
  DeleteDC(memDC);
  //InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}


이 부분이다.

즉, memDC는 그 함수에서만 사용하도록 지역으로 옮기고 (Draw() 쪽에서는 새로운 추가 전역 변수인 static HBITMAP hbmScreen; 에 현재 read() 한 bmp 사진을 가져와 복사하기 위함. Paint() 쪽에서는 화면에 칠하기 위함)

의 용도로 옮김, 칠함 의 용도로 hdc, memDC 를 지역변수로써 바꿨다.

조금 이것저것 바뀌어서 해제하는 부분과 생성하는 ㅣ부분이 바뀌었을지 모르니 확인해보자.

우선 paint() 부분,


LRESULT On_Paint(WPARAM wParam, LPARAM lParam)//화면에 그리는 역활만.
{
  HDC hdc;
  HDC memDC;
  HBITMAP myBitmap;
  PAINTSTRUCT ps;

  hdc = BeginPaint(hWnd, &ps);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);

  BitBlt(hdc, X_POS, Y_POS, stBinfoHead.biWidth, stBinfoHead.biHeight, memDC, 00, SRCCOPY);

  EndPaint(hWnd, &ps);
  DeleteDC(memDC);

  InvalidateRect(hWnd, &ps, FALSE);

  return 0;
}


  InvalidateRect(hWnd, &ps, FALSE);

이 부분인데, 전체를 다시그리는 것에 FALSE 를 주고, 그 그림 영역만큼만 다시 그리도록 바꿨다.

그랬더니 다시 잘 뜨는 것을 확인했다.






CreateCompatibleDC(HDC hdc) 

  -  hdc에 NULL 써줘도 된다. NULL 써주면 내부적으러 스크린 DC 얻어서 사용한다.

  GetDC(NULL)와 비슷한원리

  

 

  CreateCompatibleBitmap(HDC hdc, int Widht, int Height)

  -  hdc에 NULL 써주면 안된다!!

 

 

 

 ex)

 g_MemDC      = CreateCompatibleDC(NULL);                        // ok

 g_hBitmap    = CreateCompatibleBitmap(hdc, 1024, 768);         // 에러 리턴


 

 HDC hdc = GetDC(g_hWnd);
 g_hBitmap    = CreateCompatibleBitmap(hdc, 1024, 768);        // ok
 g_hOldBitmap = (HBITMAP)SelectObject(g_MemDC, g_hBitmap);
 ReleaseDC(g_hWnd, hdc); 



즉, 메인 DC가 아닌 memDC를 활용하여 비트맵 복사 작업을 한다 라는 것이다.

이제 hbmScreen 에 우리가 불러들인 bmp 파일이 복사 되었으므로 이 변수만 이용하여 값을 바꾸거나 수정하거나 한 뒤 저장만 따로 하게끔 해준다면

원하는대로 프로그램이 돌아갈 것이다.

다시 정리하자면, DrawBmp() 는,


void DrawBmp()//hbmScreen에 원본 그림 복사
{
  HDC hdc;
  HDC memDC;
  HBITMAP hbmOldScreen;
  int iCountY; 
  int iCountX;

  hdc = GetDC(hWnd);
  hbmScreen = CreateCompatibleBitmap(hdc, stBinfoHead.biWidth, stBinfoHead.biWidth);
  memDC = CreateCompatibleDC(hdc);
  hbmOldScreen = SelectObject(memDC, hbmScreen);
  
  //ucpData, 즉 불러들인 bmp 파일로부터 memDC를 활용하여 hbmScreen에 원본 비트맵 파일 저장. 
  //( stBinfoHead.biWidth - iCountY 함으로써 현재 리틀엔디안으로 상이 뒤집힌 상태로 저장된 것을 올바르게 다시 저장) 
  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(memDC, iCountX, stBinfoHead.biWidth - iCountY, RGB(ucpData[(iCountY * stBinfoHead.biWidth * 3) + ((iCountX * 3) + 2) + (iCountY*uiPad)], 00));
    }
  }

  ReleaseDC(hWnd, hdc);
  DeleteDC(memDC);
  free(ucpData);//hbmScreen에 bmp복사가 끝났으므로 필요 없음.

  return;
}

이와 같을 것이고.On_Paint() 는 


LRESULT On_Paint(WPARAM wParam, LPARAM lParam)//화면에 그리는 역활만.
{
  HDC hdc;
  HDC memDC;
  PAINTSTRUCT ps;

  hdc = BeginPaint(hWnd, &ps);
  memDC = CreateCompatibleDC(hdc);
  SelectObject(memDC, hbmScreen);

  BitBlt(hdc, X_POS, Y_POS, stBinfoHead.biWidth, stBinfoHead.biHeight, memDC, 00, SRCCOPY);

  EndPaint(hWnd, &ps);
  DeleteDC(memDC);

  InvalidateRect(hWnd, &ps, FALSE);

  return 0;
}


이와 같을 것이다.

여기서 무효화 영역 설정이 좀 애매한데..




InvalidateRect

원형BOOL InvalidateRect(HWND hWnd, CONST RECT *lpRect, BOOL bErase);
MFC 원형void CWnd::InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE );
인수

▶hWnd:무효 영역을 설정할 윈도우의 핸들. NULL일 경우 모든 윈도우를 무효화한다.

▶lpRect:무효화할 영역. NULL이면 작업 영역 전체가 무효화된다.

▶bErase:무효 영역의 배경을 먼저 지울 것인가를 지정한다. TRUE이면 BeginPaint 함수가 배경을 먼저 지운 후 작업 영역을 그린다.

리턴성공하면 nonzero, 실패시 0을 리턴한다.


윈도우 프로그래밍상에서 사용되는 정확한 용어는 "무효화 영역" 이며, 무효화 영역의 반대 의미로 유효화 영역이라는 단어를 사용하기엔 부적절해 보이는군요.

 

무효화 영역이란, "다시 그려져야 할 필요가 있는 부분" 을 말합니다. 예를 들면.. 계산기 프로그램을 화면에 띄어놓고, 메모장 프로그램을 열였을때, 메모장이 계산기의 일부(또는 전체)를 가렸다고 해 보죠. 메모장이 나중에 실행되었기 때문에 화면상에서 가장 위쪽에 위치하므로, 계산기의 가려진 부분은 화면에 보이지 않는 상태가 될겁니다. 그때 메모장을 화면의 다른 부분으로 옮기거나 최소화시켰다면, 가려져있던 계산기는 화면에 다시 나타나야 합니다. 즉, 다시 그려져야 할 필요성이 있다는 얘기죠. 이때 "다시 그려져야 할 부분" 을 무효화 영역이라고 합니다. 그림이나 스크린샷으로 설명해드리면 더 쉽게 이해가 가실텐데 좀 아쉽군요. 김상형님의 Windows API 정복 이라는 책에 그림과 함께 아주 쉽게 설명되어 있는데 말이죠.

 

예로 들기로는 다른 윈도우에 의해 가려졌다가 나타나는 상황에 무효화 영역이 만들어진다고 예를 들었는데, 사실 무효화 영역이 만들어지는 경우는 무지하게 많습니다. 윈도우의 위치가 옮겨지는 경우에는 이전 위치에 있던 윈도우를 화면에서 지우고 새로운 위치에 "다시 그려야"하므로 무효화 영역이 발생합니다. 최소화 시켰다가 다시 원래 크기로 복원하는 경우에도 무효화 영역이 발생합니다.



어쨌거나, 이제 제대로 출력이 되니 WinAPI 용 비트맵 뷰어 나 컨버터를 만들어 봐야하겠다. 이건 우리 알아서 하라는 느낌.

이제 영상처리로 넘어간다. 가 아닌 비트맵 뷰어 다시  그래프를 그리려 한다.


그냥 RGB 각각 분리해서 구했지만 그냥 전부 합산 해서 /3 으로 평균값 구한다. 나누는건 우리가 알아서 구할 것.

자, 드로우 bmp 함수를 보면, 현재 그래프 분포도를 그리고 있는 중인데

void DrawBmp()//hbmScreen에 원본 그림 복사
{
  HDC hdc;
  HDC memDC;
  HBITMAP hbmOldScreen;
  unsigned int uiCount[256= { 0, };
  int iCountY; 
  int iCountX;
  unsigned int uiMaxVal;  //그래프의 세로 축 최대 값을 구하기 위함.

  hdc = GetDC(hWnd);
  hbmScreen = CreateCompatibleBitmap(hdc, stBinfoHead.biWidth, stBinfoHead.biWidth);
  memDC = CreateCompatibleDC(hdc);
  hbmOldScreen = SelectObject(memDC, hbmScreen);
  
  //ucpData, 즉 불러들인 bmp 파일로부터 memDC를 활용하여 hbmScreen에 원본 비트맵 파일 저장. 
  //( stBinfoHead.biWidth - iCountY 함으로써 현재 리틀엔디안으로 상이 뒤집힌 상태로 저장된 것을 올바르게 다시 저장)
  //1. 빈도 수 계산 
  for (iCountY = 0; iCountY < stBinfoHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBinfoHead.biWidth; ++iCountX)
    {
      SetPixel(memDC, iCountX, stBinfoHead.biWidth - iCountY, RGB(ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 2) + (iCountY * uiPad)], 00));
      ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 2) + (iCountY * uiPad)]];  //R, 빈도 수 계산 ex) 0 값 몇개 ~ 1 값 몇개 ~
      ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 1) + (iCountY * uiPad)]];  //G, 빈도 수 계산
      ++uiCount[ucpData[((iCountY * stBinfoHead.biWidth) * 3) + ((iCountX * 3) + 0) + (iCountY * uiPad)]];  //B, 빈도 수 계산
    }
  }

  uiMaxVal = 0;
  for (iCountX = 0; iCountX < 256; ++iCountX)
  {
    uiCount[iCountX] = uiCount[iCountX] / 3;  //평균 값 구함.
    if (uiMaxVal < uiCount[iCountX])
    {
      uiMaxVal = uiCount[iCountX];  // 맥스 값 구함.
    }
  }

  for (iCountX = 0; iCountX < 256; ++iCountX)
  {
    uiCount[iCountX] = (uiCount[iCountX] * 255) / uiMaxVal;  // 그래프의 세로(상한선)가 255를 못 넘게 끔 만든다. 이때 숫자를 키운뒤 나누면 오차율이 줄어든다. 
  }// 이제 uiCount 는 높이 값을 가지게 된다.

  ReleaseDC(hWnd, hdc);
  DeleteDC(memDC);
  free(ucpData);//hbmScreen에 bmp복사가 끝났으므로 필요 없음.

  return;
}


dos 쪽 히스토그램 그리던 곳을 가서 복습해보자면

//1단계. 명암 값의 빈도 수를 계산해 입력 영상의 히스토그램 생성
  for (iCountY = 0; iCountY < stBINFOHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBINFOHead.biWidth; ++iCountX)
    {
      ++uiCountR[ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 2 + iCountY*uiPad]];  // R
      ++uiCountG[ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 1 + iCountY*uiPad]];  // G
      ++uiCountB[ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 0 + iCountY*uiPad]];  // B
    }
  }

  //초기화
  uiSumR[0= uiCountR[0];
  uiSumG[0= uiCountG[0];
  uiSumB[0= uiCountB[0];

  //노는 변수 재활용 iCountx, uiAvg
  //2단계. 각 명암 값 x에서 0 ~ x까지의 누적 빈도 수(누적합)를 계산
  for (iCountX = 1; iCountX < 256; ++iCountX)
  {
    uiSumR[iCountX] = uiSumR[iCountX - 1] + uiCountR[iCountX];
    uiSumG[iCountX] = uiSumG[iCountX - 1] + uiCountG[iCountX];
    uiSumB[iCountX] = uiSumB[iCountX - 1] + uiCountB[iCountX];
  }

  uiAvg = stBINFOHead.biWidth * stBINFOHead.biHeight;

  //3단계. 2단계에서 구한 누적 빈도 수를 정규화(정규화 누적합, 공식 = 누적값 * 최대밝기 / 총픽셀수)
  for (iCountX = 0; iCountX < 256; ++iCountX)
  {
    uiCountR[iCountX] = (uiSumR[iCountX] * 255) / uiAvg;
    uiCountG[iCountX] = (uiSumG[iCountX] * 255) / uiAvg;
    uiCountB[iCountX] = (uiSumB[iCountX] * 255) / uiAvg;
  }

  //4단계. 이미지를 쓴다. 1. buf = org 원래 값 추출 2. 
  for (iCountY = 0; iCountY < stBINFOHead.biHeight; ++iCountY)
  {
    for (iCountX = 0; iCountX < stBINFOHead.biWidth; ++iCountX)
    {
      ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 2 + iCountY*uiPad]          // R
        = uiCountR[ucOrg[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 2 + iCountY*uiPad]];  // R

      ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 1 + iCountY*uiPad]          // G
        = uiCountG[ucOrg[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 1 + iCountY*uiPad]];  // G

      ucBuf[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 0 + iCountY*uiPad]          // B
        = uiCountB[ucOrg[(iCountY*stBINFOHead.biWidth * 3) + iCountX * 3 + 0 + iCountY*uiPad]];  // B
    }
  }