핵심정리

비트맵 파일의 구조

비트맵 파일은 파일 헤더, 비트맵 정보, 픽셀 데이터 순으로 구성되어 있습니다(픽셀당 색상 수가 16비트 이상일 때는 색상 테이블을 사용하지 않음).

그림 비트맵 파일의 구조

비트맵 헤더의 구조

비트맵 헤더는 비트맵 파일 식별 정보, 파일 크기, 데이터 위치 등의 정보를 담고 있습니다.

비트맵 파일 헤더의 구조
멤버 크기(바이트) 설명
bfType 2 BMP 파일 매직 넘버. 비트맵 파일이 맞는지 확인하는데 사용하며 ASCII 코드로 0x42(B), 0x4D(M)가 저장됩니다.
bfSize 4 파일 크기(바이트)
bfReserved1 2 현재는 사용하지 않으며 미래를 위해 예약된 공간
bfReserved2 2 현재는 사용하지 않으며 미래를 위해 예약된 공간
bfOffBits 4 비트맵 데이터의 시작 위치

비트맵 정보를 저장하는 DIB(device independent bitmap) 헤더는 가로, 세로 크기, 해상도, 픽셀의 비트 수 등 그림의 자세한 정보를 담고 있습니다.

비트맵 정보의 구조
멤버 크기(바이트) 설명
biSize 4 현재 비트맵 정보(BITMAPINFOHEADER)의 크기
biWidth 4 비트맵 이미지의 가로 크기(픽셀)
biHeight 4 비트맵 이미지의 세로 크기(픽셀).
  • 양수: 이미지의 상하가 뒤집혀서 저장된 상태
  • 음수: 이미지가 그대로 저장된 상태
보통 세로 크기는 양수로 저장되어 있습니다.
biPlanes 2 사용하는 색상판의 수. 항상 1입니다.
biBitCount 2 픽셀 하나를 표현하는 비트 수
biCompression 4 압축 방식. 보통 비트맵은 압축을 하지 않으므로 0입니다.
biSizeImage 4 비트맵 이미지의 픽셀 데이터 크기(압축 되지 않은 크기)
biXPelsPerMeter 4 그림의 가로 해상도(미터당 픽셀)
biYPelsPerMeter 4 그림의 세로 해상도(미터당 픽셀)
biClrUsed 4 색상 테이블에서 실제 사용되는 색상 수
biClrImportant 4 비트맵을 표현하기 위해 필요한 색상 인덱스 수

24비트 비트맵은 픽셀을 파랑(B), 초록(G), 빨강(R) 순서로 저장하며 각 색상의 크기는 1바이트입니다. 즉, 픽셀 하나의 크기는 3바이트입니다.

24비트 비트맵의 픽셀 구조
멤버 크기(바이트) 설명
rgbtBlue 1 파랑
rgbtGreen 1 초록
rgbtRed 1 빨강

CPU는 데이터를 4바이트 크기로 처리하는 것이 효율적입니다(3, 7, 11처럼 4로 나누어 떨어지지 않는 크기보다는 4, 8, 12처럼 4로 나누어 떨어지는 크기가 효율적). 그래서 비트맵 포맷은 픽셀의 가로 한 줄을 4의 배수 크기로 저장합니다. 이때 픽셀 정보를 읽으려면 남는 공간(padding)을 구해야 합니다.

#define PIXEL_SIZE   3    // 픽셀 한 개의 크기 3바이트(24비트)
#define PIXEL_ALIGN  4    // 픽셀 데이터 가로 한 줄은 4의 배수 크기로 저장됨
padding = (PIXEL_ALIGN - ((width * PIXEL_SIZE) % PIXEL_ALIGN)) % PIXEL_ALIGN;

가로 x 세로 2차원으로 된 그림의 픽셀 데이터는 파일에 일렬로 저장됩니다. 픽셀 배열에 접근하기 위해서는 다음과 같은 과정으로 인덱스를 구하면 됩니다.

// 일렬로 된 배열에 접근하기 위해 인덱스를 계산
// (x * 픽셀 크기)는 픽셀의 가로 위치
// (y * (세로 크기 * 픽셀 크기))는 픽셀이 몇 번째 줄인지 계산
// 남는 공간 * y는 줄별로 누적된 남는 공간
int index = (x * PIXEL_SIZE) + (y * (width * PIXEL_SIZE)) + (padding * y);

비트맵 픽셀 데이터를 ASCII 코드로 변환하기

비트맵 픽셀을 ASCII 코드로 변환하기 전에 먼저 RGB 색상의 평균을 구하여 흑백값으로 만듭니다. 이 흑백값에 ASCII 문자의 개수를 곱한 뒤 256으로 나누면 ASCII 문자의 인덱스를 얻을 수 있습니다.

// 각 픽셀을 표현할 ASCII 문자. 인덱스가 높을 수록 밝아지는 것을 표현
char ascii[ ] = { '#', '#', '@', '%', '=', '+', '*', ':', '-', '.', ' ' };    // 11개

// 파랑, 초록, 빨강 값의 평균을 구하면 흑백 이미지를 얻을 수 있음
unsigned char gray = (red + green + blue) / PIXEL_SIZE;

// 흑백값에 ASCII 문자의 개수를 곱한 뒤 256으로 나누면 흑백값에 따라 
// ASCII 문자의 인덱스를 얻을 수 있음
char c = ascii[gray * sizeof(ascii) / 256];

아카이브 파일

파일 안에 여러 개의 파일을 저장하는 방식을 아카이브 파일이라 합니다. 아카이브 파일을 구현할 때 최소한으로 필요한 정보는 아카이브 헤더(매직 넘버, 버전), 파일 정보(파일 이름, 파일 크기, 파일 데이터 위치)입니다.

그림 아카이브 파일의 구조

바이너리 포맷

구조체를 이용해서 파일에 정보를 저장한 방식을 바이너리 포맷이라고 부릅니다. 바이너리 포맷은 속도를 중요시하는 곳에서 주로 사용됩니다. 단, 문자열은 그대로 저장하지만 정수와 실수 등은 2진수로 저장하기 때문에 사람이 알아보기가 힘듭니다.