Try reading and writing the framebuffer (/ dev / fbXX) on Linux. This article specifically mentions it for the Raspberry Pi, but I think it's the same for other Linux. The confirmed environment is Raspberry Pi 2, the image output is HDMI, and the resolution is DMT mode 9 (800x600). Main use:
――I want to output the screen of Raspberry Pi to my own display device ――It's easier to use https://github.com/notro/fbtft/ for this purpose. --Send the Raspberry Pi screen over the network and try a similar non-remote desktop ――I'm thinking of making an app, but it's troublesome to use a graphic engine. I want to output directly to an HDMI display without thinking about difficult things.
On Linux, you can access the framebuffer through the device file (/ dev / fbXX). It is prepared as / dev / fb0
in Raspberry Pi. This content is output to the display or HDMI.
You can save the image data by typing the following from the command line (same as screen capture). If you do the opposite, you can write (draw on the display).
cat /dev/fb0 > aaa.raw
The acquired RAW image data is composed of 4 bytes (32 bits) such as ARGB per pixel. This is raw pixel data, unlike JPEG and BMP. Therefore, it is necessary to specify the size etc. when playing. You can get the information you need with the following command.
fbset
mode "800x600"
geometry 800 600 800 600 32
timings 0 0 0 0 0 0 0
rgba 8/16,8/
Alternatively, you can get the same result with the following command.
cat /sys/class/graphics/fb0/bits_per_pixel
32
cat /sys/class/graphics/fb0/virtual_size
800,600
This is a setting example when viewing with IrfanView. You can make various settings by dragging and dropping an image with a RAW extension. If your display has the above settings, you should be able to see it correctly with the following settings. (The setting value changes depending on the environment)
--Is the size of the framebuffer halfway? --Set the type to DMT in the Raspy screen resolution setting. CEA is smaller than the specified size (because of the blanking area for TV data information?) --Does the saved file size not fit the calculation? --Probably because the horizontal and vertical sizes are rounded up to 32 pixels. --File size = horizontal (px) x vertical (px) x 4Byte --On a remote desktop such as VNC, writing to the framebuffer and drawing something on the display may not update anything.
It should have been painted in red, but with VNC it will not be fully updated. Looking at HDMI, it is filled properly.
This is the code to get the framebuffer information. There are two ways to do it. The result is the same regardless of which one you do. getFrameBufferSize ()
reads the information as a string from the above / sys / class / graphics / fb0 / bits_per_pixel
. getFrameBufferSize2 ()
uses ʻioctl ()` to get device information.
getSizeFrameBuffer.cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <linux/omapfb.h>
void getFrameBufferSize(int* width, int* height, int* colorWidth)
{
int fd;
int n;
char str[64];
/* Get Bit Per Pixel (usually 32-bit(4Byte) */
fd = open("/sys/class/graphics/fb0/bits_per_pixel", O_RDONLY);
n = read(fd, str, sizeof(str));
str[n] = '\0';
*colorWidth = atoi(str);
close(fd);
/* Get Frame Buffer Size */
fd = open("/sys/class/graphics/fb0/virtual_size", O_RDONLY);
n = read(fd, str, sizeof(str));
str[n] = '\0';
/* divide "800,600" into "800" and "600" */
*width = atoi(strtok(str, ","));
*height = atoi(strtok(NULL, ","));
close(fd);
}
void getFrameBufferSize2(int* width, int* height, int* colorWidth)
{
struct fb_var_screeninfo var;
int fd;
fd = open("/dev/fb0", O_RDWR);
ioctl(fd, FBIOGET_VSCREENINFO, &var);
*colorWidth = var.bits_per_pixel;
*width = var.xres_virtual;
*height = var.yres_virtual;
close(fd);
}
Code that reads image data from the frame buffer. It does the same thing as cat above by opening, reading and closing to / dev / fb0
. You can also read by mmaping / dev / fb0
. They are readBuffer ()
and readBuffer2 ()
, respectively. You can read the data using either one, but depending on how you use it, useless memory copy may occur, so it is better to use it according to the purpose.
readFrameBuffer.cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <linux/omapfb.h>
void readBuffer(int width, int height, void* dstBuffer)
{
int fd;
fd = open("/dev/fb0", O_RDONLY);
read(fd, dstBuffer, width * height * 4);
close(fd);
}
void readBuffer2(int width, int height, void* dstBuffer)
{
int fd;
fd = open("/dev/fb0", O_RDONLY);
uint32_t *frameBuffer = (uint32_t *)mmap(NULL, width * height * 4, PROT_READ, MAP_SHARED, fd, 0);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
((uint32_t*)dstBuffer)[x + y * width] = frameBuffer[x + y * width];
}
}
munmap(frameBuffer, width * height * 4);
close(fd);
}
Code that writes image data to the framebuffer. This allows you to draw directly on the display (HDMI output). However,
--The screen may not be updated with VNC --In both GUI and CUI, if there is a screen drawing on the main body (OS?) Side, that part (not the entire screen) will be overwritten.
There is a problem such as. It would be nice to have a way to completely hijack access to the framebuffer, but we're investigating.
You can write by opening, writing, and closing to / dev / fb0
. Here, as an example, a buffer filled with a single color is output. You can do the same with mmap as well as with reading. I think that mmap will be more efficient depending on the application because it can write directly. However, when using mmap, be aware that the value may not actually be written until you call msync
.
writeFrameBuffer.cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <linux/omapfb.h>
// color = AARRGGBB
void drawColor(int width, int height, int color)
{
uint32_t *canvas = new uint32_t[width * height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
canvas[x + y * width] = color;
}
}
int fd;
fd = open("/dev/fb0", O_WRONLY);
write(fd, canvas, width * height * 4);
close(fd);
delete[] canvas;
}
// color = AARRGGBB
void drawColor2(int width, int height, int color)
{
int fd;
fd = open("/dev/fb0", O_RDWR);
uint32_t *frameBuffer = (uint32_t *)mmap(NULL, width * height * 4, PROT_WRITE, MAP_SHARED, fd, 0);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
frameBuffer[x + y * width] = color;
}
}
msync(frameBuffer, width * height * 4, 0);
munmap(frameBuffer, width * height * 4);
close(fd);
}
An example is reading the framebuffer information, taking a screen capture, and filling the entire screen in red. However, even if it is painted in red, the area near the cursor in CUI mode and the area near the menu bar in GUI mode will be overwritten by the OS (it seems that the drawing is updated only where there is a change on the screen).
Compile with g ++ main.cpp getSizeFrameBuffer.cpp readFrameBuffer.cpp writeFrameBuffer.cpp
main.cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void getFrameBufferSize(int* width, int* height, int* colorWidth);
void getFrameBufferSize2(int* width, int* height, int* colorWidth);
void drawColor(int width, int height, int color);
void drawColor2(int width, int height, int color);
void readBuffer(int width, int height, void* dstBuffer);
void readBuffer2(int width, int height, void* dstBuffer);
void saveFileBinary(const char* filename, void* data, int size);
// g++ main.cpp getSizeFrameBuffer.cpp readFrameBuffer.cpp writeFrameBuffer.cpp
int main()
{
int colorWidth;
int width;
int height;
/* get frame buffer size */
getFrameBufferSize(&width, &height, &colorWidth);
printf("%d(W) x %d(H) x %d(Bit)\n", width, height, colorWidth);
/* screen capture */
uint32_t *buffer = new uint32_t[width * height];
readBuffer(width, height, buffer);
saveFileBinary("capture.raw", buffer, width * height * 4);
delete[] buffer;
/* Fill solid color */
drawColor(width, height, 0xFFFF0000); // RED
}
void saveFileBinary(const char* filename, void* data, int size)
{
FILE *fp;
fp = fopen(filename, "wb");
fwrite(data, 1, size, fp);
fclose(fp);
}
Todo --Investigation of how to completely hijack the framebuffer (currently, it is partially overwritten by the OS) --I think the correct way is to create a separate framebuffer (/ dev / fb1) and use that for HDMI output. ――I think it's a double buffer, but what is it like in / dev / fb?
http://archive.linux.or.jp/JF/JFdocs/kernel-docs-2.6/fb/framebuffer.txt.html
Recommended Posts