본문 바로가기
공부하는 하스씨/Graphics

Frame Buffer 이야기 (7)

by 박하스. 2008. 11. 26.
728x90
반응형

출처: KELP 이규명님의 강좌 http://kelp.or.kr/korweblog/stories.php?story=02/11/09/8557035&topic=29

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


== 시작
pixel 찍기(쓰기)와 pixel 읽기를 알아보았으므로 사실 frame buffer에 대한 프로그래밍은 다 알아봤다고 볼 수 있습니다. 적어도 지금까지 테스트한 16bpp 환경에서는 말이죠. 그런데 random pixel 찍기나 random 네모 그리기는 그야말로 데모 수준이지 그렇게 쓸모가 있는 프로그램은 아니죠. 또한 원하는 색깔의 pixel이 제대로 나타나는지도 정확하게 알 수 없죠. 그래서 지금까지 알아본 내용을 바탕으로 작성된 bmp 파일 display 프로그램을 소개합니다. 사실 bmp 파일은 MS와 IBM이 만들었으므로 linux와 친하다고 볼 수는 없습니다.(상식 : GNU/linux와 가장 친하지 않은 그래픽 파일형식은 GIF죠.) 그래도 굳이 bmp 파일형식을 사용한 이유는 파일 구조와 압축 방식이 비교적 간단하고 쉽게 구할 수 있는 이미지 파일형식이기 때문입니다.


== fbbmp 프로그램

= 사용법
그냥 압축 풀고 make로 build하면 fbbmp라는 프로그램이 만들어 집니다. bmp 파일 이름을 인자로하여 수행하면 화면에 그 파일을 display합니다.(당연히 그 전에 frame buffer driver가 제대로 올라가 있어야 하고 16bpp로 셋팅되어 있어야 합니다.) 네 개의 bmp 파일이 포함되어 있으니 테스트하면 됩니다.

= 지원 환경
(1) 16bpp mmap이 가능한 frame buffer
=> mmap이 지원되지 않으면 lseek/write로 pixel을 찍도록 코딩을 할 수도 그런 경우가 많지도 않고 귀찮으므로 그냥 작성하였습니다.
(2) little endian machine
=> bmp 파일 형식이 little endian으로 되어 있는데 big endian machine까지 지원하도록 만들기 위해서는 byte를 swap해야 하는데 귀찮아서 그냥 두었습니다.

= 지원하는 bmp 파일 형식
BI_RLE4 압축을 한 4bpp 파일을 제외한 모든 bmp 파일을 지원합니다.(코딩하기 귀찮고 어짜피 테스트도 못하기 때문에 BI_RLE4 4bpp를 제외했습니다.)

= 테스트된 bmp 파일 형식
(1) 1bpp 파일 : kelp_logo_1bpp.bmp
(2) 4bpp(BI_RGB : 즉 압축되지 않은) 파일 : kelp_logo_4bpp.bmp
(3) 8bpp(BI_RGB : 즉 압축되지 않은) 파일 : tux_resize_8bpp.bmp
(4) 24bpp 파일 : lixgreen_24bpp.bmp
지원하는 모든 bmp 파일 형식에 대해서 테스트하고 싶었으나 bmp 파일을 구할 수 없어서 어쩔 수 없이 4가지 형식에 대해서만 테스트를 진행했습니다. 포함되어 있는 모든 bmp 파일은 인터넷을 돌아다니면서 몇 가지 골라온 것을 적당한 툴로 변환한 것입니다.
당연히 다른 형식에서 바르게 작동하는지 검사하지 못했으므로 버그가 있을 가능성이 큽니다. 혹 디스플레이가 되지 않는 bmp 파일이 있으면 메일을 보내 주길 바랍니다.

= 약간의 문제
급히 작성한 소스코드라서 error처리가 완전하지 않습니다. 따라서 손상된 bmp파일을 입력하면 어떻게 될지 모르겠습니다.


== 소스코드 설명
소스코드 설명은 없습니다. 소스코드를 설명하려면 bmp 파일 구조부터 설명해야 하는데 그것은 “frame buffer 이야기”라는 제목과 어울리지 않아서 그만 둡니다. bmp 파일 구조에 대해서는 reference에 나와있는 사이트에서 있는 관련 문서에 자세히 나와 있습니다. 관심있는 사람은 bmp 파일형식에 대해 알아본 후 소스코드를 직접 살펴보길 바랍니다.


