본문 바로가기

Programming/Linux_Kernel

frame buffer 이야기(8)(9)

원문 :
http://kelp.or.kr/korweblog/stories.php?story=02/12/08/8598141
http://kelp.or.kr/korweblog/stories.php?story=02/12/10/6611366


글쓴이 : holelee (2002년 12월 08일 오후 08:56) 읽은수: 10,893 [ 임베디드강좌/이규명 인쇄용 페이지 ]
== 시작하기에 앞서
이 글에 있는 모든 내용은 글쓴이가 가지고 있는 ATI mach 64(2MB) 그래픽 카드가 달려있고 RedHat 8.0이 설치된 PC(또는 Permedia2(8MB) 그래픽 카드가 달려 있는 RedHat 7.3이 설치된 PC)에서 제대로 동작하지만 이 글에 있는 모든 내용이 정확하다고 말씀 드릴 수 없습니다. 이 글에 있는 내용을 따라 했을 때 혹 생길 지 모르는 모든 문제에 대해서 글쓴이는 책임을 지지 않습니다. 글의 내용 중에 잘못된 내용이 있거나 질문할 것이 있으면 위쪽의 “holelee”를 누르고 메일을 보내기 바랍니다.


== 시작
저번 글에서 bmp 파일을 frame buffer상에 display하는 프로그램을 작성하였으므로 이제 16bpp frame buffer에 대한 모든 사항을 알아보았다고 볼 수 있습니다. 물론 pixel 읽기는 말로 넘어갔으므로 조금 의심스러운 부분이 남아 있을 지라도 적어도 display에 대해서는 모든 사항을 알아보았습니다. 오늘 이야기는 16bpp가 아닌 다른 bpp(bits per pixel)에 관한 프로그래밍에 대해서 짤막하게 알아보도록 하겠습니다.
PC 기반의 linux 시스템에서 frame buffer는 보통 4, 8, 16, 24, 32bpp를 사용합니다. 모두 color를 나타낼 수 있습니다. 그런데 embedded system의 경우 gray LCD를 사용하는 경우도 있고 color LCD를 사용한다고 해도 PC와는 다른 bpp를 사용하는 경우가 있습니다. 예를 들어 Compaq(HP에 인수되었지만)에서 생산한 PDA인 iPAQ의 초기 모델의 경우 12bpp를 사용하여 총 4096 color를 나타낼 수 있었습니다. 이렇게 다양한 frame buffer 하드웨어에 대하여 모든 조사를 진행할 수도 없고 테스트도 불가능 합니다. 따라서 이 글은 제 개인적인 경험(보잘 것 없기는 하지만)에 바탕으로 한 추측을 기반으로 작성되었습니다. 이 글의 내용을 모두 곧이 곧대로 사실로 받아들이지 말고 그냥 가이드 정도의 수준으로 생각하고 읽기 바랍니다. 자 시작할 까요?


== 1 bpp
pixel 하나를 1 bit로 표현할 수 있습니다. 어떻게 가능할까요? 당연히 bit가 ‘1’이면 1번 pixel이 나타나고 bit가 ‘0’이면 0번 pixel이 나타나도록 할 수밖에 없습니다. 보통은 0번 pixel은 그냥 pixel이 없는 상태로 결국 bit가 ‘1’이면 점이 있고, bit가 ‘0’이면 점이 없는 그냥 말로 “흑백”이죠. 무슨 gray level이 있고 하는 것이 아니라 그야말로 pixel의 유무를 1bit로 나타낸 것입니다. 얼마 전까지 주로 사용되는 핸드폰용 흑백 LCD가 이런 형태를 지니고 있었죠.(물론 핸드폰은 linux가 아직 널리 쓰이지 않는 분야이기는 하지만요.) 이런 종류의 frame buffer가 많이 쓰이지는 않을 것으로 보입니다. 간단한 정보를 display하기 위해서는 character LCD를 더 선호할 테니깐요.
프로그래밍은 간단합니다. 우선 Frame buffer상의 좌표를 모두 byte와 bit단위로 계산을 해야 합니다. lseek/read/write를 이용하거나 mmap을 이용하거나 하더라도 우리가 기본적으로 읽을 수 있는 최소 단위는 byte(char)이기 때문에 읽고 쓰기는 byte단위로 이루어져야 합니다. 그럼 예를 들어 320x240 사이즈의 frame buffer에서 (99, 50)에 pixel을 찍고 싶다고 가정하죠. 그럼 (99,50)의 위치는 byte 단위로는 (320*50+99)/8 = 2012가 됩니다. 그럼 offset 2012에서 byte만큼의 값을 먼저 읽어 prev_pixel(char형)에 저장합니다. 그 다음 bit offset은 (320*50+99)%8 = 3이 됩니다. 그럼 prev_pixel의 MSB부터 3비트 떨어진 비트 위치를 ‘1’로 바꾸고(prev_pixel |= 0x10 으로 하면 되죠.) 다시 그 값을 offset 2012에 다시 적어주면 됩니다. 아주 간단하죠. 이런 식으로 byte단위 offset과 그 byte내부의 bit 단위 offset을 계산해서 값을 읽거나 쓸 수 있겠습니다.

