이번에는 그래픽 라이브러리에 이미지를 출력하는 함수를 추가했습니다. 가장 기본이되는 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
 *
 */

현재 이 소스에 대해 완벽히 이해를 못하고 부분부분 소화하지 못한 부분이 많습니다만 앞으로 업그레이드해 가면서 수정해 나가도록 하겠습니다.