그래픽 & 멀티미디어
이번에는 그래픽 라이브러리에 이미지를 출력하는 함수를 추가했습니다. 가장 기본이되는 Bitmap을 출력하는 함수에 대해 알아 보도록 하겠습니다.
지금까지 소개된 그래픽 라이브러리, jwGxLib는 화면에 점이나 선 및 도형을 그리기 위해서는 gx_screen_dc()에서 Device Context 핸들인 dc_t 를 구하고 그 핸들을 통해 그래픽을 출력합니다.
처음에는 Bitmap을 다른 도형처럼 출력하려 했습니다. 즉, gx_circle()처럼 gx_draw_bmp() 식으로 말이죠. 아니면 앞으로 jpg나 png, gif를 위해 gx_draw_imgae() 식으로 처리하려 했습니다만, 생각을 바꾸어서 bmp도 화면 출력위한 screen dc 처럼 dc로 처리했습니다.
이미지도 Device Context 로 처리
이유는 이미지를 출력하는 함수를 화면에 이미지만 출력하는 것이 아니라 이미지에 화면을 출력할 수 있고, 또는 이미지에 이미지를 복사해 붙여 넣기를 할 수 있기 때문입니다.
예로 화면을 출력하기 위한 Device Context 핸들이 dc_screen 이라고 하고, 비트맵의 정보를 읽거나 쓸 수 있는 Deviece Context 핸들을 dc_bmp 라고 한다면 아래와 같이 함수를 호출할 수 있습니다.
- gx_bitblt( dc_screen, dc_bmp) <- 화면에 비트맵을 출력
- gx_bitblt( dc_bmp, dc_screen) <- 비트맵에 화면을 출력
모든 이미지를 Device Context 핸들로 다룬다면 하나의 함수로 이미지에서 이미지로 출력할 수 있습니다.
- gx_bitblt( dc_bmp1, dc_bmp2)
- gx_bitblt( dc_bmp, dc_jpg)
그러나 무엇 보다도 dc_t 를 이용한 gx_line(), gx_box(), gx_circle() 함수를 이미지에도 사용할 수 있다는 장점이 있습니다.
Device Context 구조
Device Context 는 아래와 같이 구성되어 있습니다.
typedef struct dc_t_ dc_t;
struct dc_t_
{
int width;
int height;
int dots; // 전체 도트 갯수 width * height
int coor_x;
int coor_y;
int pen_color;
int brush_color;
void *mapped; // 메모리 매핑된 포인터
int (*color )( int red, int green, int blue); // 칼라값 구하기
void (*clear )( dc_t *dc, int color); // 색으로 전체 칠하기
void (*get_pixel)( dc_t *dc, int coor_x, int coor_y, color_t *color); // 칼라 값을 읽어 오기
void (*set_pixel)( dc_t *dc, int coor_x, int coor_y, int color ); // 점 찍기
void (*hline )( dc_t *dc, int fst_x , int snd_x , int coor_y, int color); // 수평선 긋기
void (*vline )( dc_t *dc, int coor_x, int fst_y , int snd_y , int color); // 수직선 긋기
};
이미지도 위의 정보를 제공하도록 합니다. 제일 중요한 것은 get_pixel() 포인터 함수가 되겠습니다. 일단 이 함수만 이미지의 칼라 깊이에 따라 지정해 주면 이미지의 색상을 구할 수 있고, 색상을 구할 수 있다면 화면에 출력할 수 있습니다.
gxbmp.h 에 있는 Bitmap에 대한 정보 struct 입니다.
typedef struct bmp_t_ bmp_t;
struct bmp_t_
{
int width;
int height;
int dots; // 전체 도트 개수
int coor_x;
int coor_y;
int pen_color;
int brush_color;
void *mapped; // 메모리 매핑된 포인터
int (*color )( int red, int green, int blue);
void (*clear )( bmp_t *bmp, int color);
void (*get_pixel)( bmp_t *bmp, int coor_x, int coor_y, color_t *color);
void (*set_pixel)( bmp_t *bmp, int coor_x, int coor_y, int color );
void (*hline )( bmp_t *bmp, int fst_x , int snd_x , int coor_y, int color);
void (*vline )( bmp_t *bmp, int coor_x, int fst_y , int snd_y , int color);
int error_code; // 에러 코드
int filesize; // ^ 이미지 파일 사이즈
int reserved; // | 예약 영역
int data_offset; // |
int header_size; // |
int img_width; // | 이미지 폭
int img_height; // | 이미지 높이
short cnt_planes; // | bmp 헤더 영역 52 bytes
short bpp; // |
int compression; // | 압축 형식
int bitmap_size; // |
int hres; // |
int vres; // |
int cnt_colors; // |
int important_colors; // v
int cnt_palette; // 팔레트 개수
palette_t *palette; // 팔레트 칼라 정보
int bytes_per_line;
unsigned char *data;
unsigned char *encoded_data;
unsigned bsize_blue , bsize_green , bsize_red;
unsigned boffset_blue, boffset_green, boffset_red;
};
첨부된 라이브러리 소스에는 get_pixel()만 완성했습니다. 또한 지원하느 칼라 깊이가 1, 4, 8, 16, 24, 36비트이므로 gxbmp.c에는 아래와 같은 함수가 준비되어 있습니다.
static void get_pixel_1( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
static void get_pixel_4( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
static void get_pixel_8( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
static void get_pixel_16( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
static void get_pixel_24( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
static void get_pixel_32( bmp_t *bmp, int coor_x, int coor_y, color_t *color)
{
}
그리고 Bitmap의 헤더 정보에서 칼라 깊이인 bpp정보를 구하고 이 정보에 따라 get_pixel() 함수를 지정해 줍니다.
bmp_t *gxbmp_open( char *file_name)
{
:
switch(bmp->bpp)
{
case 1:
bmp->get_pixel = get_pixel_1;
break;
case 4:
bmp->get_pixel = get_pixel_4;
break;
case 8:
bmp->get_pixel = get_pixel_8;
break;
case 16:
bmp->get_pixel = get_pixel_16;
break;
case 24:
bmp->get_pixel = get_pixel_24;
break;
case 32:
bmp->get_pixel = get_pixel_32;
break;
default:
bmp->error_code = GXERR_COLOR_DEPTH;
fclose(fp);
return bmp;
}
return bmp;
}
이렇게 함수가 준비되면 gx_bitblt()처럼 인수로 받은 dc로 각 도트의 칼라 값을 복사할 수 있습니다. 이와 같은 방법으로 이미지를 화면에 출력할 수 있습니다.
void gx_bitblt( dc_t *dc_dest, // 출력 목적지 Device Context Handle
int x_dest, // 출력 x 좌표
int y_dest, // 출력 y 좌표
dc_t *dc_sour, // 출력할 대상 Device Context Handle
int x_sour, // 출력할 대샹에서 실제 출력할 좌측 상단 x 좌료
int y_sour, // 출력할 대샹에서 실제 출력할 좌측 상단 y 좌료
int w_sour, // 출력할 대샹에서 실제 출력할 폭
int h_sour // 출력할 대샹에서 실제 출력할 높이
)
{
int coor_x;
int coor_y;
color_t color;
int color_value;
for ( coor_y = 0; coor_y < h_sour; coor_y++)
{
for ( coor_x = 0; coor_x < w_sour; coor_x++)
{
dc_sour->get_pixel( dc_sour, coor_x+x_sour, coor_y+y_sour, &color);
color_value = dc_dest->color( color.red, color.green, color.blue);
dc_dest->set_pixel( dc_dest, coor_x+x_dest, coor_y+y_dest, color_value);
}
}
}
Bitmap 소스 코드 제작자
중요한 말씀을 빠뜨릴 뻔했습니다. Bitmap을 처리하는 소스코드는 처음부터 모두 제가 만든 것이 아니라 전에 어디서 갈무리한 파일을 이용한 것입니다. 그 프로그램 소스는 Bitmap을 화면에 출력하는 방법이 소개되어 있는데, 그 소스 코드를 Device Context 핸들로 제어할 수 있도록 수정한 것입니다.
아쉽게도 그 소스를 만드신 분의 이름과 출처를 적어 놓지 못해서 아쉽네요. 대산에 그 소스 위에 헤더 부분을 소개합니다.
/* * bmplib.c : bmp reading library * only little endian machine supported * * Copyright(C) 2002 holelee * */
현재 이 소스에 대해 완벽히 이해를 못하고 부분부분 소화하지 못한 부분이 많습니다만 앞으로 업그레이드해 가면서 수정해 나가도록 하겠습니다.



holelee 아이디를 쓰시는 분은 이규명님이시구요. 예전에 KELP에서 활동하시던 분이네요.