programing

Linux 프레임 버퍼를 통해 화면에 픽셀 그리기

javamemo 2023. 7. 14. 23:21
반응형

Linux 프레임 버퍼를 통해 화면에 픽셀 그리기

저는 최근에 /dev/urandom에서 입력을 가져와서 관련 문자를 임의의 정수로 변환하고 이러한 정수를 화면에 그릴 픽셀의 rgb/x-y 값으로 사용하는 흥미로운 아이디어에 놀랐습니다.

(StackOverflow 등에 대해) 몇 가지 조사를 해 보았는데, /dev/fb0이 장치의 파일 표현이므로 직접 /dev/fb0에 쓸 수 있다고 제안하는 경우가 많습니다.불행하게도, 이것은 시각적으로 명백한 결과를 만들어내지 못하는 것처럼 보입니다.

저는 버퍼에 쓰기 위해 mmap을 사용하는 QT 튜토리얼(더 이상 사용할 수 없음)에서 샘플 C 프로그램을 찾았습니다.프로그램이 성공적으로 실행되지만 화면으로 출력되지 않습니다.흥미롭게도 랩톱을 일시 중단 상태로 두었다가 나중에 복원했을 때 훨씬 이전에 프레임 버퍼에 기록된 이미지(빨간색 사각형)가 순간적으로 깜박이는 것을 보았습니다.프레임 버퍼에 쓰는 것이 리눅스에서 더 이상 화면에 페인트칠을 할 수 있습니까?이상적으로는 (bash)script를 작성하고 싶지만, C나 비슷한 것도 가능합니다.감사합니다!

편집: 여기 샘플 프로그램이 있습니다... 수의사들에게 친숙해 보일 수 있습니다.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int main()
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;
    char *fbp = 0;
    int x = 0, y = 0;
    long int location = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    x = 100; y = 100;       // Where we are going to put the pixel

    // Figure out where in memory to put the pixel
    for (y = 100; y < 300; y++)
        for (x = 100; x < 300; x++) {

            location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                       (y+vinfo.yoffset) * finfo.line_length;

            if (vinfo.bits_per_pixel == 32) {
                *(fbp + location) = 100;        // Some blue
                *(fbp + location + 1) = 15+(x-100)/2;     // A little green
                *(fbp + location + 2) = 200-(y-100)/5;    // A lot of red
                *(fbp + location + 3) = 0;      // No transparency
        //location += 4;
            } else  { //assume 16bpp
                int b = 10;
                int g = (x-100)/6;     // A little green
                int r = 31-(y-100)/16;    // A lot of red
                unsigned short int t = r<<11 | g << 5 | b;
                *((unsigned short int*)(fbp + location)) = t;
            }

        }
    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

저는 다음과 같은 몇 가지 실험으로 성공했습니다.

먼저 X가 32비트로 패딩된 TrueColor RGB를 사용하는지 확인합니다(또는 이 경우를 가정합니다).그런 다음 fb0에 대한 쓰기 권한이 있는지 확인하고 해당 권한이 있는지 확인합니다.만약 이것들이 사실이라면 (그리고 많은 최신 툴킷/데스크탑/PC들이 이것들을 기본값으로 사용할 것으로 예상됩니다) 다음을 수행할 수 있을 것입니다. 그리고 이러한 기본값이 유지되지 않는다면, 세부 사항은 다를 수 있지만 다음 테스트를 통해 어느 정도 성공을 거둘 수 있을 것입니다.

테스트 1: 가상 터미널(X)을 열고 $ echo "dddd ... ddd" > /dev/fb0을 입력합니다. 여기서 ...은 실제로 몇 개의 화면 가득 d입니다.에코 문자열의 길이와 활성화한 픽셀 해상도에 따라 화면 상단에 회색 선이 하나 이상 표시됩니다.원하는 문자를 선택할 수도 있습니다(아스키 값이 모두 0x80보다 작으므로 생성되는 색상은 진한 회색이 됩니다).그리고 회색 이외의 것을 원한다면 글자를 변경합니다.)분명히, 이것은 셸 루프로 일반화되거나 효과를 더 명확하게 보기 위해 큰 파일을 캐팅할 수 있습니다: 예: $cat /lib/libc.so .6 > /dev/fb0. 일부 fsf 서포터의 실제 색상을 보기 위해;-P.