옛날 이야기 좋아하는 사람들을 위해서 한마디 적으면, PC상에서 사용되던 그래픽 카드중에 Hercules(요즘도 Hercules라는 상표를 달고 나오는 그래픽 카드가 있는데 그런 녀석 말고.) 라고 하는 녀석도 1bpp를 사용했었죠. simcga라고 하는 CGA emulation 프로그램을 램 상주 시키고 게임을 했던 기억이 있습니다.(simvga라고 하는 희대의 사기극이 온라인계를 강타하고 그랬었죠. simvga를 다운로드 받으려고 노력했던 기억이 아련합니다.) 놀라운 것은 linux kernel source에 이 Hercules 그래픽 카드를 지원하는 frame buffer driver가 존재한다는 겁니다. DOS시절 turbo C 2.0으로 Hercules 카드에 그림을 그려본 경험으로 보면 Hercules 카드의 그래픽 메모리는 linear하지 않고 interleaving되어 있어서 offset 계산을 특별하게 했던 기억이 있는데, linux kernel source에 포함되어 있는 Hercules frame buffer는 어떻게 프로그래밍 해야 하는지 궁금합니다. 아마도 mmap을 지원하지 않거나 지원하더라도 linear하지 않을 것으로 보입니다. (도저히 그 그래픽 카드를 구할 수 있는 방법이 없으므로 테스트 못합니다. 혹 구한다고 해도 테스트하기 싫죠. -.-! 또한 kernel source code를 분석해야 할 필요성 조차 느끼지 못한답니다.)


== 2 bpp
pixel 하나를 2 bpp로 나타내는 경우는 gray level LCD에서 사용되는 것으로 보입니다. 한 점이 4개의 gray level로 나타내어 지게 됩니다. pixel값이 “00b”(이진수 00)라면 pixel이 없는 상태(즉 가장 밝은(?, 흰색에 가까운) pixel)이고 “11b”라면 가장 어두운(?, 검정색에 가까운) pixel을 나타냅니다. Pixel값을 이 어두운(?) 순서로 나열하면 “11b” > “10b” > “01b” > “00b”이 됩니다.
프로그래밍은 당연히 1bpp의 경우처럼 byte단위 offset과 bit단위의 offset으로 나누어 계산해야 합니다. 한번 더 예를 들어서 살펴보죠. 320x240사이즈의 2 bpp frame buffer에서 (99, 50)의 위치에 점 “11b”를 쓰고 싶다고 하죠. 그럼 (320*50*2 + 99*2)/8 = 4024의 byte offset을 가집니다. 그 byte 내부의 bit offset은 (320*50*2 + 99*2)%8 = 6이 됩니다. 그럼 한 byte에 총 4개의 2 bpp pixel이 들어가므로 MSB에서 6bit 만큼 떨어져 있는 위치는 LSB에 있는 2bit을 나타내게 됩니다. 따라서 byte offset에서 한 byte를 읽은 후에 LSB의 2 bit를 “11b”로 바꾸고 다시 값을 써 주면 되겠습니다.
한 byte에 4개의 pixel이 들어가므로 다음과 같이 계산 할 수도 있습니다. (320*50 + 99)/4 = 4024, (320*50+99)%4 = 3이 됩니다. 여기서 3이라는 의미는 byte내부에서 3번째 pixel 위치를 나타냅니다.(당연히 LSB 2bit죠.)

== 4 bpp
4 bpp가 사용되는 경우는 크게 color와 gray level로 나누어 볼 수 있습니다. 우선 gray level(주로 LCD가 되겠죠)을 사용하는 시스템은 16 gray level을 나타낼 수 있다는 점만 빼고는 2bpp의 경우와 비슷합니다. pixel값이 “0000b”라면 pixel이 없는 상태(즉 가장 밝은(?) pixel)이고 “1111b”라면 가장 어두운(?) pixel을 나타냅니다.
그럼 color는 어떻게 될까요? pixel 값이 “0000b”이면 어떤 색의 pixel일까요? 혹 3bpp라면 R(1),G(1),B(1)로 나타낼 수 있을 지도 모르는 데 4bpp라면 그렇게 나누기도 쉽지 않습니다. 이 이야기는 아래 palette 이야기에서 살펴보도록 하겠습니다.
프로그래밍은 설명 없이도 알겠죠. 1bpp와 2bpp의 경우와 비슷하게 byte, bit offset을 각각 찾아서 프로그래밍하면 됩니다.

