175 lines
5.3 KiB
C
175 lines
5.3 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/ioctl.h>
|
||
|
#include <linux/fb.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <cairo/cairo.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#define DELAY_IN_SECS 5
|
||
|
#define SCALE_WIDTH 256.0
|
||
|
#define SCALE_HEIGHT 256.0
|
||
|
|
||
|
typedef struct _cairo_linuxfb_device {
|
||
|
int fb_fd;
|
||
|
unsigned char *fb_data;
|
||
|
long fb_screensize;
|
||
|
struct fb_var_screeninfo fb_vinfo;
|
||
|
struct fb_fix_screeninfo fb_finfo;
|
||
|
} cairo_linuxfb_device_t;
|
||
|
|
||
|
/* Destroy a cairo surface */
|
||
|
void cairo_linuxfb_surface_destroy(void *device)
|
||
|
{
|
||
|
cairo_linuxfb_device_t *dev = (cairo_linuxfb_device_t *)device;
|
||
|
|
||
|
if (dev == NULL)
|
||
|
return;
|
||
|
|
||
|
munmap(dev->fb_data, dev->fb_screensize);
|
||
|
close(dev->fb_fd);
|
||
|
free(dev);
|
||
|
}
|
||
|
|
||
|
/* Create a cairo surface using the specified framebuffer */
|
||
|
cairo_surface_t *cairo_linuxfb_surface_create(const char *fb_name)
|
||
|
{
|
||
|
cairo_linuxfb_device_t *device;
|
||
|
cairo_surface_t *surface;
|
||
|
|
||
|
/* Use fb0 if no fram buffer is specified */
|
||
|
if (fb_name == NULL) {
|
||
|
fb_name = "/dev/fb0";
|
||
|
}
|
||
|
|
||
|
device = malloc(sizeof(*device));
|
||
|
if (!device) {
|
||
|
perror("Error: cannot allocate memory\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
// Open the file for reading and writing
|
||
|
device->fb_fd = open(fb_name, O_RDWR);
|
||
|
if (device->fb_fd == -1) {
|
||
|
perror("Error: cannot open framebuffer device");
|
||
|
goto handle_allocate_error;
|
||
|
}
|
||
|
|
||
|
// Get variable screen information
|
||
|
if (ioctl(device->fb_fd, FBIOGET_VSCREENINFO, &device->fb_vinfo) == -1) {
|
||
|
perror("Error: reading variable information");
|
||
|
goto handle_ioctl_error;
|
||
|
}
|
||
|
|
||
|
// Figure out the size of the screen in bytes
|
||
|
device->fb_screensize = device->fb_vinfo.xres * device->fb_vinfo.yres
|
||
|
* device->fb_vinfo.bits_per_pixel / 8;
|
||
|
|
||
|
// Map the device to memory
|
||
|
device->fb_data = (unsigned char *)mmap(0, device->fb_screensize,
|
||
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||
|
device->fb_fd, 0);
|
||
|
if ((int)device->fb_data == -1) {
|
||
|
perror("Error: failed to map framebuffer device to memory");
|
||
|
goto handle_ioctl_error;
|
||
|
}
|
||
|
|
||
|
// Get fixed screen information
|
||
|
if (ioctl(device->fb_fd, FBIOGET_FSCREENINFO, &device->fb_finfo) == -1) {
|
||
|
perror("Error reading fixed information");
|
||
|
goto handle_ioctl_error;
|
||
|
}
|
||
|
|
||
|
/* Create the cairo surface which will be used to draw to */
|
||
|
surface = cairo_image_surface_create_for_data(device->fb_data,
|
||
|
CAIRO_FORMAT_RGB16_565,
|
||
|
device->fb_vinfo.xres,
|
||
|
device->fb_vinfo.yres,
|
||
|
cairo_format_stride_for_width(CAIRO_FORMAT_RGB16_565,
|
||
|
device->fb_vinfo.xres));
|
||
|
cairo_surface_set_user_data(surface, NULL, device,
|
||
|
&cairo_linuxfb_surface_destroy);
|
||
|
|
||
|
return surface;
|
||
|
|
||
|
handle_ioctl_error:
|
||
|
close(device->fb_fd);
|
||
|
handle_allocate_error:
|
||
|
free(device);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
int image_width;
|
||
|
int image_height;
|
||
|
char frame_buffer_number;
|
||
|
char fb_node[16] = {0};
|
||
|
cairo_surface_t *surface;
|
||
|
cairo_surface_t *image;
|
||
|
cairo_t *cr;
|
||
|
|
||
|
if (argc != 2) {
|
||
|
printf("Usage: ./cairo /path/to/png/image\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (strstr(argv[1], ".png") == NULL) {
|
||
|
printf("Only png images are supported with this example\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
printf("Enter frame buffer number:\t");
|
||
|
scanf("%c", &frame_buffer_number);
|
||
|
sprintf(fb_node, "/dev/fb");
|
||
|
fb_node[strlen(fb_node)] = frame_buffer_number;
|
||
|
fb_node[strlen(fb_node)] = '\0';
|
||
|
printf("Frame buffer node is: %s\n", fb_node);
|
||
|
|
||
|
surface = cairo_linuxfb_surface_create(fb_node);
|
||
|
cr = cairo_create(surface);
|
||
|
|
||
|
/*
|
||
|
* We clear the cairo surface here before drawing
|
||
|
* This is required in case something was drawn on this surface
|
||
|
* previously, the previous contents would not be cleared without this.
|
||
|
*/
|
||
|
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
|
||
|
cairo_paint(cr);
|
||
|
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||
|
|
||
|
cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
|
||
|
cairo_set_font_size(cr, 32.0);
|
||
|
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
|
||
|
cairo_move_to(cr, 100, 300);
|
||
|
cairo_show_text(cr, "Toradex Cairo Example!");
|
||
|
|
||
|
/* Wait for the result of drawing operation to persist for the user to see */
|
||
|
sleep(DELAY_IN_SECS);
|
||
|
/* Clear the surface and prepare for a new drawing operation */
|
||
|
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
|
||
|
cairo_paint(cr);
|
||
|
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||
|
|
||
|
image = cairo_image_surface_create_from_png(argv[1]);
|
||
|
image_width = cairo_image_surface_get_width(image);
|
||
|
image_height = cairo_image_surface_get_height(image);
|
||
|
|
||
|
/* Scale the image arbitrarily */
|
||
|
cairo_scale(cr, SCALE_WIDTH/image_width, SCALE_HEIGHT/image_height);
|
||
|
|
||
|
cairo_set_source_surface(cr, image, 350, 200);
|
||
|
cairo_paint(cr);
|
||
|
/* Wait for the result of the drawing operation to persist for the user to see */
|
||
|
sleep(DELAY_IN_SECS);
|
||
|
|
||
|
/* Destroy and release all cairo related contexts */
|
||
|
cairo_destroy(cr);
|
||
|
cairo_surface_destroy(surface);
|
||
|
|
||
|
return 0;
|
||
|
}
|