Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get RGB pixel values from Linux framebuffer?

I want to get RGB values of screen pixels in the most efficient way, using Linux. So I decided to use the framebuffer library in C (fb.h) to access the framebuffer device (/dev/fb0) and read from it directly.

This is the code:

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

int main() {

    int fb_fd;
    struct fb_fix_screeninfo finfo;
    struct fb_var_screeninfo vinfo;
    uint8_t *fb_p;

    /* Open the frame buffer device */
    fb_fd = open("/dev/fb0", O_RDWR);
    if (fb_fd < 0) {
        perror("Can't open /dev/fb0\n");
        exit(EXIT_FAILURE);
    }

    /* Get fixed info */
    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0) {
        perror("Can't get fixed info\n");
        exit(EXIT_FAILURE);
    }

    /* Get variable info */
    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
        perror("Can't get variable info\n");
        exit(EXIT_FAILURE);
    }

    /* To access to the memory, it can be mapped*/
    fb_p = (uint8_t *) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (fb_p == MAP_FAILED) {
        perror("Can't map memory\n");
        exit(EXIT_FAILURE);
    }

    /* Print each byte of the frame buffer */
    for (int i = 0; i < finfo.smem_len; i++) {
        printf("%d\n", *(fb_p + i));

        // for (int j = 0; j < 500000000; j++);       /* Delay */
    }

    munmap(fb_p, 0);
        close(fb_fd);

    return 0;
}

But when I print the values, I don't get what I was expecting...

If I pick the RGB value of the pixel (0, 0) with a tool like grabc, I get this:

#85377e
133,55,126

But the first printings with my code are:

126
145
198
...

It looks like I am obtaining well the first value of the first pixel, corresponding to blue, but the rest is wrong.

like image 505
Sergio Avatar asked Oct 21 '17 18:10

Sergio


2 Answers

If you look at the grabc source code (https://www.muquit.com/muquit/software/grabc/grabc.html), you'll see that he's querying X Windows (and NOT the hardware frame buffer per se).

root_window=XRootWindow(display,XDefaultScreen(display));
target_window=selectWindow(display,&x,&y);
...
ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap);
...
color->pixel=XGetPixel(ximage,0,0);
XDestroyImage(ximage);

For whatever it's worth - and for several different reasons - I'd strongly encourage you to consider doing the same.

You might also be interested in:

  • Intro to Low-Level Graphics on Linux

  • DirectFB

like image 125
paulsm4 Avatar answered Oct 01 '22 21:10

paulsm4


In some rare buggy device using fbdev you may need to align the mmap returned pointer to the next system page in order to get the first pixel correctly, and by doing so you may also need to mmap one more page.

I don't think that's your case, it seems like you're trying to get the xserver desktop pixels by using linux fbdev, unless your xserver is configured to use the fbdev driver (and even so I'm not sure would work) isn't gonna work. If reading the desktop pixels is your target you should look into some screenshot tool.

Beside, fbdev drivers are usually really basic, is a low-level access but there's nothing efficient about mmapping and reading pixels, the Xlib or any higher level graphic system may be aware and support your graphic card accelerations and be far more efficient (perhaps using DMA to read the whole framebuffer to your system memory without loading the cpu).

like image 20
Alex Avatar answered Oct 01 '22 20:10

Alex