== palette(스펠링이 어려워요.)
palette는 우리말(?)로는 팔레트라고 하죠. 왜 그 미술시간에 튜브에 들어있는 물감을 짜서 쓸 수 있도록 구획이 있는 플라스틱으로 된 판(?) 있잖아요. 환상적인(?) 그림 솜씨를 뽐내며 설명하고 싶지만 KELP사이트의 제약으로 인해 잘 안됩니다.(혹 아직도 모르겠으면 주변 사람들에게 물어 보면 됩니다.) 컴퓨터 그래픽에서도 이 palette라는 것이 등장합니다. 컴퓨터 그래픽에서 등장하는 palette도 미술시간에 사용하던 팔레트와 마찬가지로 지금 사용하고 있는 색깔을 담아두는 공간(실제로는 memory)입니다.
Color 4bpp에서 pixel 값이 “0000b”라면 어떤 색의 pixel이 나올지 궁금했죠. 모든 사람들이 비웃을지 모르지만 답을 공개 합니다. “0번 색깔 pixel”입니다. 그럼 pixel값이 “0011b”라면 “3번 색깔 pixel”이 되고 “1111b”라면 “15번 색깔 pixel”이 됩니다. 상당히 우스운 답이죠? 그럼 이제 “x번 색깔”은 도대체 무슨 색깔인지가 궁금해 집니다.
Color 4bpp에서는 총 16개의 색의 물감을 채울 수 있는 palette가 자동으로 주어집니다. 팔레트의 구획이 총 16개이고 각각의 구획에 0번부터 15번까지의 번호를 부여했다고 가정합니다. 각 구획에 무언가 물감이 들어 있고 그 물감의 색깔을 구획의 번호에 맞추어 “x번 색깔”이라고 부르도록 합니다. 위에서 말한 “0번 색깔”은 말 그대로 palette에 있는 “0번 색깔”을 의미합니다. 그럼 이제 palette의 “0번 색깔”이 무엇인지 알아볼 수 있는 방법만 있다면 pixel값이 “0000b”일 때 어떤 색깔의 pixel인지 알 수 있습니다.
(컴퓨터에서 개수가 정해져 있는 데이터를 관리하기에 가장 편한 것은 array입니다. 따라서 palette도 array로 구현되는 경우가 일반적입니다.)

Linux frame buffer에서는 palette라는 말을 사용하지 않고 대신 colormap이라는 말을 사용합니다. colormap이라는 말이 좀더 그럴싸하게 들리기는 합니다만 아무튼 다음과 같은 ioctl()로 colormap(palette)의 내용을 확인 할 수 있습니다.
struct fb_cmap fbcmap;
….
ioctl(fd, FBIOGETCMAP, &fbcmap);
당연히 frame buffer driver가 colormap을 지원하지 않는다면 ioctl()은 에러를 리턴하게 됩니다.

/usr/include/linux/fb.h(kernel source include/linux/fb.h)를 보면 struct 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 */
};
start는 보통 0이고 4bpp의 경우 len은 16이 됩니다. red, green, blue는 특정 array나 아니면 malloc된 address를 가리키고 있어야 합니다.(transp는 나중에 설명하죠. 그냥 NULL) 그런 다음 위의 ioctl()을 수행하면 red, green, blue가 가리키고 있는 address에서 현재 frame buffer가 사용하고 있는 palette의 값을 읽어오게 됩니다.
그러면 “0000b” pixel의 색은 RED=fbcmap.red[0], GREEN=fbcmap.green[0], BLUE=fbcmap.blue[0]의 색을 가지게 됩니다. “1111b”는 RED=fbcmap.red[15], GREEN=fbcmap.green[15], BLUE=fbcmap.blue[15]의 색을 가지게 됩니다.
palette에 들어 있는 색을 바꿀 수 있으면 더욱 좋겠죠. 지금 셋팅되어 있는 palette를 사용하는 것이 아니라 우리가 직접 16개의 물감을 짜 놓은 팔레트를 사용하는 것이 그림 그릴 때 더 좋을 것 아닙니까? 그 방법도 있습니다. FBIOPUTCMAP ioctl을 사용하면 됩니다.(당연히 frame buffer driver가 지원하지 않는다면 error를 리턴합니다.)

이제 palette를 좀 정리해서 학구적인(?) 말로 적어보도록 하겠습니다. 어떤 computer graphic에서 palette mode를 사용한다고 하면 pixel의 값은 R,G,B값으로 직접 encoding되어 있지 않고, palette 안에 있는 색을 가리키는 index역할을 하게 됩니다. 실제 색을 알아내기 위해서는 pixel값으로 palette를 indexing하면 됩니다. Linux frame buffer에서는 palette라는 말 대신 colormap이라는 말을 사용하고 palette내용을 알아내거나 바꿀 때 FBIOGETCMAP, FBIOPUTCMAP ioctl()을 사용합니다.(정리끝 : 이렇게 간단한 이야기를 저렇게 길게 써 놓았다니..)


