그래픽 & 멀티미디어
gx 라이브러리 기본 구조에 맞추어 선을 그리는 루틴을 완셩해 보았습니다. 작업 시작이 얼마 되지 않아서 소스는 16비트 칼라에 대해서 직선을 그리는 부분까지 완성했습니다.
*** 참고로 선을 그리는 루틴은 이전에 브레슨햄을 소개하면서 만든 코드와는 달리 조금더 빠르게 실행이 되도록 최적화 했습니다. 역시 수학하시는 분들의 힘은 대단합니다. 선을 그리는 루틴도 함께 봐 주십시오. ^^ ***
앞으로 파일 구조는 아래와 같은 형식으로 만들어져 갈 것입니다.
파일 이름 | 파일 설명 |
gx.*** |
전체 그래픽 라이브러리, 사용자는 이 파일만 참조하여 사용합니다. |
gx8.*** | 8bit 칼라, 즉 256 칼라에 대해서만 처리합니다. |
gx15.*** | 15bit 칼라에 대해서만 처리합니다. |
gx16.*** | 16bit 칼라에 대해서만 처리합니다. |
gx24.*** | 24bit 칼라에 대해서만 처리합니다. |
gx32.*** | 32bit 칼라에 대해서만 처리합니다. |
아래의 소스를 보시면 아시겠습니다만, gx[NNN] .h gx[NNN] .c 는 칼라 계산, 좌표에 맞추어 점을 찍거나 조금 더 빠르게 선을 그리기 위한 수직 및 수직선을 그리는 등의 칼라 깊이에 종속이 되는 함수를 모아 놓은 것입니다. 그리고 gx.h 와 gx.c 에서는 이들 함수를 캡슐화하듯 대신 호출하는 식으로 구현하는 것입니다.
그래픽 라이브러리 초기화 부분은 앞서서 많이 말씀을 드렸기 때문에 생략하겠습니다. 우선 gx16.h 와 gx16.c를 봐 주십시오. 아래와 같이 16bit 칼라 전용 함수가 선언 및 구현되어 있습니다.
int gx16_color ( int red, int green, int blue); void gx16_clear ( dc_t *dc, int color); void gx16_dot ( dc_t *dc, int coor_x, int coor_y, int color ); void gx16_hline ( dc_t *dc, int fst_x , int snd_x , int coor_y); void gx16_vline ( dc_t *dc, int coor_x, int fst_y , int snd_y );
앞으로 8bit, 24bit, 32bit 도 이와 같은 모습으로 만들어질 것입니다.
또한 이 함수들은 인수를 보시면 아시겠지만 그래픽이 출력되는 목적지는 dc 입니다. dc 가 화면이면 화면으로, dc가 가상 캔버스면 캔버스, 비트맵이면 비트맵으로 방향이 선택되게 됩니다.
우선 공개되는 함수에서는 화면의 dc를 구하는 gx_screen_dc() 가 구현되어 있습니다.
dc_t *gx_screen_dc( void) { dc_t *dc; dc = malloc( sizeof( dc_t)); if ( NULL != dc) { dc->width = gx_fb.width; dc->height = gx_fb.height; dc->dots = gx_fb.dots; dc->mapped = gx_fb.mapped; dc->coor_x = 0; dc->coor_y = 0; dc->pen_color = clr_black; dc->brush_color= clr_black; printf( "colors=%dn", gx_fb.colors); switch( gx_fb.colors) // 칼라 깊이별 처리가 다른 함수를 지정 { case 16 : dc->color = gx16_color; dc->clear = gx16_clear; dc->dot = gx16_dot; dc->hline = gx16_hline; dc->vline = gx16_vline; break; default : free( dc); dc = NULL; } } return dc; }
switch( gx_fb.colors) 문을 보시면 아시겠지만 칼라 깊이에 맞추어 dc에 대해 전문적으로 처리할 전용 함수를 지정해 주며, 앞으로 gx_****( dc, ...) 에 대해서는 모두 dc 안에 있는 전문 함수가 처리하므로, 라이브러리를 사용하는 사용자는 gx.h 에 선언된 함수만 사용하셔도 모든 그래픽 해상도에 대한 프로그램을 처리할 수 있습니다.
그래픽 라이브러리에 대해서는 gx.h만 사용하시면 됩니다.
#include// graphic library
제일 먼저 그래픽 라이브러리를 초기화해서 시스템에 맞추어 출력을 준비할 수 있도록 합니다.
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_init() 호출 후에 GXERR_NONE 일 경우 그래픽 작업을 수행합니다. 화면에 무엇인가를 출력하기 위해서는 Device Context 를 구해야 합니다. gx_screen_dc()입니다.
if ( NULL != ( dc = gx_screen_dc())) { // 화면에 출력 작업 } else { printf( "스크린 dc를 얻지 못했습니다.n"); }
이후로는 dc를 이용하여 그래픽을 출력합니다.
gx_clear( dc, gx_color( dc, 0, 0, 0)); dc->pen_color = gx_color( dc, 255, 255, 255); gx_line( dc, 0 , 0 , ndx, 200); gx_lineto( dc, 200+rand() % offset_x, 200+rand() % offset_y);
이제 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 // FBIOGET_VSCREENINFO를 구하지 못함 #define GXERR_FSCREENINFO -4 // FBIOGET_FSCREENINFO를 구하지 못함 #define GXERR_MEMORYMAPPING -5 // 프레임 버퍼 메모리 매핑에 실패 #define clr_clear 0x8000000 // 출력하지 않는 색상 #define clr_black 0x00 // 검은 색 typedef struct framebuffer_t_ framebuffer_t; struct framebuffer_t_ { int fd; // 프레임 버퍼에 대한 파일 디스크립터 int width; int height; int bytes; // 메모리 전체 크기 int dots; int colors; unsigned short *mapped; // 메모리 매핑된 포인터 }; typedef struct dc_t_ dc_t; struct dc_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)( dc_t *dc, int color); void (*dot )( 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); void (*vline)( dc_t *dc, int coor_x, int fst_y , int snd_y); }; extern framebuffer_t gx_fb; // 프레임 버퍼 정보 extern int gx_init ( char *dev_name); // 그래픽 라이브러리 초기화 extern void gx_close ( void); // 그래픽 라이브러리 사용 종료 extern dc_t *gx_screen_dc ( void); // 화면 dc를 구함 extern void gx_release_dc ( dc_t *dc); // dc 를 소멸 extern int gx_color ( dc_t *dc, int red, int green, int blue); // 칼라 값을 구함 extern void gx_clear ( dc_t *dc, int color); // dc를 지정한 색으로 모두 채움 extern void gx_dot ( dc_t *dc, int coor_x, int coor_y, int color); // dc에 점을 찍음 extern void gx_moveto( dc_t *dc, int coor_x, int coor_y); // dc의 좌표를 이동 extern void gx_lineto( dc_t *dc, int coor_x, int coor_y); // dc의 좌표에 새 좌표까지 선을 그림 extern void gx_line ( dc_t *dc, int x1, int y1, int x2, int y2); // dc에 선을 그림 #endif
#include#include // malloc srand #include // abs #include // open/close #include // O_RDWR #include // ioctl #include // mmap PROT_ #include #include #include framebuffer_t gx_fb; // 프레임 버퍼 정보 //-------------------------------------------------------------------- // 설명: dc에 맞추어 칼라 값을 구한다. // 인수: dc Device Context // red 0~255 사이의 빨강색 비율 // green 0~255 사이의 녹색 비율 // blue 0~255 사이의 파랑색 비율 //-------------------------------------------------------------------- int gx_color( dc_t *dc, int red, int green, int blue) { return dc->color( red, green, blue); } //-------------------------------------------------------------------- // 설명: dc에 인수로 받은 색상으로 전체 칠함 // 인수: dc Device Context // color 칠할 색상 //-------------------------------------------------------------------- void gx_clear( dc_t *dc, int color) { dc->clear( dc, color); } //-------------------------------------------------------------------- // 설명: dc에 점을 찍습니다. // 인수: dc Device Context // coor_x x 좌표 // coor_y y 좌표 // color 점 색상 //-------------------------------------------------------------------- void gx_dot ( dc_t *dc, int coor_x, int coor_y, int color) { dc->dot( dc, coor_x, coor_y, color); } //-------------------------------------------------------------------- // 설명: dc의 좌표를 이동 // 인수: dc Device Context // coor_x x 좌표 // coor_y y 좌표 //-------------------------------------------------------------------- void gx_moveto( dc_t *dc, int coor_x, int coor_y) { dc->coor_x = coor_x; dc->coor_y = coor_y; } //-------------------------------------------------------------------- // 설명: dc의 좌표와 새로 인수로 받은 좌표와 선을 그림 // 인수: dc Device Context // coor_x x 좌표 // coor_y y 좌표 // 참고: 인수로 받은 좌료를 dc 좌료로 대입 //-------------------------------------------------------------------- void gx_lineto( dc_t *dc, int coor_x, int coor_y) { int rx, ry; int dx, dy; int inc_x; int inc_y; int offset; int color; rx = dc->coor_x; // dc 좌료 값 ry = dc->coor_y; dc->coor_x = coor_x; // dc 좌료를 인수로 받은 값으로 대입 dc->coor_y = coor_y; dx = coor_x -rx; // x 좌표에 대한 delta 값 if ( 0 == dx) // 수식선을 그린다면 { dc->vline( dc, rx, ry, coor_y); return; } else if ( 0 < dx) inc_x = 1; // x 좌표가 우측으로 증가하는 직선이라면 else // x 좌표가 좌측으로 감소하는 직선이라면 { dx = -dx; inc_x = -1; } dy = coor_y -ry; // y 좌표에 대한 delta 값 if ( 0 == dy) // 수평선을 그린다면 { dc->hline( dc, rx, coor_x, ry); return; } else if ( 0 < dy) inc_y = 1; // y 좌료가 밑으로 증가하는 직선이라면 else // y 좌료가 위로 감소하는 직선이라면 { dy = -dy; inc_y = -1; } color = dc->pen_color; // 선의 색깍을 구함 gx_dot( dc, rx, ry, color); // 첫번째 좌표를 찍음 if ( dy <= dx) // x 좌료 변도이 더 큼, x 좌표는 1씩 변화 { offset = dx / 2; for (; rx != coor_x; rx += inc_x) { offset += dy; if ( dx <= offset) { offset -= dx; ry += inc_y; } gx_dot( dc, rx, ry, color); } } else // y 좌료 변동이 더 큼, y 좌표는 1씩 변화 { offset = dy /2; for (; ry != coor_y; ry += inc_y) { offset += dx; if ( dy <= offset) { offset -= dy; rx += inc_x; } gx_dot( dc, rx, ry, color); } } } //-------------------------------------------------------------------- // 설명: 선을 그린다. // 인수: x1, y1, x2, y2 선을 그리기 위한 좌표 // 참고: x2, y2는 dc 좌표가 dc 좌료가 된다. //-------------------------------------------------------------------- void gx_line( dc_t *dc, int x1, int y1, int x2, int y2) { gx_moveto( dc, x1, y1); gx_lineto( dc, x2, y2); } //-------------------------------------------------------------------- // 설명: Device Context 자원을 반환 //-------------------------------------------------------------------- void gx_release_dc( dc_t *dc) { if ( NULL != dc) free( dc); } //-------------------------------------------------------------------- // 설명: 화면에 그래픽을 출력하기 위한 Device Context를 구합니다. // 반환: 정상 -> dc_t *, 오류 -> NULL // 참고: 프레임 버퍼에서 화면에 대한 모든 정보를 구합니다. //-------------------------------------------------------------------- dc_t *gx_screen_dc( void) { dc_t *dc; dc = malloc( sizeof( dc_t)); if ( NULL != dc) { dc->width = gx_fb.width; dc->height = gx_fb.height; dc->dots = gx_fb.dots; dc->mapped = gx_fb.mapped; dc->coor_x = 0; dc->coor_y = 0; dc->pen_color = clr_black; dc->brush_color= clr_black; printf( "colors=%dn", gx_fb.colors); switch( gx_fb.colors) // 칼라 깊이별 처리가 다른 함수를 지정 { case 16 : dc->color = gx16_color; dc->clear = gx16_clear; dc->dot = gx16_dot; dc->hline = gx16_hline; dc->vline = gx16_vline; break; default : free( dc); dc = NULL; } } return dc; } //-------------------------------------------------------------------- // 설명: 그래픽 라이브러리 종료 // 참고: 메모리 매핑 포인터와 프레임 버퍼 디스크립터를 소멸하고, // 변수를 초기화 한다. //-------------------------------------------------------------------- void gx_close( void) { if ( 0 <= gx_fb.mapped) // 메모리가 메핑 되어 있다면 { munmap( gx_fb.mapped, gx_fb.bytes); // 메모리 메핑 자원 반환 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 bytes_per_line; int bits_per_pixel; 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; } gx_fb.width = fbvar.xres; // 스크린의 픽셀 폭 gx_fb.height = fbvar.yres; // 스크린의 픽셀 높이 gx_fb.dots = gx_fb.width * gx_fb.height; // 스크린 도트 개수 bits_per_pixel = fbvar.bits_per_pixel; // 스크린의 칼라 깊이 bytes_per_line = fbfix.line_length; // 한개 라인 당 바이트 개수 gx_fb.bytes = bytes_per_line * gx_fb.height; // 스크린의 총 메모리 바이트 수 gx_fb.mapped = ( void *)mmap( 0, gx_fb.bytes, PROT_READ|PROT_WRITE, MAP_SHARED, gx_fb.fd, 0); // 칼라 깊이 정보를 구합니다. // 16 bit에서 각 R,G,B의 비트 개수를 합해 // 정확히 구합니다. switch( bits_per_pixel) { case 16 : gx_fb.colors = fbvar.red.length +fbvar.green.length +fbvar.blue.length; break; default : gx_fb.colors = bits_per_pixel; } if ( 0 > gx_fb.mapped) { gx_close(); return GXERR_MEMORYMAPPING; } return GXERR_NONE; }
#ifndef _GX_16_H_ #define _GX_16_H_ #include// 16 bit 칼라 함수를 선언 extern int gx16_color ( int red, int green, int blue); // 색상을 구함 extern void gx16_clear ( dc_t *dc, int color); // 인수로 받은 색상으로 전체 칠하기 extern void gx16_dot ( dc_t *dc, int coor_x, int coor_y, int color );// 점 찍기 extern void gx16_hline ( dc_t *dc, int fst_x , int snd_x , int coor_y);// 수평선 그리기 extern void gx16_vline ( dc_t *dc, int coor_x, int fst_y , int snd_y );// 수직선 그리기 #endif
#include#include #include // abs #include // open/close #include // O_RDWR #include // ioctl #include // mmap PROT_ #include #include //-------------------------------------------------------------------- // 설명: 칼라 값을 구한다. // 인수: red 0부터 255 사이의 red 값 // green 0부터 255 사이의 green 값 // blue 0부터 255 사이의 blue 값 // 참고: 16 bit 환경에 따라 shift=0:5:6:5, shift=0:11:5:0 //-------------------------------------------------------------------- int gx16_color( int red, int green, int blue) { red >>= 3; // 8bit 인수 값을 칼라 bit 크기 만큼 축소 green >>= 2; // 8bit 인수 값을 칼라 bit 크기 만큼 축소 blue >>= 3; // 8bit 인수 값을 칼라 bit 크기 만큼 축소 return ( red << 11) | ( green << 5) | blue; } //-------------------------------------------------------------------- // 설명: 스크린을 특정 칼라로 채움 // 인수: color 칼라 //-------------------------------------------------------------------- void gx16_clear( dc_t *dc, int color) { int ndx; unsigned short *ptr; if (clr_clear > color) { ptr = (unsigned short *)dc->mapped; for ( ndx = 0; ndx < dc->dots; ndx++) *ptr++ = color; } } //-------------------------------------------------------------------- // 설명: 점을 찍는다. // 인수: coor_x x 좌표 // coor_y y 좌표 // color 점 색상 // 참고: 좌표 값을 확인하여 엉뚱한 좌료가 입력되도 // 실행 에러가 발생하지 않도록 한다. //-------------------------------------------------------------------- void gx16_dot( dc_t *dc, int coor_x, int coor_y, int color) { unsigned short *ptr; if ( 0 > coor_x || dc->width <= coor_x) return; if ( 0 > coor_y || dc->height <= coor_y) return; ptr = (unsigned short *)dc->mapped +dc->width * coor_y + coor_x; *ptr = color; } //-------------------------------------------------------------------- // 설명: 수평선을 그린다. // 인수: fst_x, snd_x x 좌표 // coor_y y 좌표 // color 선 색상 // 반환: //-------------------------------------------------------------------- void gx16_hline( dc_t *dc, int fst_x, int snd_x, int coor_y) { unsigned short *ptr; int color; int ndx; if ( 0 > coor_y || dc->height <= coor_y) // y 좌료가 스크린 영역을 벗어나면 복귀 return; if ( snd_x < fst_x) // x 좌표가 다음 좌표보다 작게 수정 { ndx = fst_x; fst_x = snd_x; snd_x = ndx; } if ( 0 > fst_x) fst_x = 0; // 좌측 화면 밖의 좌표이면 0부터 그린다. else if ( dc->width <= fst_x) fst_x = dc->width-1;// 우측 화면 밖의 좌표이면 우측 화면까지만 그린다. if ( 0 > snd_x) snd_x = 0; else if ( dc->width <= snd_x) snd_x = dc->width-1; color = dc->pen_color; ptr = (unsigned short *)dc->mapped + dc->width * coor_y +fst_x; for ( ndx = fst_x; ndx <= snd_x; ndx++) *ptr++ = color; } //-------------------------------------------------------------------- // 설명: 수직선을 그린다. // 인수: coor_x x 좌표 // fst_y, snd_y y 좌표 // color 선 색상 // 반환: //-------------------------------------------------------------------- void gx16_vline( dc_t *dc, int coor_x, int fst_y, int snd_y) { unsigned short *ptr; int color; int ndx; if ( 0 > coor_x || dc->width <= coor_x) // x 좌료가 스크린 영역을 벗어나면 복귀 return; if ( snd_y < fst_y) // y 좌표가 다음 좌표보다 작게 수정 { ndx = fst_y; fst_y = snd_y; snd_y = ndx; } if ( 0 > fst_y) fst_y = 0; // 화면 상단 밖의 좌표이면 0부터 그린다. else if ( dc->height <= fst_y) fst_y = dc->height-1; // 화면 하단 밖의 좌표이면 우측 화면까지만 그린다. if ( 0 > snd_y) snd_y = 0; else if ( dc->height <= snd_y) snd_y = dc->height-1; color = dc->pen_color; ptr = (unsigned short *)dc->mapped + dc->width * fst_y +coor_x; for ( ndx = fst_y; ndx <= snd_y; ndx++) { *ptr = color; ptr += dc->width; } }
#include#include #include #include // graphic library #include // sleep void test( void) { dc_t *dc; int offset_x, offset_y; int ndx; srand( (unsigned)time(NULL)); if ( NULL != ( dc = gx_screen_dc())) { gx_clear( dc, gx_color( dc, 0, 0, 0)); dc->pen_color = gx_color( dc, 255, 255, 255); for( ndx = 0; ndx < dc->height; ndx +=10){ gx_line( dc, 0 , 0 , ndx, 200); gx_line( dc, 0 , 0 , 200, ndx); gx_line( dc, ndx, 0 , ndx, 200); gx_line( dc, 0 , ndx, 200, ndx); } offset_x = dc->width -200; offset_y = dc->height -200; gx_moveto( dc, 200+rand() % offset_x, 200+rand() % offset_y); for ( ndx= 0; ndx < 100; ndx++) { dc->pen_color = gx_color( dc, rand() %128 +128, rand() %128 +128, rand() %128 +128); gx_lineto( dc, 200+rand() % offset_x, 200+rand() % offset_y); } } else { printf( "스크린 dc를 얻지 못했습니다.n"); } } 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; }
실행하면 아래와 같은 화면이 출력됩니다.
태그: *그래픽 *라이브러리 *그래픽라이브러리