그래픽 & 멀티미디어
이번 시간에는 각 해상도 별로 칼라 값을 지정하는 방법입니다. 16bit, 24bit, 32bit는 크게 어려움이 없습니다. 다만 8bit 에서는 color map 을 사용하기 때문에 다소 까다로운 점이 있습니다. 이런 이유로 16bit, 24bit, 32bit 에 대해서 먼저 알아 보고 마지막에 8bit에 대해서 자세히 알아 보도록 하겠습니다.
16bit 에서의 칼라 지정
16bit에서 칼라를 지정하는 방법은 2가지가 있습니다. Green 칼라만 6bit로 해서 16bit 모두를 사용하는 경우와.
R | G | B |
5bit | 6bit | 5bit |
또 한 가지는 MSB bit는 사용하지 않고, RGB 모두 5 bit를 사용하여 실제로는 15bit를 사용하는 것입니다.
사용 안함 | R | G | B |
1 bit | 5bit | 5bit | 5bit |
왜 이거 하나 통일하지 못했는지 이유는 모르겠습니다만, 프로그램 작성하는 사람으로서는 참 짜증나는 일임에 틀림없습니다.
프레임버퍼 설정 중에 15bit를 선택했을 때 MSB를 사용하지 않는 15bit 칼라를 사용하고, 16bit를 선택했을 때 Green의 6bit를 사용하는 줄 알았는데, 15bit color 은 맞는지 모르겠습니다만, 웹에서 얻은 자료를 보면 16bit 모드에서 반드시 Green 이 6bit 라고 말하기 어려운 것 같습니다. 그래픽 카드에 따라서는 15bit처럼 설정이되는 경우가 있는 것으로 생각됩니다. 아쉽게도 제 가상 P.C.가 15bit를 지원하지 못해 확인을 못했습니다.
그러나 프레임 버퍼의 변경 가능 정보에서 각 RGB에 대한 정보가 나오므로 이 값을 이용하면 15bit와 16bit 처리를 구분하여 처리할 수 있습니다.
프레임 버퍼의 변경 가능 정보에는 RGB에 대한 정보가 있습니다.
struct fb_var_screeninfo.red.length // R 비트의 크기
struct fb_var_screeninfo.green.length // G 비트의 크기
struct fb_var_screeninfo.blue.length // B 비트의 크기struct fb_var_screeninfo.red.offset // R 값을 위한 shift 횟수
struct fb_var_screeninfo.green.offset // G 값을 위한 shift 횟수
struct fb_var_screeninfo.blue.offset // B 값을 위한 shift 횟수
RGB Length 정보로 R,G,B 각 bit의 크기를 알 수 있습니다.
struct fb_var_screeninfo | 15bit | 16bit |
red.length | 5 | 5 |
green.length | 5 | 6 |
red.legnth | 5 | 5 |
RGB offset 정보로 R,G,B로 얼만큼 shift 해야 색상값을 구할 수 있는지 알 수 있습니다.
struct fb_var_screeninfo | 15bit | 16bit |
red.offset | 10 | 11 |
green.offset | 5 | 5 |
red.offset | 0 | 0 |
아래에 색상을 출력하는 예를 올렸습니다.
#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; void *fb_mapped; int mem_size; unsigned short *ptr; int coor_y; int coor_x; int color_r; int color_g; int color_b; int 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; // 한개 라인 당 바이트 개수 mem_size = line_length * screen_height; fb_mapped = ( void *)mmap( 0, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_fd, 0); color_r = 0x1f; color_g = 0x00; color_b = 0x00; color = (color_r << fbvar.red.offset ) | (color_g << fbvar.green.offset) | (color_b << fbvar.blue.offset ); for ( coor_y = 0; coor_y < 20; coor_y++) { ptr = ( unsigned short*)fb_mapped + screen_width * coor_y; for ( coor_x = 0; coor_x < 200; coor_x++) { *ptr++ = color; } } color_r = 0x00; color_g = 0x3f; color_b = 0x00; color = (color_r << fbvar.red.offset ) | (color_g << fbvar.green.offset) | (color_b << fbvar.blue.offset ); for ( coor_y = 20; coor_y < 40; coor_y++) { ptr = ( unsigned short*)fb_mapped + screen_width * coor_y; for ( coor_x = 0; coor_x < 200; coor_x++) { *ptr++ = color; } } color_r = 0x00; color_g = 0x00; color_b = 0x1f; color = (color_r << fbvar.red.offset ) | (color_g << fbvar.green.offset) | (color_b << fbvar.blue.offset ); for ( coor_y = 40; coor_y < 60; coor_y++) { ptr = ( unsigned short*)fb_mapped + screen_width * coor_y; for ( coor_x = 0; coor_x < 200; coor_x++) { *ptr++ = color; } } munmap( fb_mapped, mem_size); close( fb_fd); return 0; }
실행한 후의 결과입니다.
24bit와 32bit 칼라 지정은 위의 방법과 대동소이하므로 설명을 생락하겠습니다.
8bit 에서의 칼라 지정
15, 16, 24, 32bit에서는 R,G,B 값에 따라 비트나 값을 세팅하는 방법으로 간단히 색상을 만들 수 있었습니다. 그러나 8bit 에서는 R,G,B 값을 이용하여 바로 색상을 선택하는 것이 아니라 이미 색상이 만들어진 색상 테이블인 Color Map 에서 원하는 색상값을 구하고, 그 색상값에 해당하는 인덱스 번호를 지정해 주어야 합니다.
즉,
인덱스 | 색상 |
0 | 검정 |
1 | 파랑 |
2 | 녹색 |
3 | 하늘색 |
4 | 빨강 |
: | |
255 | 검정 |
Color Map이 이렇게 되어 있고 녹색을 찍고 싶으면,
*ptr = 2
이렇게 원하는 색상 번호의 인덱스를 대입해 주어야 합니다.
아래의 소스는 Color Map 에 지정된 색상을 모두 보여 줍니다. 소스의 이해를 돕기 위해, 일부러 변수를 많이 사용하고 가급적 주석을 많이 달도록 노력했습니다.
#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; // 프레임 버퍼 고정 정보 void *fb_mapped; // 프레임 버퍼를 메모리 매핑한 포인터 int mem_size; // 프레임 버퍼의 전체 메모리 용량 unsigned char *ptr; // 프레임 버퍼 중 점을 찍을 위치 포인터 unsigned color; // 점의 색상 int cell_x; // 16x16 바둑판에서 한개의 셀에 대한 x 픽셀 위치 int cell_y; // 16x16 바둑판에서 한개의 셀에 대한 y 픽셀 위치 int coor_x; // 16x16 바둑판을 그리기 위한 loop 변수 int coor_y; // 16x16 바둑판을 그리기 위한 loop 변수 if ( access( "/dev/fb", F_OK)) { printf( "프레임 버퍼 장치가 없습니다.n"); return 0; } if ( 0 > ( fb_fd = open( "/dev/fb", O_RDWR))) { printf( "프레임 버퍼에 접근할 수 없습니다.n"); return 0; } ioctl( fb_fd, FBIOGET_VSCREENINFO, &fbvar); ioctl( fb_fd, FBIOGET_FSCREENINFO, &fbfix); screen_width = fbvar.xres; // 스크린의 픽셀 폭 screen_height = fbvar.yres; // 스크린의 픽셀 높이 bits_per_pixel = fbvar.bits_per_pixel; // 픽셀 당 비트 개수 line_length = fbfix.line_length; // 한개 라인 당 바이트 개수 mem_size = line_length * screen_height; fb_mapped = ( void *)mmap( 0, // 프레임 버퍼에 대한 매핑 mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_fd, 0); color = 0; for ( cell_y = 0; cell_y < 256; cell_y += 16) // 16x16 바둑판의 y 좌표 { for ( cell_x = 0; cell_x < 256; cell_x += 16) // 16x16 바툭판의 x 좌료 { for ( coor_y = cell_y; coor_y < cell_y+16; coor_y++) // y 좌표 { for ( coor_x = cell_x; coor_x < cell_x+16; coor_x++) // x 좌료 { ptr = ( unsigned char*)fb_mapped + screen_width * coor_y + +coor_x; *ptr = color; } } color++; // color 인덱스를 증가 } } munmap( fb_mapped, mem_size); close( fb_fd); return 0; }
실행하시면 화면에 Color Map 의 기본 색상이 출력됩니다.
Color Map
또한 8bit 에서는 Color Map을 사용하기 때문에 Color Map 에 지정된 색상을 변경하면 그 칼라 인텍스를 사용한 색상 들은 모두 같은 색으로 변경할 수 있습니다.
Color Map 정보를 읽어 들이거나 변경하기 위해서는 struct fb_cmap 구조체를 사용합니다. fb_cmap 의 구조는 아래와 같습니다.
struct fb_cmap {
? __u32 start;???/* First entry?*/
? __u32 len;??? /* Number of entries */
? __u16 *red;??? /* Red values?*/
? __u16 *green;
? __u16 *blue;
? __u16 *transp;?/* transparency, can be NULL */
};
값을 읽어 들이려면,
fbcmap.start = 0; // 읽어 들일 첫 번째 인덱스 번호
fbcmap.len = 256; // 몇 개까지 읽어 들이겠다는 개수
fbcmap.red = calloc( 256, sizeof( __u16)); // 정보를 받을 read 값 메모리 확보
fbcmap.green = calloc( 256, sizeof( __u16)); // 정보를 받을 green 값 메모리 확보
fbcmap.blue = calloc( 256, sizeof( __u16)); // 정보를 받을 blue 값 메모리 확보
fbcmap.transp = NULL; // transparent 정보는 NULL로 설정ioctl( fb_fd, FBIOGETCMAP, &fbcmap); // Color Map 값을 구한다.
값을 적용하려면 역시 fbcmap 값을 저장한 후 FBIOPUTCMAP 를 사용하면 됩니다.
ioctl( fb_fd, FBIOPUTCMAP, &fbcmap); // Color Map 에 적용
아래는 32번부터 16개의 칼라 값을 수정하는 예제 입니다.
#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; // 프레임 버퍼 고정 정보 void *fb_mapped; // 프레임 버퍼를 메모리 매핑한 포인터 int mem_size; // 프레임 버퍼의 전체 메모리 용량 unsigned char *ptr; // 프레임 버퍼 중 점을 찍을 위치 포인터 unsigned color; // 점의 색상 int cell_x; // 16x16 바둑판에서 한개의 셀에 대한 x 픽셀 위치 int cell_y; // 16x16 바둑판에서 한개의 셀에 대한 y 픽셀 위치 int coor_x; // 16x16 바둑판을 그리기 위한 loop 변수 int coor_y; // 16x16 바둑판을 그리기 위한 loop 변수 struct fb_cmap fbcmap; int ndx_color; if ( access( "/dev/fb", F_OK)) { printf( "프레임 버퍼 장치가 없습니다.n"); return 0; } if ( 0 > ( fb_fd = open( "/dev/fb", O_RDWR))) { printf( "프레임 버퍼에 접근할 수 없습니다.n"); return 0; } ioctl( fb_fd, FBIOGET_VSCREENINFO, &fbvar); ioctl( fb_fd, FBIOGET_FSCREENINFO, &fbfix); screen_width = fbvar.xres; // 스크린의 픽셀 폭 screen_height = fbvar.yres; // 스크린의 픽셀 높이 bits_per_pixel = fbvar.bits_per_pixel; // 픽셀 당 비트 개수 line_length = fbfix.line_length; // 한개 라인 당 바이트 개수 mem_size = line_length * screen_height; fb_mapped = ( void *)mmap( 0, // 프레임 버퍼에 대한 매핑 mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_fd, 0); color = 0; for ( cell_y = 0; cell_y < 256; cell_y += 16) // 16x16 바둑판의 y 좌표 { for ( cell_x = 0; cell_x < 256; cell_x += 16) // 16x16 바툭판의 x 좌료 { for ( coor_y = cell_y; coor_y < cell_y+16; coor_y++) // y 좌표 { for ( coor_x = cell_x; coor_x < cell_x+16; coor_x++) // x 좌료 { ptr = ( unsigned char*)fb_mapped + screen_width * coor_y + +coor_x; *ptr = color; } } color++; // color 인덱스를 증가 } } // Color Map의 256 색상 값을 읽어 들입니다. fbcmap.start = 0; // 첫 번째 Color Map 부터 fbcmap.len = 256; // 총 256개의 정보를 구한다. fbcmap.red = calloc( 256, sizeof( __u16)); // 정보를 받을 read 값 메모리 확보 fbcmap.green = calloc( 256, sizeof( __u16)); // 정보를 받을 green 값 메모리 확보 fbcmap.blue = calloc( 256, sizeof( __u16)); // 정보를 받을 blue 값 메모리 확보 fbcmap.transp = NULL; // transparent 정보는 NULL로 설정 ioctl( fb_fd, FBIOGETCMAP, &fbcmap); // Color Map 값을 구한다. // 이 중에 32번째에서 16개의 색상값을 변경해 보겠습니다. fbcmap.start = 32; // 변경을 시작할 인덱스 번호는 32 fbcmap.len = 16; // 총 16개를 변경할 것임 for ( ndx_color= 0; ndx_color < 16; ndx_color++) // 16개의 칼라 값을 설정 { fbcmap.red[ndx_color] = 0; // red와 green의 값을 0으로 초기화 fbcmap.green[ndx_color] = 0; } for ( color= 0; color < 0xffff; color+=20) // blue 값을 0에서 20씩 증가 시킨다. { for ( ndx_color = 0; ndx_color < 16; ndx_color++) // 칼라맵의 16개에 대해 blue 색상 변경 { fbcmap.blue[ndx_color] = color; } ioctl( fb_fd, FBIOPUTCMAP, &fbcmap); // Color Map 에 적용 } free( fbcmap.red); free( fbcmap.green); free( fbcmap.blue); munmap( fb_mapped, mem_size); close( fb_fd); return 0; }
잘 보고 따라하고 있습니다. ^^;;
궁금한 점이 있는데요.
ptr = ( unsigned short*)fb_mapped + screen_width * coor_y; 에서 screen_width면 화면의 픽셀 폭이라고 하는데..
화면이 1024*768일때 fb_mapped가 최초 0을 가르킨다고 하면 화면폭 1024를 더하면 맨 끝으로 가는것 아닌가요..
이해가 안갑니다..ㅡㅜ;;;