== 8bpp
8bpp의 경우는 gray level LCD에 사용되는 경우는 거의 없고 대부분 256 color를 나타낼 수 장치에 사용됩니다. Color 8bpp는 color 4bpp에서와 마찬가지로 palette mode를 사용합니다. 즉 pixel값에 직접 R,G,B가 encoding되어 있는 것이 아니라 pixel값으로 palette를 index해야 pixel의 색상을 알 수 있습니다. 단지 8bpp이므로 256개의 entry를 가지는 palette를 사용한다는 점이 다릅니다. 프로그래밍 방법은 아주 간단합니다. 이제는 Byte offset만 계산해서 byte의 값만 update하면 pixel이 바뀌게 됩니다. 다시 한번 예로 320x240 사이즈의 8bpp frame buffer에서 (99, 50)에 0xf0라는 pixel을 찍는다고 가정하면 byte offset은 (320*50+99)가 됩니다.
특별한 예외로, 삼성에서 나온 ARM MCU의 경우 8bpp color LCD를 지원하는 데 palette mode를 사용하는 것이 아니라 pixel값에 직접 R,G,B값이 encoding되어 있는 것으로 보입니다.(MSB부터 Red 3bit, Green 3bit, Blue 2bit라고 합니다.)


== 15bpp
16bpp에서 한 픽셀이 dummy(1bit),R(5bit),G(5bit),B(5bit)으로 나타내어질 때 보통 15bpp라고 칭하기도 합니다. Dummy bit가 있다고 해도 한 pixel이 16bit로 나타내어지므로 16bpp라고 해야 정확하다고 볼 수 있습니다.


== 16bpp
넘어 갑니다.


== 24bpp
24bpp이상은 작은 embedded system에서 쓰이는 경우는 거의 없습니다. 아무래도 bpp가 커질수록 메모리에 대한 부담이 크게 작용하기 때문입니다. Embedded system에 사용된다면 대부분 TV(settop)와 같이 display를 주로 이용하는 system의 경우가 많습니다. 24bpp를 보통 true color라고 부릅니다. 24bpp에서 한 pixel의 값은 R,G,B가 각각 8bit(1byte)씩인 모여서 24bit를 이루게 됩니다. 따라서 프로그래밍을 위해서는 offset을 계산한 후에 R,G,B 1byte씩을 써 주면 됩니다.
(R,G,B가 1 byte씩이라는 것은 일반적인 경우입니다. 정확하게 R,G,B가 어떤 bit를 점유하고 있는지는 16bpp에서와 마찬가지로 struct fb_var_screeninfo 내부의 struct fb_bitfield타입의 red, green, blue를 각각 참조를 해야 알 수 있습니다.)

== 32bpp
32bpp도 24bpp와 마찬가지로 true color라고 부릅니다. 24bpp와 동일하게 R,G,B값이 각각 8bit(1byte)로 coding됩니다. 나머지 8bit를 우리는 보통 alpha channel(이하 alpha)이라고 부릅니다. alpha는 그 pixel의 투명도(transparency)를 나타냅니다. 위의 colormap에서 나오는 transp라는 포인터와 struct fb_var_screeninfo내부의 struct fb_bitfield transp라고 하는 부분도 이에 해당합니다. 투명도에 대해서 감이 없는 분은 http://www.directfb.org/screenshots/gimp.png 을 열어보기 바랍니다. 반투명한 image들이 막 섞여 있는 것이 보일 겁니다. 보통 Alpha와 R,G,B를 합쳐서 ARGB라고 부르는 경우도 있습니다. 프로그래밍 방법은 역시 크게 다르지 않습니다. 한 pixel이 4 byte의 크기를 가지므로 4 byte단위의 offset을 계산한 후에 4 byte를 적어 주면 됩니다. 쉽게는 그냥 unsigned형 데이터를 직접 쓰면 되겠습니다.
(A,R,G,B가 1byte씩이라는 것은 일반적인 경우를 나타내며 정확히 A,R,G,B에 대한 bitoffset을 계산하기 위해서는 struct fb_bitfield red, green, blue, transp를 참조하면 됩니다.)


== 몇 가지 남은 이야기
왜 9bpp라는 것은 잘 사용되지 않을까요? R,G,B를 각각 3bit씩 coding하면 아주 쉬울 것 같은데 말이죠. 그 이유는 9bpp로 하면 offset계산과 9bit를 읽고 쓰기가 불편하기 때문일 겁니다. offset계산이야 당연히 byte단위와 bit단위로 나누어 계산한다고 하더라도 항상 2byte를 읽어와서 bit masking을 하고 그 값을 다시 적는 것이 까다롭겠죠. 잘 이해가 안되는 사람은 직접 코딩을 해보면 쉽게 이해할 수 있을 겁니다.

16bpp 이상의 그래픽 시스템에서는 일반적으로 palette mode를 사용하지 않습니다. 그 이유는 어짜피 palette도 메모리의 어딘가에 저장하고 있어야 하는데 bpp가 커질수록 palette의 사이즈가 기하급수적(?)으로 증가하기 때문입니다. 예를 들어 320x240 16bpp 그래픽 시스템에서 palette mode를 사용한다고 가정하죠. 그리고 각 palette안에 들어 있는 pixel은 24bpp로 코딩되어 있다고 할 때 palette를 위해서 필요한 메모리의 크기는 24bit*2^16 = 196608byte가 됩니다. 또한 pixel값을 저장할 메모리의 크기는 320*240*16bit=153600byte이므로 시스템은 총 350208byte의 메모리를 사용합니다. 그런데 320x240 24bpp 그래픽 시스템이 palette mode를 사용하지 않으면 pixel값을 저장할 메모리만 필요하므로 총 320*240*24bit = 230400byte만 필요합니다. 당연히 320x240 16bpp palette mode에서 display할 수 있는 image는 모두 화질 저하 없이 320x240 24bpp 시스템에서 display할 수 있습니다. 이런 결과를 보고 누가 16bpp 이상의 그래픽 시스템이 palette mode를 사용하겠습니까?

