Search code examples
linuxqtembedded-linuxframebufferdrm

Linux DRM ( DRI ) Cannot Screen Scrape /dev/fb0 as before with FBDEV


On other Linux machines using the FBDEV drivers ( Raspberry Pi.. etc. ), I could mmap the /dev/fb0 device and directly create a BMP file that saved what was on the screen.

Now, I am trying to do the same thing with DRM on a TI Sitara AM57XX ( Beagleboard X-15 ). The code that used to work with FBDEV is shown below.

This mmap no longer seems to work the DRM. I'm using a very simple Qt5 Application with the Qt platform linuxfb plugin. It draws just fine into /dev/fb0 and shows on the screen properly, however I cannot read back from /dev/fb0 with a memory mapped pointer and have an image of the screen saved to file. It looks garbled like this:

enter image description here

Code:

#ifdef FRAMEBUFFER_CAPTURE

    repaint();
    QCoreApplication::processEvents();

    // Setup framebuffer to desired format
    struct fb_var_screeninfo var;
    struct fb_fix_screeninfo finfo;
    memset(&finfo, 0, sizeof(finfo));
    memset(&var, 0, sizeof(var));
    /* Get variable screen information. Variable screen information
    * gives information like size of the image, bites per pixel,
    * virtual size of the image etc. */
    int fbFd = open("/dev/fb0", O_RDWR);
    int fdRet = ioctl(fbFd, FBIOGET_VSCREENINFO, &var);
    if (fdRet < 0) {
        qDebug() << "Error opening /dev/fb0!";
        close(fbFd);
        return -1;
    }

    if (ioctl(fbFd, FBIOPUT_VSCREENINFO, &var)<0) {
        qDebug() << "Error setting up framebuffer!";
        close(fbFd);
        return -1;
    } else {
        qDebug() << "Success setting up framebuffer!";
    }

    //Get fixed screen information
    if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) < 0) {
        qDebug() << "Error getting fixed screen information!";
        close(fbFd);
        return -1;
    } else {
        qDebug() << "Success getting fixed screen information!";
    }

    //int screensize = var.xres * var.yres * var.bits_per_pixel / 8;
    //int screensize = var.yres_virtual * finfo.line_length;
    //int screensize = finfo.smem_len;
    int screensize = finfo.line_length * var.yres_virtual;
    qDebug() << "Framebuffer size is: " << var.xres << var.yres << var.bits_per_pixel << screensize;
    int linuxFbWidth = var.xres;
    int linuxFbHeight = var.yres;

    int location = (var.xoffset) * (var.bits_per_pixel/8) +
                           (var.yoffset) * finfo.line_length;

    // Perform memory mapping of linux framebuffer
    char* frameBufferMmapPixels = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbFd, 0);
    assert(frameBufferMmapPixels != MAP_FAILED);

    QImage toSave((uchar*)frameBufferMmapPixels,linuxFbWidth,linuxFbHeight,QImage::Format_ARGB32);
    toSave.save("/usr/bin/test.bmp");
    sync();

#endif

Here is the output of the code when it runs:

Success setting up framebuffer!
Success getting fixed screen information!
Framebuffer size is:  800 480 32 1966080

Here is the output of fbset showing the pixel format:

mode "800x480"
    geometry 800 480 800 480 32
    timings 0 0 0 0 0 0 0
    accel true
    rgba 8/16,8/8,8/0,8/24
endmode

root@am57xx-evm:~#

Solution

  • finfo.line_length gives the size of the actual physical scan line in bytes. It is not necessarily the same as screen width multiplied by pixel size, as scan lines may be padded.

    However the QImage constructor you are using assumes no padding.

    If xoffset is zero, it should be possible to construct a QImage directly from the framebuffer data using a constructor with the bytesPerLine argument. Otherwise there are two options:

    • allocate a separate buffer and copy only the visible portion of each scanline to it
    • create an image from the entire buffer (including the padding) and then crop it