안녕하세요. 오랜만에 C언어에 대해 글을 써봅니다.
여러분들 BMP를 불러올 때 어떻게 하시나요?
https://dojang.io/mod/page/view.php?id=703
아마 위 본 예제가 가장 많이들 사용하실겁니다.
#pragma pack(push, 1) // 구조체를 1바이트 크기로 정렬
typedef struct _BITMAPFILEHEADER // BMP 비트맵 파일 헤더 구조체
{
unsigned short bfType; // BMP 파일 매직 넘버
unsigned int bfSize; // 파일 크기
unsigned short bfReserved1; // 예약
unsigned short bfReserved2; // 예약
unsigned int bfOffBits; // 비트맵 데이터의 시작 위치
} BITMAPFILEHEADER;
typedef struct _BITMAPINFOHEADER // BMP 비트맵 정보 헤더 구조체(DIB 헤더)
{
unsigned int biSize; // 현재 구조체의 크기
int biWidth; // 비트맵 이미지의 가로 크기
int biHeight; // 비트맵 이미지의 세로 크기
unsigned short biPlanes; // 사용하는 색상판의 수
unsigned short biBitCount; // 픽셀 하나를 표현하는 비트 수
unsigned int biCompression; // 압축 방식
unsigned int biSizeImage; // 비트맵 이미지의 픽셀 데이터 크기
int biXPelsPerMeter; // 그림의 가로 해상도(미터당 픽셀)
int biYPelsPerMeter; // 그림의 세로 해상도(미터당 픽셀)
unsigned int biClrUsed; // 색상 테이블에서 실제 사용되는 색상 수
unsigned int biClrImportant; // 비트맵을 표현하기 위해 필요한 색상 인덱스 수
} BITMAPINFOHEADER;
typedef struct _RGBTRIPLE // 24비트 비트맵 이미지의 픽셀 구조체
{
unsigned char rgbtBlue; // 파랑
unsigned char rgbtGreen; // 초록
unsigned char rgbtRed; // 빨강
} RGBTRIPLE;
#pragma pack(pop)
#pragma pack(push, 1)
구조체 데이터를 강제로 1바이트 단위로 쪼개서 정렬한다는 뜻입니다.
가장 최소로 쪼개면 빈공간이 없이 정렬이 되기 때문에 메모리가 100퍼센터 사용할 수 있게 됩니다.
하지만 1바이트 씩 정렬하면 너무 낭비가 되지 않을까요?
cpu가 한 번에 32bit, 64bit를 한 번에 수행하는 반면에 1바이트씩이면은... 분명 낭비가 됩니다. 그래서 많은 블로그에
글쓰신 분들께서도 추천하지는 않습니다.
오늘은 pack#pragma pack(push, 1)를 쓰지 않고 정렬하여 사용하는 방법을 알아보도록 하겠습니다.
1. BMP 바이너리 모드
BMP에 대해 알아보겠습니다. bmp를 binary mode로 열어봅시다.
visual studio를 열어줍니다.
파일을 열고나서 BMP인지 아닌지를 판별하는 데이터가 시작주소에서 2바이트가 존재합니다.
0x424D(16진수) => BM을 의미합니다. (bfType)
BM으로 BMP파일인지 아닌지 확인하게 됩니다. (BM이면 BMP파일)
그럼 BMP파일인지 아닌지 확인해보았고.
BMP파일 구조체에 대해 알아보도록 하겠습니다.
첫번 째 사진인 pragma pack(push,1)가 있고,
두번 째 사진은 pragma pack(push,1)이 없습니다.
첫번 째 경우는 당연히 가장 최소 단위로 쪼개기 때문에 바로 사용가능합니다.
하지만 두번 째 경우는 어떻게 될까요?
포인터 오류가 뜨네요. 널포인터... 아주 무서운 녀석입니다.
왜 이런 결과가 나올까요? 그 이유는 포인터정렬이 제대로 되지 않아 NULL 포인터가 생겼기 때문입니다.
구조체는 #pragma가 정의가 되어 있지 않으면 가장 큰 변수의 사이즈를 기준으로 정렬하게 됩니다.
다시 한 번 볼까요? 가장 큰 바이트가 4바이트입니다. 그렇다면 4바이트 기준으로 정렬을 하겠네요.
그림보시면 단박에 이해가 가실겁니다.
4바이트 씩 맞춰 정렬하게 되면 이와 같이 NULL 포인터가 2바이트 발생하게 되어 저 위와 같은 오류가 뜨게 됩니다.
어떻게 하면 NULL 포인터를 없애면서 4바이트씩 정렬할 수 있을까요?
bfType을 지워버리면 됩니다.
요렇게요. 그렇다면 4바이트씩 정렬이 되겠죠?
어? 그렇다면 bfType을 없앴는데 어떻게 bmp파일인지 아닌지 어떻게 구별하나?
그래서 위에 BMP파일을 바이너리 모드로 연 것입니다.
기억하시나요?
맨 첫 주소에서 2바이트가 BM인지 아닌지 판별하는 bfType입니다.
bfType이 맨 첫주소에 있기 때문에 파일 입출력 문제로 바뀌게 되는것입니다.
여러분들은 저 방법 말고 fread를 사용해서 읽으시길 바랍니다.~
아까와 똑같은 bmp파일으 열고
bmp파일을 fseek함수를 이용해서 맨 첫주소로 이동하여, 0x42, 0x4D를 확인합니다.
if문은 0x424D(==BM) 이면 첫 두 바이트를 출력하게 됩니다
이 방법을 사용하면 1바이트씩이 아닌 4바이트씩 사용하여 BMP를 입출력하는 것이 가능하게 됩니다.
다음 C언어 글은 저 위 방법을 사용하면서, BMP를 사용해서 size를 키우는 실습(upscaling)을 해보도록 하겠습니다.
'C, C++' 카테고리의 다른 글
[C] 구조체 포인터 접근과 최적화 (0) | 2021.01.19 |
---|---|
[C]BMP구조체 : #pragma pack(push,1 )사용하지 않고 정렬하기 2탄 (0) | 2020.03.02 |
[C]BMP RGB 값 조절 (0) | 2020.01.29 |
2차원 배열 동적할당하기 3 편 (1) | 2020.01.27 |
2차원 배열 동적할당하기 2편 (0) | 2020.01.23 |