bmp파일을 16bpp frame buffer상에 display하는 프로그램에서 24bpp로 encoding된 bmp파일도 display가 가능했습니다. 24bpp pixel을 16bpp pixel로 변환할 때 R,G,B각각의 하위 2~3비트씩을 떼어내었다는 것을 코드를 살펴본 사람은 알 수 있을 겁니다. 당연히 24bpp frame buffer에 display하는 것보다 화질 저하가 생겼지만 어쩔 수 없습니다. 16bpp로 나타낼 수 있는 색상의 수가 24bpp보다는 적으므로 피할 수 없습니다. 실제로 화질 저하를 좀더 줄이려면 bmp파일 내의 각 pixel의 R,G,B값의 통계적인 분포를 살펴보고 그에 따라서 하위 비트를 떼어낼지 아니면 상위 비트를 떼어내어 saturation시킬 지 결정해야 합니다. 예를 들어 24bpp로 encoding된 bmp파일이 R,G,B값을 하위 두 비트만 사용하여 매우 어두운 image였다면 제가 수행한 방법으로는 완전히 검은 image를 얻을 수 밖에 없습니다.
그럼 24bpp로 encoding된 bmp파일을 8bpp(palette mode) frame buffer상에 display하려면 어떻게 해야 할까요? 16bpp의 경우는 그래도 pixel의 값이 R,G,B 값 자체로 코딩되어 있으므로 적당히 bit를 떼어내면 쉽게 가능합니다. 하지만 Palette mode를 사용하면 256가지 색상은 원하는 색상을 마음대로 골라서 사용할 수 있지만 다른 색깔은 어떻게 표현할 방법이 없습니다. 혹시 24bpp로 encoding된 bmp파일이 256색 이하만 사용한다면 그 사용된 색으로 palette를 업데이트하고 display하면 화질 저하가 전혀 없는 image를 얻을 수 있습니다. 하지만 그럴 가능성은 일반적으로 매우 적죠. 이런 경우 bmp파일을 살펴봐서 bmp파일에서 적당히 256가지 색상을 찾은 다음 그 256가지 색상으로 bmp파일의 각 pixel을 이미 고른 256개의 색상중에 가장 가까운 색상으로 모두 바꾸어서 화질 저하를 최소화하려고 노력합니다. 당연히 화질 저하가 최소화되려면 256가지 색상을 잘 선택해야 합니다. 이런 과정을 전문 용어로는 dithering(디더링)이라고 표현한다고 합니다. 사실 전 dithering의 구체적인 방법은 전혀 아는 바가 없습니다. 그래서 처음부터 frame buffer이야기를 16bpp로 한정한 것입니다.(아! 이 잔머리 놀랍지 않습니까?) bmp파일 display 예제로 올렸던 압축 파일 내부의 kelp_logo_1bpp.bmp와 kelp_logo_4bpp.bmp는 KELP 사이트의 logo(24bpp)를 PC용 프로그램으로 dithering해서 만든 이미지입니다. kelp_logo_1bpp는 당연히 흑백이니까 차이가 나고 kelp_logo_4bpp도 잘 살펴보면 원본 이미지와 차이가 나는 것을 볼 수 있습니다.

마지막으로 24bpp나 32bpp를 왜 true color라고 부를까요? 이건 학교 다닐 때 수업시간에 얼핏 들은 이야기인데 보통 사람의 눈으로는 256개 이상의 gray level로 encoding된 image를 256개의 gray level image와 구별하기 힘들다고 합니다. 즉 똑 같은 image를 512(9bit)개의 gray level로 나타내든지, 1024(10bit)개의 gray level로 나타내든 256 gray level로 나타낸 모습과의 화질 차이는 보통 사람 눈에는 안 보인다고 합니다. 어찌 보면 사람의 가청 주파수가 20Hz~20kHz라는 것과 같은 사람의 한계라고 볼 수 있습니다. 물론 백마 타고 온 초인이 간혹 존재해 구분해 내겠지만요. 따라서 R,G,B를 각각 256(8bit)단계로 표현한 24bpp와 32bpp를 true color라고 부릅니다. 마케팅적인 요소가 있는지 아니면 초인들이 늘어났는지 알 수는 없지만 DVD player에서 사용하는 DAC(Digital to Analog Converter)은 보통 R,G,B(또는 Y,Cb,Cr)를 각각 10bit씩 사용하고 PC 시스템에서도 최근에 나온 matrox Parhelia와 같은 그래픽 카드는 R,G,B값을 8bit이상으로 표현할 수 있는 경우가 있습니다. Matrox Parhelia 그래픽 카드는 너무 비싸서 선뜻 사기도 힘들고 저와 같은 막눈으로는 화질이 좋아졌다고 판단할 수 없죠.


