이번 시간부터 리눅스 그래픽 라이브러리 gx를 만들어 가도록 하겠습니다. 혹, 좋은 아이디어나 지적 사항이 있으시면 말씀 주시고, 그래픽 라이브러리를 위한 참고 자료를 알려 주신다면 감사하겠습니다.

gx 라이브러리 기본 구조

함수 및 변수 명명법

gx 라이브러리(이하 gx) 에서의 함수명은 모두 gx_ 로 시작합니다. 변수는 gx_ 접두사 사용을 원칙으로 하지만 특성에 따라, 또는 사용 편의에 따라 다른 접두사를 사용할 수 있습니다.

Device Context 개념

gx는 그래픽 함수를 다양하게 사용할 수 있도록 Device Context, 그래픽 출력의 기본 단위 개념을 사용하려 합니다.

이렇게 하는 이유는 점이나 도형 그리기 함수를 화면뿐만 아니라 이미지나 가상 그래픽 장치에도 같은 방법으로 사용할 수 있기 때문입니다.

자세한 내용은 가상 그래픽 장치를 소개할 때 말씀을 올리겠습니다.

함수 포인터 사용

gx는 다양한 칼라 깊이에서 사용될 수 있도록 하려 합니다. 이렇게 환경에 따라 칼라 값을 계산하는 것부터 화면에 출력하는 방법까지 모두 지원해야 해야 합니다.

이것을 하나의 함수로 구현한다는 것은 너무 어렵고, 함수 실행만 느려지게할 뿐입니다.

이와 같은 요구와 문제를 해결하기 위해 칼라 깊이에 관계된 함수를 포인터 함수로 선언하고, 그래픽 함수를 초기화하는 루틴에서 해상도에 따라 포인터 함수를 지정 하도록 하겠습니다.

즉,

switch( bits_per_pixel)
{
15       :
               gx_color = gx15_color;
               gx_clear = gx15_clear;
               gx_dot   = gx15_dot;
32       :
               gx_color = gx32_color;
               gx_clear = gx32_clear;
               gx_dot   = gx32_dot;
default  :
               gx_color = gx16_color;
               gx_clear = gx16_clear;
               gx_dot   = gx16_dot;
}
  

이와 같이 라이브러리를 초기화할 때, 그래픽 환경에 맞추어 함수를 설정해 놓으면, 이후로는 칼라 깊이가 다르더라도 같은 함수로 처리가 가능합니다.

gx.h

#ifndef _GX_H_
#define _GX_H_

#define  GXERR_NONE              0           // 에러 없음
#define  GXERR_NO_DEVICE         -1          // 장치가 없음
#define  GXERR_ACCESS_DEVICE     -2          // 접근 권한이 없음
#define  GXERR_VSCREENINFO       -3
#define  GXERR_FSCREENINFO       -4
#define  GXERR_MEMORYMAPPING     -5

#define  clr_clear               0x8000000
#define  clr_black               0x00

extern   int   clr_white;
extern   int   clr_magenta;
extern   int   clr_green;
extern   int   clr_olive;
extern   int   clr_navy;
extern   int   clr_purple;
extern   int   clr_teal;
extern   int   clr_gray;
extern   int   clr_silver;
extern   int   clr_red;
extern   int   clr_lime;
extern   int   clr_yellow;
extern   int   clr_blue;
extern   int   clr_fuchsia;
extern   int   clr_aqua;

typedef struct rgb_bit_t_ rgb_bit_t;
struct rgb_bit_t_
{
   int   offset;
   int   bit_value;
};

typedef struct framebuffer_t_ framebuffer_t;
struct framebuffer_t_
{
   int      fd;                           // 프레임 버퍼에 대한 파일 디스크립터
   int      size;                         // 메모리 전체 크기
   rgb_bit_t  red;
   rgb_bit_t  green;
   rgb_bit_t  blue;
   unsigned short *mapped;                // 메모리 매핑된 포인터
};

typedef struct dc_t_ dc_t;
struct dc_t_
{
   int      width;
   int      height;
   int      size;
   int      coor_x;
   int      coor_y;
   int      pen_color;
   int      brush_color;
   unsigned short *mapped;                // 메모리 매핑된 포인터
};

extern   framebuffer_t  gxfb;
extern   dc_t           dc_screen;

extern int  gx_init( char *dev_name);
extern void gx_close( void);

extern int  (*gx_color)( int red, int green, int blue);
extern void (*gx_clear)( dc_t *dc, int color);
extern void (*gx_dot  )( dc_t *dc, int coor_x, int coor_y, int color);

#endif

gx.c

#include 
#include 
#include         // abs

#include         // open/close
#include          // O_RDWR
#include      // ioctl
#include       // mmap PROT_
#include 
#include 
#include 

int   clr_white;           // 자주 사용하는 칼라 변수
int   clr_magenta;
int   clr_green;
int   clr_olive;
int   clr_navy;
int   clr_purple;
int   clr_teal;
int   clr_gray;
int   clr_silver;
int   clr_red;
int   clr_lime;
int   clr_yellow;
int   clr_blue;
int   clr_fuchsia;
int   clr_aqua;

framebuffer_t  gx_fb;
dc_t           dc_screen;

int   (*gx_color)( int red, int green, int blue);
void  (*gx_clear)( dc_t *dc, int color);
void  (*gx_dot  )( dc_t *dc, int coor_x, int coor_y, int color);