== 중대한 에러 발견
모든 프로그래밍을 끝내고 Permedia2 frame buffer에 디스플레이를 해보고 잘된다고 생각했습니다. 집에 가서 글을 적으며 ATI mach64 frame buffer에서 테스트를 해보았더니 색깔이 깨지며 원하는 색상이 나오지 않는 문제가 발견되었습니다. 그래서 어떤 문제인지 알아보았더니 16bit pixel encoding 문제가 있었습니다.
“Frame buffer 이야기(3)”에서 16bpp의 pixel은 Red 5bit, Green 6bit, Blue 5bit이 MSB에서부터 순서대로 붙어 있다고 말했습니다. 이렇게 구성된 16bpp를 보통은 high color라고 부릅니다. 6만5천(정확하게는 2^16=65536) color를 표현할 수 있죠. 하지만 특별한 시스템에서는 16bpp에서의 pixel이 그렇게 표현되지 않고 다음과 같이 표현되는 경우도 있습니다.
============================================
| NotUsed(1) | Red(5) | Green(5) | Blue(5) |
============================================
즉 최상위비트(MSB)는 사용하지 않고 Red, Green, Blue가 각각 5bit씩 사용됩니다. 어찌보면 15bpp라고 할 수도 있겠지만 결국 1 pixel이 16bit로 표현되므로 16bpp입니다. 이런 경우 당연히 6만5천 color를 표현하지 못하고 3만2천(정확하게는 2^15=32768) color를 표현할 수 있습니다.
일반적인 PC 시스템에서는 보통 16bpp라고 하면 R(5), G(6), B(5)로 coding된 경우를 일컫는 것으로 알고 있어서 그렇게 계속 이야기를 진행하고 나중에 다른 bpp에 관한 이야기를 할 때 이 문제를 잠시만 언급하려고 했습니다. 그런데 ATI mach64 frame buffer driver(RedHat 8.0)상의 16bpp는 R(5),G(5),B(5)인 것으로 나타났습니다. 그래서 색상이 깨진 것이죠. 이 문제를 해결할 수 있는 방법을 언급하고 넘어가도록 하겠습니다.

== 어떻게 구분하는가?
문제점을 해결하기 위해서 가장 간단한 방법은 프로그램을 두 가지 버전으로 만들어보고 잘 되는 것을 사용하는 것이겠지만, 이런 식의 해결은 많은 사람들이 좋아하는 방법이 아니죠. 그럼 하나의 프로그램이 두 가지 pixel encoding 형식을 모두 지원하도록 만드는 방법이 좋은데 어떻게 frame buffer driver가 어떤 pixel encoding 형식을 사용하는 지를 알아낼 수 있는가가 핵심이 되겠습니다.
실제로 알아내는 방법은 아주 간단합니다. ioctl(FBIOGET_VSCREENINFO)를 통해 얻은 fb_var_screeninfo 구조체에 모든 내용이 들어 있습니다. 그 구조체 안에 있는 bits_per_pixel이라는 member는 두 가지 pixel encoding 방식 모두 16의 값을 가지므로 구분을 위해 사용할 수 없습니다. 구조체 member중에 struct fb_bitfield type의 red, green, blue, transp라고 하는 것들이 있는데 그것을 통해서 알 수 있습니다.(/usr/include/linux/fb.h나 직접 kernel 소스의 include/linux/fb.h를 살펴보길 바랍니다.) transp의 경우는 나중에 32bpp에 관한 설명을 할 때 설명하도록 하고 지금은 무시하기로 하겠습니다.(보통 16bpp상에는 투명도는 잘 사용되지 않으므로. 이 가정도 잘못될 수 있기는 하진만요.)
struct fb_bitfield는 다음과 같습니다.
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is right*/
};
struct fb_bitfield red가 { 11, 5, 0 }의 값을 가진다면 이 의미는 pixel encoding에서 색상 Red가 11bit의 위치에서 시작하여 5 bit의 크기를 가지고 색상 Red를 나타내는 bit열의 MSB가 left(즉 전체 pixel encoding상 MSB쪽)임을 나타냅니다. msb_right는 정말 특수한 경우가 아니면 0이 되므로 무시하기로 합니다.
이제 R(5),G(6),B(5) 형식의 pixel encoding을 생각해 보면 fb_var_screeninfo내의 red, green, blue가 다음과 같은 값을 가지게 됩니다.
struct fb_bitfield red = { 11, 5, 0 };
struct fb_bitfield green = { 5, 6, 0 };
struct fb_bitfield blue = { 0, 5, 0 };
그리고 R(5),G(5),B(5) 형식의 pixel encoding의 경우는 다음과 같습니다.
struct fb_bitfield red = { 10, 5, 0 };
struct fb_bitfield green = { 5, 5, 0 };
struct fb_bitfield blue = { 0, 5, 0 };
이제 구분할 수 있겠죠.
사실 구분할 필요가 없습니다. Red, Blue, Green 각각의 색상이 연속적인(contiguous) bit열로 encoding되어 있는 모든 16bpp에 대해서 위의 세 개의 값을 통해서 나타낼 수 있습니다. 즉 16bpp라고 해서 pixel encoding이 꼭 R(5),G(6),B(5) 또는 R(5),G(5),B(5)의 경우가 아닐 수도 있고 그런 경우라고 해도 위의 세 개의 값을 이용하면 모두 encoding을 표현하는 것이 가능합니다. 즉 엽기적으로 R(14),G(1),B(1)의 pixel encoding도 표현할 수 있다는 말이죠.(물론 그야말로 엽기적인 경우일 뿐, 일반적인 상식으로는 별로 가능성을 고려할 필요가 없죠.)