== 마치며
간단히 적으려고 했는데 많이 길어졌습니다. 너무 길어져서 그냥 휙 넘어간 부분도 많은데 그냥 이해하기 바랍니다. 이번 글에서 제일 중요한 이야기는 palette에 관한 것이었고 상식으로 alpha channel이나 이런 것들을 기억해 두면 됩니다. 이번 글은 사실 frame buffer뿐 아니라 대부분의 그래픽 시스템과 그래픽 파일에도 동일하게 적용되는 부분입니다.
다음 글이 frame buffer 이야기의 마지막 글일 겁니다. frame buffer를 사용하는 몇 가지 프로그램들에 대한 소개하고 frame buffer의 한계 등에 대해서 다룰 예정입니다. 빨라도 다음 주말에나 글을 쓸 수 있지 않을까 생각이 듭니다.

(푸념 한마디) 지금 사용하고 있는 MS Windows PC의 그래픽 카드(matrox G400)가 맛이 가려나 봅니다. 툭하면 화면에 줄이 가고 다운 되고 그러네요. 드라이버 문제라면 다시 설치해 보겠지만 잘 모르겠네요. 가뜩이나 궁핍한 생활에 그래픽 카드마저 속을 썩이니…혹 Hercules 그래픽 카드를 아직까지 골동품(?)으로 소장(?)하고 있는 분은 저한테 택배로 부쳐 주시기 바랍니다. PC에 ISA slot이 있었나? 앗, 그러고 보니 MS Windows가 Hercules 카드를 지원하지 않을 듯. Linux 만세! 후후…



== 시작하기에 앞서
이 글에 있는 모든 내용은 글쓴이가 가지고 있는 ATI mach 64(2MB) 그래픽 카드가 달려있고 RedHat 8.0이 설치된 PC(또는 Permedia2(8MB) 그래픽 카드가 달려 있는 RedHat 7.3이 설치된 PC)에서 제대로 동작하지만 이 글에 있는 모든 내용이 정확하다고 말씀 드릴 수 없습니다. 이 글에 있는 내용을 따라 했을 때 혹 생길 지 모르는 모든 문제에 대해서 글쓴이는 책임을 지지 않습니다. 글의 내용 중에 잘못된 내용이 있거나 질문할 것이 있으면 위쪽의 “holelee”를 누르고 메일을 보내기 바랍니다.


== 시작
이번 글이 frame buffer 이야기의 마지막 글이 될 겁니다. 이제까지 linux frame buffer를 이용하여 화면에 그림을 그리는 것에 대한 내용을 모두 마쳤다고 볼 수 있습니다. 현실적인 제약으로 인해 모든 frame buffer에 대해서 테스트를 해보거나 할 수는 없었지만 그래도 어느 정도 프로그래밍을 위한 기초적인 정보는 모두 살펴봤다고 볼 수 있습니다. 이번 글에서는 “frame buffer 프로그래밍으로 무엇을 할 수 있나?”에 대해서 알아본 후에 frame buffer가 실제로 사용되고 있는 프로그램들에 대해서 짤막하게 소개하도록 하겠습니다. 그 다음 linux frame buffer의 한계에 대해서 생각해 보는 시간을 갖도록 하겠습니다. 준비가 되었다면 시작하도록 하죠.


== frame buffer 응용
이제 까지 frame buffer를 이용하여 화면에 나타날 pixel을 찍거나 이미 화면에 있는 pixel을 읽을 수 있다는 것을 알아보았습니다. 그럼 이런 방법론을 이용하여 실제로 응용할 수 있는 분야는 어떤 것이 있을까요? 당연히 2차원 컴퓨터 그래픽을 전부를 할 수 있습니다. 2차원 컴퓨터 그래픽을 사용하고 있는 분야는 상당히 많이 있죠. 하지만 실제 embedded system에서 사용할 만한 가능성이 있는 것들만 추려보면 다음과 같습니다.
(1) 그래픽 파일 viewer나 동영상 player
(2) GUI(Graphic User Interface)
더 생각이 나지 않는 군요.(더 좋은 응용 분야를 알고 있는 사람은 리플을 달아주기 바랍니다.)
(1) 번이야 쉽게 이해가 되죠? 예를 들어 요즘 지하철 열차 내에 설치되어 있는 LCD를 이용한 광고 장치(?, OS로는 MS Windows를 사용하는 것으로 보입니다. 따라서 열차 내의 어딘가에는 PC가 있을 것으로 보입니다. 오로지 출력만 여러 LCD로 연결했을 가능성이 큽니다.)와 같이 일방적으로 사람들에게 화상 정보를 보내는 장치는 frame buffer를 이용해서 프로그래밍하면 될 겁니다.(지하철 역사에는 PDP와 프로젝션이 설치되고 있는 추세더군요.)
GUI의 경우도 역시 2차원 컴퓨터 그래픽을 이용하는 분야이므로 frame buffer를 이용할 수 있습니다. 물론 GUI는 어디까지나 사용자의 입력도 받아야 하므로 사용자의 입력을 받을 수 있는 장치가 필수죠. 대부분 마우스나 터치스크린과 같은 pointing device와 같이 이용합니다. (1)이 GUI위에 올라갈 수도 있죠.


