[cairo] cairo_paint crashing with 24 bit bpp

victorascroft at gmail.com victorascroft at gmail.com
Fri Feb 27 04:22:28 PST 2015


Hello,

I am using a Freescale Vybrid module and running the 3.18.5 kernel on 
it. Vybrid has a framebuffer driver and has the concept of multiple 
layers. The driver is not in the mainline due to it being a framebuffer 
driver and not DRM based.

http://thread.gmane.org/gmane.linux.ports.arm.kernel/260784/focus=269349

It allows for setting BPP to 16, 24 and 32 for each of the framebuffer 
layers. The alpha values of each of the five framebuffer layers can be 
set by using IOCTL call provided by the driver. The layers have priority 
with the 0 layer being highest priority and the highest layer having 
lower priority. Depending on the alpha value of each of the individual 
layers, these interact to render a blended image. For example, I can set 
the alpha value of fb0 to 150 and then set the fb1 to have an alpha 
value of 200. What I write to fb1 will appear blended with what is on 
fb0.

Now to write to the individual fbx I am using Cairo. The cairo version 
being used is 1.12.16 build using Openembedded. I do have LXDM and X on 
it but both are disabled and not required for the use of framebuffer 
which I want. X/LXDM is not much of use here as it does not seem to work 
with 32 bit bpp. Openembedded build is based on Yocto daisy.

On using 16 bit bpp and 32 bit bpp I can write successfully to any of 
the framebuffers and get the results I am expecting. The alpha value for 
each of these framebuffers is being controlled by hardware and the 
driver. At 24 bit bpp, calling cairo_paint crashes. I tried using 
cairo_paint_with_alpha which also crashes if an alpha value > 0.0 is 
specified. If I set alpha to 0.0 and then call it, I can see the images 
being rendered but severely distorted kind of.

I used gdb to track and make sure that it is cairo_paint where this 
crashes, using breakpoints at addresses inside cairo_paint to confirm.
I do not have the disassembled output of cairo library with source, but, 
this is probably at the cr->backend->paint call. I was only able to 
trace the address based on PLT entries in disassembled output of my 
cairo application.

Not being too well versed with cairo or graphics, can someone point me 
in the right direction here and give some inputs as to why I am 
observing this with 24 bit bpp.

My code piece is as below:
#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>

#define DELAY_IN_SECS    5
#define SCALE_WIDTH      256.0
#define SCALE_HEIGHT     256.0

typedef struct _cairo_linuxfb_device {
	int fb_fd;
	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)
{
	int color_input;
	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 = (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;
	}

	printf("1. CAIRO_FORMAT_RGB16_565\n");
	printf("2. CAIRO_FORMAT_RGB24\n");
	printf("3. CAIRO_FORMAT_ARGB32\n");
	printf("Enter the color input:\t");
	scanf("%d", &color_input);
	switch(color_input) {
	case 1:
		/* 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));
		break;
	case 2:
		/* Create the cairo surface which will be used to draw 
to */
	surface = cairo_image_surface_create_for_data(device->fb_data,
	              CAIRO_FORMAT_RGB24,
	              device->fb_vinfo.xres,
	              device->fb_vinfo.yres,
	              cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
												
device->fb_vinfo.xres));
		break;
	case 3:
		/* Create the cairo surface which will be used to draw 
to */
	surface = cairo_image_surface_create_for_data(device->fb_data,
	              CAIRO_FORMAT_ARGB32,
	              device->fb_vinfo.xres,
	              device->fb_vinfo.yres,
	              cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
												
device->fb_vinfo.xres));
		break;
	default:
		printf("Invalid input. Selecting 
CAIRO_FORMAT_RGB16_565\n");
		/* 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));
		break;
	}

	if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
		perror("Error in creating cairo surface\n");
		goto handle_ioctl_error;
	}

	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;
	float cairo_alpha_value;
	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);
	if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
		printf("Error in creating cairo surface\n");
		cairo_surface_destroy(surface);
		exit(1);
	}

	printf("Enter the cairo alpha value to paint with:\t");
	scanf("%f", &cairo_alpha_value);

    /* 
	 * 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_with_alpha(cr, cairo_alpha_value);
	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_with_alpha(cr, cairo_alpha_value);
	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;
}


Regards,
Victor.


More information about the cairo mailing list