이번 시간에는 각 해상도 별로 칼라 값을 지정하는 방법입니다. 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;
}