== linux frame buffer의 장점
linux 시스템에서 frame buffer를 이용할 때 장점은 딱 한가지 있습니다. 포팅의 용의성입니다. 예를 들어 frame buffer에 결과를 출력하는 동영상 player를 만들었다고 하면 그 player는 오로지 linux kernel(frame buffer driver포함)에만 의존적(dependent)입니다. 또한 비교적 frame buffer 프로그래밍 interface가 잘 되어 있고(문서는 어디 있는지 찾기 어렵지만), 그것이 하드웨어(그래픽 카드)에 의존적이지 않습니다. 결국 의존성(dependency)이 작으므로 한번 작성된 프로그램을 여러 embedded target으로 포팅하기 쉽게 됩니다.


== linux frame buffer를 이용하고 있는 프로그램들
linux에서 frame buffer를 이용하고 있는 프로그램들의 범주 역시 위에서 살펴본 “응용”과 별반 다르지 않습니다. 실제로는 더욱 많이 이용되고 있겠지만 사람들의 입에 자주 오르내리는 몇가지 프로그램만 소개하도록 하겠습니다.

= 동영상 player
(*) Xine(
http://xine.sourceforge.net/)
(*) MPlayer(
http://www.mplayerhq.hu/)
Linux에서 가장 유명한 두 개의 동영상 player입니다. 두 프로그램 모두 많은 동영상 코덱(codec)을 지원하고 많은 video output driver와 audio output driver를 지원합니다. 지원하는 video output driver 중에 linux frame buffer가 있습니다.

= GUI
(*) X server(
http://www.xfree86.org)
X 를 빼 놓고는 linux의 GUI를 이야기할 수 없죠. XFree86은 사실상 PC를 주된 target으로 합니다. 사이트에서 살펴보면 PC에서 사용되는 많은 그래픽 카드를 나열해 놓고 지원한다고 말하고 있습니다. 특정 그래픽 카드에 맞게 최적화된 드라이버를 계속 만들어 내고 있습니다. 그 드라이버가 하드웨어를 직접 제어해서 화면을 구성합니다.
XFree86에서 지원하는 드라이버 중에 linux frame buffer도 존재합니다. 보통 linux frame buffer를 이용하도록 컴파일된 X server를 TinyX라고 부릅니다.

(*) Qt/Embedded(
http://www.trolltech.com/products/embedded/)
KDE에 주로 이용되고 있는 toolkit인 Qt는 trolltech에서 만들었습니다. 그 toolkit을 embedded system으로 포팅한 것을 Qt/Embedded라고 한다고 합니다. PC에서는 Qt toolkit이 화면에 무엇인가를 나타내려고 할 때 X를 사용했겠지만, X가 올라가 있지 않는 embedded system에서는 frame buffer를 이용할 수 밖에 없습니다.(위에서 설명한 의존성이 가장 작은 interface이기 때문이죠.)
참고로 Qt/Embedded는 license를 받아야 하는 상용 프로그램으로 알고 있습니다.

(*) microwindows(
http://www.microwindows.org/)
Opensource GUI에서 유명한 녀석이죠. 역시 linux frame buffer를 지원합니다.


== linux frame buffer의 한계
frame buffer를 이용하여 그래픽을 출력하는 프로그램이 과연 성능이 좋을까요? 성능이 좋다면 XFree86에서 계속 그래픽 카드에 포팅하고 있지도 않고 PC에서 동작하는 X도 frame buffer를 이용할 겁니다. 그런데 여전히 새로 나오는 그래픽 카드에 포팅은 진행하고 있고, PC linux에서는 frame buffer가 없이도 X는 잘 동작합니다. 결국 frame buffer는 성능이 좋은 그래픽 interface는 아니라는 말입니다.
현재 시장에 나오고 있는 그래픽 카드는 3D 가속기능을 가지고 있습니다. Frame buffer야 3차원과는 거리가 있으니까 무시해도 됩니다. 그런데 MS Windows 3.1이나 MS Windows95가 시장에서 힘을 쓰고 있던 시절에는 그래픽 카드들이 모두 “Windows Acceleration”이란 광고를 하고 있었습니다. 이 “Windows Acceleration”이라는 기능이 2차원 그래픽 가속기능인데 현재 시장에 있는 그래픽 카드는 모두 이런 기능을 가지고 있습니다. 이 기능을 간단히 이야기하면 bitblt(bit-blit이라고 발음되고 bit block transfer의 약자임)이라고 하여 그래픽 카드가 스스로 bitmap을 화면(그래픽 메모리)의 이곳 저곳으로 여러 가지 연산과정과 함께 옮길 수 있는 기능입니다. 또한 그래픽 카드가 PCI나 AGP의 bus mastering(일종의 DMA)을 이용하여 시스템 메모리에 있는 bitmap을 화면(그래픽 메모리)으로 전송할 수도 있습니다. 그리고 rectangular를 특정 색깔로 채우거나 하는 것도 그래픽 카드가 스스로 수행하죠. 이러한 2차원 그래픽 가속능력은 GUI의 속도를 개선하는데 필요한 핵심 기능 중에 하나입니다.
Frame buffer를 이용한 프로그램은 모두 CPU가 직접 해 줄 수밖에 없습니다. Bmp viewer 예제에서 보았듯이 pixel 하나 하나를 CPU가 직접 그래픽 메모리로 옮겨야 합니다. 또한 그래픽 메모리의 일정 부분을 다른 곳으로 옮길 때도 CPU가 해줄 수밖에 없습니다. 문제는 테스트에서 사용된 ATI mach64나 Permedia2와 같은 그래픽 카드도 그러한 가속기능을 가지고 있는데도 Frame buffer interface로는 그러한 가속 기능을 이용할 수 없다는 점입니다. 즉 하드웨어의 문제가 아니라 interface의 문제인 것이죠. 물론 StrongARM에 있는 LCD controller는 bitblt가 같은 것을 수행하지 못합니다. 그렇다고 embedded linux에서 사용되는 그래픽 하드웨어가 모두 2차원 가속 기능을 가지지 않는다고 단정지을 수는 없습니다. Embedded linux에 국한 시켜봐도 frame buffer interface가 완전하다고 말할 수는 없습니다.
(linux frame buffer driver 내부에서 console(console on FB)을 scroll할 때 그래픽 카드의 가속 기능을 이용하는 경우가 있기는 합니다. 하지만 유저 프로그램이 그런 기능을 직접 이용하는 방법은 제가 아는 한 없습니다.)
완전한 interface라면 그래픽 하드웨어가 가지고 있는 모든 가속기능을 이용할 수 있도록 해야 합니다. StrongARM의 LCD controller와 같이 그런 기능이 없으면 그것을 CPU가 직접 소프트웨어로 처리하고, 있으면 최대한 하드웨어를 이용할 수 있는 interface가 필요합니다.
(*) DirectFB(
http://www.directfb.org)
아마도 이름은 MS의 DirectX에서 따온 것으로 보입니다. 위에서 설명한 완벽한 interface를 제공하기 위해 열심히 뛰고 있는 사람들입니다. Linux frame buffer와 그래픽 하드웨어용 루틴을 합쳐서 동일한 interface를 제공하도록 라이브러리화한 것으로 보입니다. 이 DirectFB위 GTK+ toolkit을 포팅한 것도 찾아볼 수 있습니다.(참고로 GTK+는 free입니다.)


== 글을 모두 마치며
이제까지 linux frame buffer에 관련된 많은 내용을 알아봤습니다. Frame buffer는 PC에서도 사용할 수 있기는 하지만 전체적으로 embedded target을 위한 interface라고 볼 수 있습니다. Linux Framebuffer HOWTO와 같은 문서를 보면 PC에서는 결국 부팅할 때 “펭귄”을 보려고 삽질을 한다고 나옵니다. 아무래도 PC는 X를 사용하기 때문에 별로 의미가 있는 그래픽 interface라고 하기는 어렵습니다. Embedded target에서 frame buffer 프로그램을 작성할 때 도움이 되고 사람들이 frame buffer 프로그래밍이 별 것 아니구나 하는 생각이 들었으면 좋겠다는 조그만 바람으로 시작한 글이었는데, 결과가 어떨지는 잘 모르겠습니다.
그리고 전체적으로 급히 작성을 해서 글의 완성도가 여러모로 떨어지고, 약간은 장난기가 섞여 있는 것으로 보입니다. 그냥 이해하고 봐주셨으면 좋겠습니다. 아무튼 여기까지 읽어 주신 분들에게 감사 드립니다.
다음 기회에는 좀더 좋은 주제로 글을 잘 적겠습니다.

혹, 다루었으면 하는 좋은 주제가 있으면 리플이나 메일로 알려주시기 바랍니다. 물론 제가 아는 것이 별로 없으므로, 모든 주제에 대해서 글을 적을 수 있다고 할 수는 없습니다. 그리고 하드웨어에 의존적인 부분에 대해서는 테스트를 할 수 없으니 그런 주제도 피해야 하구요. 또한 제 관심사에 벗어난 일(예를 들어 GUI 프로그래밍)이라면 당연히 안되겠죠. 푸후…이렇게 하고 나니 글을 적을 만한 주제가 없겠네요. 다시 질답란에서만 활동하며 글을 적을 만한 주제를 찾아야겠습니다

'Programming > Linux_Kernel' 카테고리의 다른 글

Branch Prediction  (2) 2009.06.26
SecureCRT 에서 menuconfig 보기  (0) 2009.06.08
frame buffer 이야기(6) (7)  (0) 2009.06.04
frame buffer 이야기(4) (5)  (0) 2009.06.04
frame buffer 이야기 (3)  (0) 2009.06.04