그래픽 & 멀티미디어
이번에는 그래픽 라이브러리에 이미지를 출력하는 함수를 추가했습니다. 가장 기본이되는 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에서 활동하시던 분이네요.