Graphic Programming using Frame Buffer on Linux

Home / Graphic Programming using Frame Buffer on Linux

Graphic Programming using Frame Buffer on Linux

December 3, 2015 | Article | No Comments

Graphics programming is one of interesting subject. While there are many graphic libraries developed, have you ever wonder what operations are performed at lowest level? have you ever wonder, how can your Operating System visualizing any 2D object in your monitor? There is a simple answer, they utilize a frame buffer.

A frame buffer is one of your computer’s component. A frame buffer (sometimes called as frame store) is a video output device that drives a video display from a memory buffer containing a complete frame of data. Frame buffer typically hold color values for every pixel (point that can be displayed) on screen. Color values are commonly stored in 1-bit binary (for monochrome), 4-bit palettized, 8-bit palettized, 16-bit highcolor, and 24-bit truecolor formats. The total amount of the memory required to drive the framebuffer depends on the resolution of the output signal, and on the color depth and palette size.

In this article we will discuss about how to programming, or manipulate a frame buffer. We will also cover a small example as proof of concept.

To use, switch to another tty by pressing ctrl+alt+f(n) where f(n) mean f1 to f7. We did this to see our work. This is because we can’t see the result from terminal within graphical environment. Also it happens that on some system we need to use root privilege to access frame buffer. You can do so by switching to root.

The Special Device File

Everything in Unix and Linux is treated as file, including hardware. In linux which a frame buffer is treated as special device and can be found at /dev/fb0. To access this file we will use low level function too, open().

We will open /dev/fb0 using open() function and get a file descriptor returned by open() function. Save this file descriptor in a variable (integer). Please note that this file descriptor is important as we will manipulate frame buffer later by this file descriptor.

Now, have you ever done file I/O before? Linux may treat everything as file, but we can simply manipulate frame buffer only by reading or writing something to /dev/fb0 (Actually you can, but it’s a little complicated and risky).

Mapping the Frame Buffer to Memory

Instead of doing simple file I/O operation, we will map the frame buffer to memory. To do so we need the file descriptor we got from open() function and pass it to mmap() function. Every single pixel of frame buffer would be mapped into an array on memory. This give us flexibility to traverse the frame buffer or to manipulate any pixel as we want.

But wait, before we did that we must get the actual condition / characteristic of the frame buffer. Information we need is fix screen information and variable screen information of frambuffer. With these information we can determine, how many length, what is the size of frame buffer, what is the maximum width and maximum height, etc.

To get both fix screen information and variable screen information we will use ioctl() function.

The Structure of Pixel Format

Now we have map the frame buffer to memory, what next? Our objective is accessing a single pixel. To accomplish this let me tell you the mapped frame buffer on memory.

The mapped frame buffer is simply (i would say) array of struct with single element of array is a single pixel. The problem is, what bit that frame buffer store for each pixel. You know, there’re some formats just as I said before. Normally frame buffer use either 24 bit per pixel (true color) or 16 bit per pixel (high color). Fortunately, once we have information about variable screen we can determine the bit per pixel.

Now, different format need different technique to manipulate pixel. This is because they used different structure to allocate every pixel. If you encounter 24-bit true color format, it would be easy for you. For every color of RGB you can use simple short integer / char. So in short, you will have 8-bit for Red color, 8-bit for Green color, and 8-bit for Blue color. How about 16-bit high color? For 16-bit high color you have different situation. You have 5-bit for Red color, 6-bit for Green color, and 5-bit for Blue color.

Now look at this picture for 16-bit high color format:

The Codes

Ok, I have present you the basic concept. Now let’s move to the actual code.

edit 1: modify the mapping so it can prevent segmentation fault problem. Thanks to Kamil Muszyński

#include <unistd.h>
#include <fcntl.h>		/* for fcntl */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>		/* for mmap */
#include <sys/ioctl.h>
#include <linux/fb.h>

#include <stdio.h>
#include <stdlib.h>

int main() {
	long int screensize = 0;
	struct fb_var_screeninfo vinfo;
	struct fb_fix_screeninfo finfo;
	int fbfd;					/* frame buffer file descriptor */
	char* fbp;					/* pointer to framebuffer */
	int location;					/* iterate to location */

	int x, y;					/* x and y location */

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

	/* get the fixed screen information */
	if (ioctl (fbfd, FBIOGET_FSCREENINFO, &finfo)) {
		printf("Error reading fixed information.\n");
		exit(2);
	}

	/* get variable screen information */
	if (ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
		printf("Error reading variable information.\n");
		exit(3);
	}

	/* 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, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);

	if ((int)fbp == -1) {
		printf ("Error: failed to map framebuffer device to memory.\n");
		exit(4);
	}
	printf ("Framebuffer device was mapped to memory successfully.\n");

	// Figure out where in memory to put the pixel
	for ( y = 0; y < (vinfo.yres/2); y++ )
	    for ( x = 0; x < vinfo.xres; 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 
	        } 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;
}

That code map our frame buffer into contiguous array of bytes on RAM. That’s why we declare frame buffer pointer as char (byte). The memmap() is used to this purpose.

fbp = (char*)mmap(0,
                    screensize,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED,
                    fbfd, 0);

Which map to the memory with read / write capability and shared.

Any questions?

,

About Author

about author

xathrya

A man who is obsessed to low level technology.

Leave a Reply

Your email address will not be published. Required fields are marked *

Social media & sharing icons powered by UltimatelySocial