화면의 큰 부분을 덮어쓰더라도 걱정할 필요가 없습니다.X는 여전히 마우스 포인터를 제어하고 창이 매핑된 위치에 대한 아이디어를 가지고 있습니다.여러분이 해야 할 일은 아무 창문이나 잡고 소음을 없애기 위해 그것을 조금만 끌고 다니는 것입니다.

테스트 2: cat /dev/fb0 > xxx 그런 다음 바탕 화면의 모양을 변경합니다(예: 새 창을 열고 다른 창을 닫습니다).마지막으로, 이전 데스크톱을 다시 가져오려면 반대로 cat xxx > /dev/fb0을 수행하십시오!

하, 글쎄요, 아직은 아닙니다.이전 데스크톱의 이미지는 착각이며, 창을 열어 전체 화면을 표시할 때 빠르게 이미지를 제거할 수 있습니다.

테스트 3: /dev/fb0의 이전 덤프를 잡고 픽셀의 색상을 수정하는 작은 앱을 작성합니다. 예를 들어 빨간색 구성 요소를 제거하거나 파란색을 확대하거나 빨간색과 녹색을 뒤집습니다.그런 다음 테스트 2의 간단한 셸 접근 방식을 통해 나중에 볼 수 있는 새 파일에 이 픽셀을 다시 기록합니다.또한 픽셀당 B-G-R-A 4바이트 수를 처리하게 될 가능성이 높습니다.즉, 모든 4번째 바이트를 무시하고 각 세트의 첫 번째 바이트를 파란색 성분으로 처리해야 합니다."ARGB"는 빅 엔디안이므로 C 어레이의 인덱스 증가를 통해 이러한 바이트를 방문하면 파란색이 먼저 오고 녹색이 다음에 빨간색이 됩니다.즉, B-G-R-A(A-R-G-B가 아님).

테스트 4: 윈도우 테두리가 없는 애니메이션을 만들 수 있도록 사각형이 아닌 그림(xyes)을 화면의 일부로 전송하여 비디오 속도로 루프하는 모든 언어로 앱을 작성합니다.추가 포인트를 얻으려면 애니메이션을 화면 전체로 이동합니다.작은 행에 해당하는 픽셀을 그린 후에는 큰 공간을 건너뛰어야 합니다(애니메이션 중인 사진보다 화면 너비가 훨씬 넓을 수 있음).

테스트 5: 친구에게 속임수를 부립니다. 예를 들어, 테스트 4를 확장하면 애니메이션 인물의 사진이 바탕 화면에 나타납니다(아마도 픽셀 데이터를 얻기 위해 직접 촬영). 그런 다음 중요한 바탕 화면 폴더 중 하나로 가서 폴더를 집어들고 조각조각 찢고 히스테리컬하게 웃기 시작합니다.불덩어리가 나와서 그들의 책상 전체를 집어삼키게 합니다.이 모든 것이 환상일 것이지만, 그들은 약간 당황할지도 모릅니다.하지만 그것을 학습 경험으로 사용하여 Linux와 오픈 소스를 자랑하고 초보자에게 실제보다 훨씬 더 무섭게 보이는 것을 보여줍니다.[리눅스에서 "바이러스"는 일반적으로 무해한 환상입니다.]

X11을 실행 중인 경우 X11 API를 사용하여 화면에 그려야 합니다.X 서버 주변을 도는 것은 매우 실패적입니다(그리고 여러분이 보신 것처럼, 종종 작동하지 않습니다).또한 충돌이 발생하거나 일반 디스플레이가 손상될 수 있습니다.