//--------------------------------------------------------------------
// 설명: 그래픽 라이브러리 종료
// 참고: 메모리 매핑 포인터와 프레임 버퍼 디스크립터를 소멸하고,
//       변수를 초기화 한다.
//--------------------------------------------------------------------
void  gx_close( void)
{
   if ( 0 <= gx_fb.mapped)                // 메모리가 메핑 되어 있다면
   {
      munmap( gx_fb.mapped, gx_fb.size);  // 메모리 메핑 자원 반환
      gx_fb.mapped      = MAP_FAILED;
   }
   if ( 0 <= gx_fb.fd)                    // 파일 디스크립터가 만들어 졌다면
   {
      close( gx_fb.fd);                   // 파일 디스크립터 자원 반환
      gx_fb.fd = -1;
   }
}

//--------------------------------------------------------------------
// 설명: 그래픽 라이브러리를 초기화
// 인수: dev_name    장치 이름
// 반환: 0 =         에러 없음
//       0 >         에러 발생
//--------------------------------------------------------------------
int   gx_init( char *dev_name)
{
   struct   fb_var_screeninfo  fbvar;
   struct   fb_fix_screeninfo  fbfix;
   int      bits_per_pixel;
   int      bytes_per_line;

   gx_fb.fd       = -1;                                                 // 초기값을 대입
   gx_fb.mapped   = MAP_FAILED;                                         // 초기값을 대입

   if ( access( dev_name, F_OK))                            return GXERR_NO_DEVICE;
   if ( 0 >  ( gx_fb.fd = open( dev_name, O_RDWR))   )      return GXERR_ACCESS_DEVICE;
   if ( ioctl( gx_fb.fd, FBIOGET_VSCREENINFO, &fbvar))
   {
      gx_close();
      return GXERR_VSCREENINFO;
   }
   if ( ioctl( gx_fb.fd, FBIOGET_FSCREENINFO, &fbfix))
   {
      gx_close();
      return GXERR_FSCREENINFO;
   }

   dc_screen.width      = fbvar.xres;                                   // 스크린의 픽셀 폭
   dc_screen.height     = fbvar.yres;                                   // 스크린의 픽셀 높이
   dc_screen.size       = dc_screen.width * dc_screen.height;           // 스크린 도트 개수
   bits_per_pixel       = fbvar.bits_per_pixel;
   bytes_per_line       = fbfix.line_length;                            // 한개 라인 당 바이트 개수

   gx_fb.size           = bytes_per_line * dc_screen.height;
   gx_fb.mapped         = ( unsigned short *)mmap( 0,
                                             gx_fb.size,
                                             PROT_READ|PROT_WRITE,
                                             MAP_SHARED,
                                             gx_fb.fd,
                                             0);
   if ( 0 > gx_fb.mapped)
   {
      gx_close();
      return   GXERR_MEMORYMAPPING;
   }

   dc_screen.mapped     = gx_fb.mapped;


   switch( bits_per_pixel)
   {
   default     :
                  gx_color = gx16_color;
                  gx_clear = gx16_clear;
                  gx_dot   = gx16_dot;
   }

                                                                        // 자주 사용하는 칼라 값을 생성

   clr_white   = gx_color( 255, 255, 255);
   clr_magenta = gx_color( 128,   0,   0);
   clr_green   = gx_color(   0, 128,   0);
   clr_olive   = gx_color( 128, 128,   0);
   clr_navy    = gx_color(   0,   0, 128);
   clr_purple  = gx_color( 128,   0, 128);
   clr_teal    = gx_color(   0, 128, 128);
   clr_gray    = gx_color( 128, 128, 128);
   clr_silver  = gx_color( 192, 192, 192);
   clr_red     = gx_color( 255,   0,   0);
   clr_lime    = gx_color(   0, 255,   0);
   clr_yellow  = gx_color( 255, 255,   0);
   clr_blue    = gx_color(   0,   0, 255);
   clr_fuchsia = gx_color( 255,   0, 255);
   clr_aqua    = gx_color(   0, 255, 255);

   return   GXERR_NONE;
}

sample.c

sample.c 는 라이브러리의 사용 예를 보여 줍니다.

  1. gx_init( "/dev/fb") 로 그래픽 라이브러리를 초기화하고,
  2. 자동으로 생성된 화면 출력용 Device Context 인 dc_screen을 이용하여 그래픽을 출려합니다.
int   main( void)
{
   switch( gx_init( "/dev/fb") )
   {
      case  GXERR_NO_DEVICE      :  printf( "프레임 버퍼 장치가 없습니다.n"              ); return -1;
      case  GXERR_ACCESS_DEVICE  :  printf( "프레임 버퍼 장치 접근 권한이 없습니다.n"    ); return -1;
      case  GXERR_VSCREENINFO    :  printf( "FBIOGET_VSCREENINFO를 실행하지 못했습니다.n"); return -1;
      case  GXERR_FSCREENINFO    :  printf( "FBIOGET_FSCREENINFO를 실행하지 못했습니다.n"); return -1;
      case  GXERR_MEMORYMAPPING  :  printf( "메모리 메핑을 못했습니다.n"                 ); return -1;
      default                    :  test();
   }

   gx_close();
   return   0;
}

에러없이 gx_init()가 완료되었다면 dc_screen을 이용하여 그래픽을 출력합니다.

gx_clear( &dc_screen, clr_black);
gx_dot( &dc_screen, ndx, ndx, clr_yellow);

gx_clear()는 인수로 받은 Device Context에 대해 전체를 지정한 색으로 칠하고, gx_dot()는 인수로 받은 색으로 점을 찍습니다.

실행하게되면 아래와 같이 출력됩니다.

 태그: *그래픽 *라이브러리 *그래픽라이브러리