== 수정 사항
fbpixel.c와 fbpixel2.c 에서 나온 makepixel이라는 함수를 바꾸어 16bpp의 경우 모두 지원할 수 있도록 만들어 보았습니다. 저번 makepixel 함수에 비해 길어졌지만 그냥 보기 쉽게 만들려고 한 것일 뿐, bit의 length와 offset을 고려한 것을 제외하면 더 복잡해 졌다고 볼 수는 없습니다.
===============================
typedef unsigned char ubyte;

static unsigned short makepixel(struct fb_var_screeninfo *pfbvar, ubyte r, ubyte g, ubyte b)
{
unsigned short rnew, gnew, bnew;

rnew = r >> (8-pfbvar->red.length);
gnew = g >> (8-pfbvar->green.length);
bnew = b >> (8-pfbvar->blue.length);

return (unsigned short) ((rnew << pfbvar->red.offset)
| (gnew << pfbvar->green.offset)
| (bnew << pfbvar->blue.offset));
}
===============================
이제 함수의 인자로 FBIOGET_VSCREENINFO ioctl을 통해 전달 받은 fb_var_screeninfo를 넘기고 있습니다. 그것의 red, green, blue의 값을 이용하여 pixel을 encoding하는 모습을 볼 수 있습니다. (fbbmp.c에서도 동일한 함수를 사용하였습니다.)
사실 좀더 정확하게 하려면 함수의 인자인 r, g, b가 unsigned char 형식이 아니라 unsigned short여야 가능한 모든 16bpp pixel encoding 형식을 지원할 수 있겠지만 일반적으로 16bpp에서 한 색상이 8bit이상은 되지 않을 것이라는 가정을 하고 그냥 사용합니다. 또한 8-length의 값이 음수가 될 수도 있으므로 그것을 고려해야 하겠지만 역시 같은 이유로 그냥 사용합니다.(shift 연산의 우측 operand가 음수일 때 C 언어의 behavior가 어떤지는 C 언어 reference 매뉴얼을 찾아봐야 하지만 귀찮으므로 넘어갑니다.)


== reference
(1) www.wotsit.com
=> 갖가지 파일 형식에 관한 문서들을 모아놓은 사이트로 bmp 파일 형식에 관한 문서도 다운로드 가능합니다.
(2) ezfb(www.akrobiz.com/ezfb/)
=> 소스코드를 다운로드하여 수행해 보길 바랍니다. 역시 bmp를 frame buffer에 display할 수 있는 프로그램뿐 아니라 frame buffer에서 pixel값을 읽어서 bmp로 저장하는 프로그램 등도 들어 있는 것으로 보입니다. 개인적으로 좋아하지 않는 코딩 스타일이라서 참고로 하지 않았습니다.


== 마치며
원래 이번 글은 소스코드만 올리고 그냥 짧게 넘어가려고 했는데 예기치 않은 문제가 발생하여 길어졌습니다. 물론 한 수 배운 셈이죠. 그리고 이번 글에서는 참 “귀찮은 것”이 많았는데 그냥 양해하길 바랍니다. 다음 글은 16bpp가 아닌 다른 bpp에서의 프로그래밍에 대해서 짤막하게 알아보도록 하겠습니다. 이제 “frame buffer 이야기”도 끝날 때가 머지 않았습니다.

728x90
반응형

'공부하는 하스씨 > Graphics' 카테고리의 다른 글

Frame Buffer 이야기 (9) - 마지막  (3) 2008.11.26
Frame Buffer 이야기 (8)  (0) 2008.11.26
Frame Buffer 이야기 (6)  (0) 2008.11.26
Frame Buffer 이야기 (5)  (0) 2008.11.26
Frame Buffer 이야기 (4)  (0) 2008.11.26