그래픽 & 멀티미디어
프레임 버퍼가 무엇인지 알았으니 이제는 써 먹어야겠죠....ㅋㅋ
이전 시간에 프레임 버퍼를 설정하는 말씀을 드렸습니다만 다시 한번 프레임 버퍼 설정에 대해 조금 자세한 말씀을 올리겠습니다. 우선 프레임 버퍼 설정이 제대로 되었는지 보겠습니다.
프레임 버퍼 속성값 확인
첫 번째 방법이 시스템이 부팅했을 때의 메시지를 검색해 보는 것입니다. 리눅스 초보탈출 1 글을 보신 분은 걱정하실 필요가 없으시죠. 글을 보시지 않았거나 dmesg 명령을 모르시는 분은 시스템을 재 부팅하시고 눈을 부릅뜨고 화면에 출력되는 부팅 메시지를 확인해 보세요....ㅋㅋ 농담이구요, ]#dmesg | grep fb 명령을 실행합니다.
]$ dmesg | grep fb
vesafb: framebuffer at 0xf8000000, mapped to 0xd0880000, size 1200k
vesafb: mode is 640x480x16, linelength=1280, pages=0
vesafb: protected mode interface info at 00ff:44f0
vesafb: scrolling: redraw
vesafb: Truecolor: size=0:5:6:5, shift=0:11:5:0
fb0: VESA VGA frame buffer device
]$
요렇게 문자열이 출력된다면 시스템에 프레임 버퍼가 제대로 세팅되어 있는 것입니다. 이렇게 문자열이 출력되지 않는다면 리눅스 그래픽 - 프레임 버퍼 글을 다시 한 번 봐 주십시오. 출력된 내용을 제가 알고 있는 범위 내에서 설명을 드리겠습니다. 아직 제가 내공이 낮아서 정확하지는 않을 수 있고 틀릴 수 있습니다.
계속 내공을 쌓아 가면서 정확하지 않은 내용과 틀린 내용이 있으면 확인이 되는 대로 이 문서를 수정해 가겠습니다. 또한 이 글을 보시는 손님 중에 틀린 부분이 있으면 꼭 댓글로 지적을 해 주시면 감사하겠습니다.
vesafb: framebuffer at 0xf8000000, mapped to 0xd0880000, size 1200k
프래임버퍼의 설정된 메모리 영역에 대한 정보인데, 저도 잘 모르겠습니다. 예상한다면 0xf800000000 주소에서 시작해서 1200K 크기의 메모리가 할당되어 있다는 얘기가 되는 것으로 생각됩니다.
vesafb: mode is 640x480x16, linelength=1280, pages=0
프레임퍼퍼의 화면 해상도에 대한 내용이네요. 가로 640 픽셀에 세로 480 픽셀, 그리고 16bit 칼라를 사용하면서 한 개 픽셀 행에 대해 1280byte 를 사용한다는 내용입니다.
예전 8 비트 컴퓨터에서는 게임화면을 빠르게 전환하기 위해 화면 하나를 다 채울 수 있는 메모리를 2개 또는 몇 개를 준비해 놓았는데, 그것을 페이지라고 한다면 첫 페이지를 출력하고 있는 중에 두번째 페이지에 다음 출력할 화면을 그려 놓고 기다립니다. 그리고 나서 출력을 첫 번째와 두 번째를 바꾸면, 느린 컴퓨터에서도 빠르게 다음 화면으로 넘어 갈 수 있었습니다.
아마도 pages에 출력되는 정보는 이와 같은 가상 페이지가 몇 개인가를 나타 내는 것으로 생각됩니다.
vesafb: protected mode interface info at 00ff:44f0
vesafb: scrolling: redraw
이 정보도 정확히 무슨 정보인지는 모르겠네요. 챵피~
vesafb: Truecolor: size=0:5:6:5, shift=0:11:5:0
이 정보는 꼭 확인해야할 정보입니다. 칼라를 지정하는 방법인데요, 예를 들어 16비트 그래픽 환경에는 칼라를 설정하는 방법이 2가지가 있습니다.
R
|
G |
B |
5bit |
6bit |
5bit |
또 한 가지는,
사용 안함 | R | G | B |
1 bit | 5bit | 5bit | 5bit |
이렇게 16 bit 칼라 설정은 2 가지인데, 지금 프로그램이 실행되는 시스템은 어떤 16bit 칼라를 사용하는지 확인해야 합니다. 그래서 size=0:5:6:5 에서 RGB 각 칼라별 비트 크기를, shift=0:11:5:0 은 RGB 칼라 값을 만드는데, 각 칼라 값을 만들려면 옆으로 얼마만큼 쉬프트해야 하는지에 대한 정보입니다.
fb0: VESA VGA frame buffer device
실제 프레임 버퍼를 사용하기 위한 장치 이름을 보여 줍니다. /dev 디렉토리에서 fb 정보를 확인해 볼까요.
]$ ls -al /dev/fb*
lrwxrwxrwx 1 root root 3 4월 4 2007 /dev/fb -> fb0
crwxrwxrwx 1 root root 29, 0 4월 4 2007 /dev/fb0
[jwjw@jwCentOS 020 fb]$
프로그램에서는 보통 /dev/fb를 사용하겠습니다만 그것은 링크 이름이고 실제 장치명은 /dev/fb0 가 되겠습니다.
프레임 버퍼로 선 긋기
역시 프레임 버퍼 장치인 /dev/fb 를 파일로 처리할 수 있습니다. 바로 확인해 볼까요? fb의 사용권한은 root 에게 있으니 root 계정으로 로그인하시거나 fb의 접근 권한을 아래와 같이 수정해 주세요.
]# chmod 777 /dev/fb
]#
우리가 파일 목록을 어떤 파일에 저장하고 싶으면 아래와 같이 출력 방향을 파일로 바꾸어 실행하면 됩니다.
]# ls -al > lst
[root@jwCentOS ~]# cat lst
합계 236
drwxr-x--- 13 root root 4096 4월 4 14:50 .
drwxr-xr-x 24 root root 4096 4월 4 14:08 ..
-rw------- 1 root root 489 3월 30 09:24 .ICEauthority
-rw------- 1 root root 3003 4월 4 13:17 .bash_history
-rw-r--r-- 1 root root 24 2월 22 2005 .bash_logout
-rw-r--r-- 1 root root 191 2월 22 2005 .bash_profile
......
이와 같은 방법은로 출력 방향을 /dev/fb로 하면 어떻게 될까요?
실행해 보시면 아시겠습니다만 화면 위에 이상한 점들이 출력되지요. 이것만 보시더라도 프레임 버퍼를 이용하는 방법은 파일을 처리하듯 하시면 됩니다.
바로 짧은 프로그램을 보시겠습니다. 아래의 예는 아직 칼라값을 제대로 처리하지 않았습니다. 이 한 번의 글로 모두 설명 드리기에는 처음부터 너무 복잡할 것 같아서요. 다음 시간에 칼라 값을 다루는 내용을 담겠습니다. 일단 프레임 버퍼를 파일처럼 처리하여 화면에 선을 긋는 것을 보여 드리는 예를 자세히 봐 주십시오.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // open/close #include <fcntl.h> // O_RDWR #include <sys/ioctl.h> // ioctl #include <sys/mman.h> // mmap PROT_ #include <linux/fb.h> int main( int argc, char **argv) { int screen_width; int screen_height; int bits_per_pixel; int line_length; int fb_fd; struct fb_var_screeninfo fbvar; struct fb_fix_screeninfo fbfix; int ndx; unsigned short color; if ( access( "/dev/fb", F_OK)) { printf( "프레임 버퍼 장치가 없습니다.n"); return 0; } if ( 0 > ( fb_fd = open( "/dev/fb", O_RDWR))) { printf( "프레임 버퍼에 접근할 수 없습니다.n"); return 0; } if ( ioctl( fb_fd, FBIOGET_VSCREENINFO, &fbvar)) { printf( "FBIOGET_VSCREENINFO를 실행하지 못했습니다.n"); return 0; } if ( ioctl( fb_fd, FBIOGET_FSCREENINFO, &fbfix)) { printf( "FBIOGET_FSCREENINFO 실행하지 못했습니다.n"); return 0; } screen_width = fbvar.xres; // 스크린의 픽셀 폭 screen_height = fbvar.yres; // 스크린의 픽셀 높이 bits_per_pixel = fbvar.bits_per_pixel; // 픽셀 당 비트 개수 line_length = fbfix.line_length; // 한개 라인 당 바이트 개수 printf( "screen width = %dn", screen_width ); printf( "screen height = %dn", screen_height ); printf( "bits per pixel = %dn", bits_per_pixel); printf( "line length = %dn", line_length ); color = 0xffff; for ( ndx = 0; ndx < 100; ndx++) { lseek( fb_fd, 0, SEEK_SET); lseek( fb_fd, ndx * line_length +ndx *(bits_per_pixel / 8), SEEK_SET); write( fb_fd, &color, 2); } for ( ndx = 0; ndx < screen_width-100; ndx++) { write( fb_fd, &color, 2); } close( fb_fd); return 0; }
프로그램 설명
먼저 변수 선언입니다.
int screen_width; // 화면의 픽섹 폭을 구합니다. int screen_height; // 화면의 픽셀 높이를 구합니다. int bits_per_pixel; // 픽셀 당 비트수를 구합니다. int line_length; // 픽셀 한 행에 대해 총 몇개의 바이트로 구성되어 있는지를 구합니다. int fb_fd; // 프레임 버퍼에 대한 파일 디스크립터입니다. struct fb_var_screeninfo fbvar; // 프레임의 가변 정보를 구합니다. struct fb_fix_screeninfo fbfix; // 프레임의 고정 정보를 구합니다. int ndx; // for loop 를 위한 인덱스입니다. unsigned short color; // 카라값을 지정하기 위한 변수입니다.
fb_var_screeninfo의 가변 정보와 fb_fix_screeninfo 고정 정보의 차이점은 fb_var_screeninfo의 가변 정보 같은 경우에는 해상도나 색상 깊이 같은 내용을 요구에 따라 바꾸어 줄 수 있다는 점입니다.
다음은 /dev/fb 가 존재하는 지를 확인합니다.
if ( access( "/dev/fb", F_OK)) { printf( "프레임 버퍼 장치가 없습니다.n"); return 0; }
장치가 있다면 파일을 처리하듯이 open합니다.
if ( 0 > ( fb_fd = open( "/dev/fb", O_RDWR))) { printf( "프레임 버퍼에 접근할 수 없습니다.n"); return 0; }
장치 열기에 성공했다면 화면 해상도와 칼라 깊이와 같은 정보를 구합니다.
if ( ioctl( fb_fd, FBIOGET_VSCREENINFO, &fbvar)) { printf( "FBIOGET_VSCREENINFO를 실행하지 못했습니다.n"); return 0; } if ( ioctl( fb_fd, FBIOGET_FSCREENINFO, &fbfix)) { printf( "FBIOGET_FSCREENINFO 실행하지 못했습니다.n"); return 0; }
스크린 정보를 변수에 대입하고 화면에 정보를 출력합니다.
screen_width = fbvar.xres; // 스크린의 픽셀 폭 screen_height = fbvar.yres; // 스크린의 픽셀 높이 bits_per_pixel = fbvar.bits_per_pixel; // 픽셀 당 비트 개수 line_length = fbfix.line_length; // 한개 라인 당 바이트 개수 printf( "screen width = %dn", screen_width ); printf( "screen height = %dn", screen_height ); printf( "bits per pixel = %dn", bits_per_pixel); printf( "line length = %dn", line_length );
아직 칼라 값을 지정하는 방법은 말씀을 드리지 않았습니다. 우선 모두 흰색으로 선을 긋도록 하겠습니다.
color = 0xffff; // 백색
이제 선을 긋는 방법인데, 조금 설명을 올리겠습니다. 화면 해상도가 640x480 이라면 화면은 작은 픽섹을 찍을 수 있는 셀 들의 집합이며, 아래와 같은 모양으로 생각할 수 있습니다.
(0,0) | (1,0) | (2,0) | (3,0) | ..... | (639,0) |
(0,1) | (1,1) | (2,1) | |||
(0,3) | (1,3) | ||||
(0,4) | |||||
(639,479) |
또한 프레임 버퍼가 파일이며, 이 파일을 가로로 주욱 늘려 놓았다면 아래와 같은 모양이 될 것입니다.
(0,0) | (1,0) | (2,0) | (3,0) | ..... | (639,0) | (0,1) | (1,1) | (2,1) | ..... | (0,3) | (1,3) | (2,3) | ..... | (639,479) |
즉, 2차원 모양의 각 행을 옆으로 늘려 붙여 놓은 형태가 됩니다. 그러므로 선을 긋기 위해서는 그 위치에 해당되는 위치에 칼라값을 써 주기만 하면 됩니다.
각 셀은 픽셀 하나로 칼라 깊이에 따라 1바이트, 2바이트, 3바이트 또는 4바이트가 될 수 있습니다. 그러므로 2바이트, 즉 16비트 칼라에서는 한개의 셀이 2바이트이며, 줄 한개의 행은 (가로 해상도 x 필셀당 비트수) 바이트가 되겠습니다.
(0,0) | (1,0) | (2,0) | (3,0) | ..... | (639,0) |
(0,1) | (1,1) | (2,1) | |||
(0,3) | (1,3) | ||||
(0,4) | |||||
(639,479) |
예로 640x480 에 16비트 칼라라면 (0,0) 은 당연히 첫 바이트 위치이구요, ( 1,0)은 세번째 바이트 위치가 되겠습니다. (1,0)은 1280 바이트 위치가 되므로 아래와 같은 공식을 생각할 수 있습니다.
(X, Y) 좌료는,
한줄의 바이트 개수 x Y + X * (픽셀당 비트수 / 8)
자, 이제 프로그램 코드를 보시겠습니다.
for ( ndx = 0; ndx < 100; ndx++) { lseek( fb_fd, 0, SEEK_SET); lseek( fb_fd, ndx * line_length +ndx *(bits_per_pixel / 8), SEEK_SET); write( fb_fd, &color, 2); }
파일 위치를 0, SEEK_SET 을 사용하여 파일 포인터를 첫 번째 바이트로 이동했습니다.
ndx * line_length +ndx *(bits_per_pixel / 8)
그리고 ndx * line_length 로 Y 좌표에 해당하는 바이트 위치를 구하고, 다시 ndx * (셀당, 즉 픽셀 당 바이트 수) 를 곱해서 X 좌표의 값을 구한 후, 서로 더했습니다. 그 위치로 SEEK_SET 을 사용하여 파일 포인터를 이동한 후,
write( fb_fd, &color, 2);
칼라값 2 바이트를 쓰기를 했습니다. X, Y 모두 0 부터 99 까지 같은 값으로 증가하면서 칼라 값을 넣었으므로 사전이 그어집니다.
for ( ndx = 0; ndx < screen_width-100; ndx++) { write( fb_fd, &color, 2); }
또한 그 위치에서 쓰기를 계속하면 X 좌표에 대해서 자동으로 증가하는 꼴이기 때문에 사선 후에는 수평선이 그어질 것입니다.
실행 화면을 담아 보았습니다.
이번 시간에는 그래픽을 출력하기 위해서 프레임 버퍼를 파일처럼 처리하는 방법에 대해 알아 보았습니다. 그러나 lseek()와 write()함수를 사용하는 방법은 아무래도 복잡하고 어렵죠. 다음 시간에는 메모리 매핑을 이용하여 프레임 버퍼를 파일로 열어 사용하지만 마치 메모리를 이용하는 것처럼 처리하는 방법에 대해 알아 보겠습니다.
태그: *프레임 버퍼 *그래픽
버그는 아니지만... 24bit와 같은 다른 색깔수에서도 정상동작하게 하려면
write( fb_fd, &color, 2); 를
write( fb_fd, &color, bits_per_pixel / 8); 로 바꾸는 것이 좋을 듯 합니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // open/close
#include <fcntl.h> // O_RDWR
#include <sys/ioctl.h> // ioctl
#include <sys/mman.h> // mmap PROT_
#include <linux/fb.h>