콘솔과 X 아래 모두에서 모든 곳에서 실행할 수 있도록 하려면 SDL 또는 GGI를 확인하십시오.만약 당신이 X11에만 관심이 있다면, 당신은 GTK, QT, 심지어 Xlib도 사용할 수 있습니다.많은 선택지가 있습니다...

위에서 제안한 대로 /dev/fb0에 쓰기 전에 주의해야 합니다.나는 unbuntu 10.04의 X 아래에서 그것을 시도했지만 a) 시각적으로 아무 일도 일어나지 않았고, b) 그것은 모든 셸 창과 심지어 다른 tyt를 파괴하여 커널 오류와 기능 부족으로 이어졌습니다.

스크롤(SDR waterfall)을 할 수 있어야 하기 때문에 프레임 버퍼 기반의 프로그램을 작성할 생각입니다.https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567 을 참조하십시오. 스크롤 테스트는 성공적이라고 생각합니다.이 두 개의 구조물에서 우리는 ioctl을 통해 정보를 얻습니다. 색 깊이에 대한 것도 있습니다.당신은 제가 했던 것과 같은 예를 바탕으로 당신의 프로그램을 만든 것 같습니다.Linux에서 프레임 버퍼에서 픽셀 색상을 가져오는 방법(Raspberry Pi)

X가 있든 없든 내 라즈베리 파이는 잘 작동합니다.그것은 내 노트북의 화면에 영향을 미치지 않습니다./dev/fb0가 있고 프로그램이 실행되고 숫자가 올바르게 표시되지만 시각적으로는 아무것도 하지 않습니다.아마 더블 버퍼 같은 것일 겁니다.

X에서는 실제로 손상을 입히지 않습니다.창문을 끌어서 다시 그리면 모든 것이 되돌아옵니다.그리고 나서 저는 화면에 있는 것을 백업하고 제가 끝나면 그것을 다시 갖다놓기로 결심했습니다. 그것도 작동합니다.내가 움직였다가 다시 갖다 놓은 창문은 마치 내가 한 번도 만져본 적이 없는 것처럼 똑같이 작동합니다.X는 내가 화면 버퍼를 잘못 사용했다는 것을 깨닫지 못하고, 거기에 무엇을 넣었는지 알고 그에 따라 마우스 클릭을 등록합니다.창을 이동하고 다시 놓지 않으면 클릭이 원래 위치에서 계속 작동합니다.

화면 크기는 직접 곱셈을 하는 대신 fb_fix_screeninfo.sem_len을 사용해야 합니다.버퍼가 4바이트 또는 다른 것에 정렬되어 있을 수 있습니다.

screensize = finfo.smem_len;

당신이 당신의 프로그램을 디버그하면, 당신은 다음과 같은 행을 발견할 것입니다.

 screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

screensize0 때문에입니다. vinfo는 0입니다.xres는 0으로 . 다음으로 변경해야 합니다.

long ppc_fx = (((long)fixed_info.smem_start) - ((long) fixed_info.smem_start & ~(PAGE_SIZE-1)));
screensize = finfo.smem_len + ppc_fx;

2의 두 , mmap()의 두 번째 인수,screensize0이면 안 됩니다. 그렇지 않으면 mmap()이 MAP_FAILED를 반환합니다.

제가 이 프로그램을 사용하여 전체 화면을 작성했을 때 화면 크기 계산이 잘못되었기 때문에 충돌했습니다.

// Figure out the size of the screen in bytes     
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

이는 다음과 같습니다.

/* Calculate the size of the screen in bytes */   
screensize = vinfo.xres_virtual * vinfo.yres_virtual * (vinfo.bits_per_pixel / 8);

언급URL : https://stackoverflow.com/questions/4996777/paint-pixels-to-screen-via-linux-framebuffer

반응형