본문 바로가기
코스웨어/12년 내장형하드웨어

[Win API] 10.16 업무일지 - 정철 (비트맵 파일구조)

by 알 수 없는 사용자 2012. 10. 18.
728x90
반응형

* BMP파일을 읽어서 윈도우에 뿌려보자.


예전에 가상머신 프로그램을 작성할 때, 윈도우 PE파일의 헤더에 대해서 공부하였다.

물론 선행학습으로 도서관리 프로젝트 때, 헤더를 직접 만들어 보기도 하였다.


오늘은 BMP파일의 헤더를 분석하고 비트맵파일을 윈도우에 뿌려보도록 한다.


1. 파일의 정보가 어디서 부터 시작되는가?


                       <hexa viewer 로 bmp 파일을 열어 보았다!!>


BMP파일을 헥사뷰어를 이용하여 열어 보았다.

이제 BMP파일의 구조에 대한 공부를 해보도록 한다.


비트맵파일의 구조 (순서)

1. 파일헤드

2. 영상헤드

3. 팔레트 정보

4. 영상데이터


1. 파일헤더 : 총 14byte

typedef struct tagBITMAPFILEHEADER
{
          WORD               bfType;                  // "BM"
이라는 값을 저장함
          DWORD             bfSize;                   //
바이트 단위로 전체파일 크기
          WORD               bfReserved1;          //
예약된 변수
          WORD               bfReserved2;          //
예약된 변수
          DWORD             bfOffBits;                //
영상데이터 위치까지의 거리
} BITMAPFILEHEADER;


파일자체의 정보를 저장하고 있는 구조체로써 windows.h에 미리 정의가 되어 있다.

사용자는 단지 선언만 하고 사용하면 된다.


2. 영상헤드

typedef struct tagBITMAPINFOHEADER
{
          DWORD              biSize;                       //
이 구조체의 크기  
          LONG                biWidth;                      //
픽셀단위로 영상의 폭
          LONG                biHeight;                     //
영상의 높이
          WORD                biplanes;                    //
비트 플레인 수 (항상 1)
          WORD                biBitCount;                 //
픽셀당 비트수 (컬러, 흑백 구별)
          DWORD              biCompression;          //
압축유무
          DWORD              biSizeImage;              //
영상의 크기 (바이트 단위)
          LONG                biXPelsPerMeter;        //
가로 해상도
          LONG                biYPelsPerMeter;        //
세로 해상도
          DWORD              biClrUsed;                 //
실제 사용 색상 수
          DWORD              biClrImportant;            //
중요한 색상 인덱스
} BITMAPINFOHEADER;


비트맵 영상에 대한 크기나 흑백, 컬러 정보, 팔레트 크기 정보 등을 저장하기 위하여 파일헤드 바로 다음에 위치하는 구조체 변수이다.


4. 영상데이터


24비트 비트맵의 경우. 한 픽셀에 R,G,B 값이 각 1byte씩 총 3byte의 값을 가진다. (그래서 24비트이다.)

영상의 정보가 시작되는 부분부터 3byte씩 한점을 그린다.

주의해야 할점들


1). 영상정보는 거꾸로 저장되어 있다.



정보를 있는 그대로 출력할 경우 그림이 반대로 나오게 된다.

증가하는 for문 대신에

총 길이 - 카운터

를 y축값으로 하면 원하는 이미지가 나온다.


2). 색의 저장순서는 RGB가 아니다!


typedef struct tagRGBQUAD
{
           BYTE          rgbBlue;                  // B
성분 (파란색)
           BYTE          rgbGreen;                // G
성분 (녹색)
           BYTE          rgbRed;                   // R
성분 (빨간색)
           BYTE          rgbReserved1;          //
예약된 변수
} RGBQUAD;


RGB의 순서는 BGR순서로 되어있다. 이점을 유의 해야 한다.

SetPixel(hdc,ixCnt,bip->biHeight-iyCnt,RGB(*(ImageP+2),*(ImageP+1),*ImageP));

하여 포인터를 역순으로 써야 원래의 색이 나오게 된다.


3). 가로측은 4단위로 저장된다.


비트맵은 가로줄 저장시 항상 4의 배수가 되어야 한다. (byte가 4의 배수가 될것.)


예를 들어 가로줄이 360이라고 하면, 각 픽셀당 3byte의 값을 가지니까 총 1080byte가 된다.

1080byte는 4로 나누어 떨어지기 때문에 출력이 잘 된다.


361일 경우, 각 픽셀당 3byte의 값을 가지게 되니까 총 1083 byte가 된다.

4로 나누면 3이 남아 버리는데, 이렇게 될경우 1byte의 더미 byte가 붙게 된다.

그래서 그림이 개판이 되어 버린다.


if((bip->biWidth%4) != 0)
{
  if(1 == bip->biWidth%4//3
  {
    ImageP = ImageP + 1;
  }
  else if(2 == bip->biWidth%4//2
  {
    ImageP = ImageP + 2;
  }
  else if(3 == bip->biWidth%4//1
  {
    ImageP = ImageP + 3;
  }
}


영상을 처리할때 조건문을 걸어주어서 더미데이터를 뛰어 넘어 버리면

문제가 해결된다.


* 집에와서 고민한 결과 위의 조건문 대신

x축의 반복문이 끝나는 시점에서

ImageP = ImageP + (bip->biWidth % 4);

한줄을 추가해주면 된다.


* 비트맵이 사선으로 엇나가는 이유


가로 길이가 9라고 가정을 해보자.

메모리에서는 9개의 데이터에 4의 배수를 맞추기 위해 3개의 더미데이터가 붙게 된다.



첫번째가 가로축이 4의 배수인경우이다.

두번째는 가로축 % 4가 1인 경우이다. (더미바이트가 1개가 붙는다. +1)

세번째는 가로축 %4 가 2인 경우이다. (더미바이트가 2개가 붙는다. +2)

네번째는 가로축 %4 가 3인 경우이다. (더미바이트가 3개가 붙는다. +3)


실제 길이는 12이지만, bmp헤더에 가로축 길이정보에서는 9로 나온다.

하여 더미데이터를 생각하지 않고 프로그래밍을 하게되면



픽셀을 위와 같이 사선으로 엇나가게 찍어버린다.

그래서 픽셀을 찍을때는 4의 배수를 계산해서 찍어주어야 한다.


*Tip


리틀인디언을 조심하라... 작은 고추가 맵... 이게 아닌가...



728x90