OpenGL rendering Cairo graphics example

OpenGL has all the vector drawing capabilities that you could want, but offers very little support for text. There is of course openVG, but finding the required hardware is a bit like looking for rocking horse droppings. The Cairo graphics library looks quite encouraging, but all the examples write the generated graphics to a .png file.

This does give O/s independence but most developers want to see the graphics on the screen. To achieve this I have used OpenGL to renender a BMP as a texture.

This does not make any real use of the 3D, lighting or shading capabilities of the GL hardware. However hardware/software support for OpenGL is pretty ubiquitous now, so this example code is more dependent on the support for OpenGL than any particular operating system.

The examples are pretty much as you find them on the Cairo graphics tutorial page: http://cairographics.org/samples/

All but the draw_flag should work. As example code they are set up for a fixed canvas size of 256 * 256.

An exception is draw_clip () which I have modified to rescale with a different canvas size. (option e).

Option y is a code generated bitmap fill. This will also resize to fill different sizes. It does not use the Cairo graphics library, so may be useful as a diagnostic fall back.

The example project is for Eclipse running on Xubuntu, but it should not be difficult to build under windows. The Raspberry Pi supports OpenGL at the surprisingly high resolution of 2560 x 1600, so that could provide an interesting avenue for further experiments.

Download canvas.zip file.

Canvas.zip contents:

Screen Shot 2014-02-18 at 15.20.15

Code

Canvass.cpp

//============================================================================
// Name        : canvas.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

// requires include path to be:
// /usr/include/
// /usr/include/cairo

// lib path
// /usr/lib/x86_64-linux-gnu

// libraries
// glut;GL;GLU;GLEW;cairo

/*
 *
 *
 *
 */
#include <GL/glew.h>			// for glClear this needs to be first
#include <GL/gl.h>
#include <GL/glut.h>
#include <GL/freeglut_ext.h>
#include <GL/glext.h>

#include 
#include 

#include  // Allow us to print to the console
#include 

#include 

#include "draw.h"

/*
 *
 *
 *
 */
void selected_view (void);

/*
 *
 *
 *
 */
static GLuint texture;
static bool bail_out = false;
static unsigned char last_key = ' ';

/*
 *
 *
 *
 */
static int canvas_width = 256;
static int canvas_height = 256;

/*
 *
 *	Put a break mark here.
 *
 */
void trap (void)
{
	 int zz = 1;
	 zz++;
}

/*
 *
 *
 *
 */
GLuint rgba(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
{
	GLuint colour;

	// big endian
	//	colour = ((red << 24) | (green << 16) | (blue << 8) | (alpha << 0));
	// little endian
	colour = ((alpha << 24) | (blue << 16) | (green << 8) | (red << 0));

	return colour;
}

/*
 *
 *  Fill a pre allocated  GL_RGBA32UI bit map with a picture of a sunset.
 *
 */
void sunset(GLuint* data, GLuint width, GLuint height)
{
	GLuint y;
	GLuint x;
	GLfloat mixer;
	GLubyte shade;

	for (y = 0; y < height; y++)
	{
		// Vary the colour from 0-255 as we fill the bitmap.
		mixer = height-y;
		mixer *= 0xff;
		mixer /= height;
		shade = (GLubyte)mixer;

	    for (x = 0; x < width; x++) 	    { 	    	if (y > height)
	    	{
				data[width*y + x] =rgba(100, 255, 255, 0);;  // I'd expect this to be transparent and the above to be slightly transparent and green, but it's red somehow.
	    	}
	    	else
	    	{
	    		// fill in a sun
				if ((x - (width/3))*(x - (width/3)) + (y - (height/3))*(y - (height/3))   <   (width/3)*(height/3))
				{
					data[width*y + x] = rgba(160, 80, 0, 100);
				}
				else
				{

					data[width*y + x] = rgba(shade, 0, 0xFF-shade, 0xFF);
				}
	    	}
	    }
	}
}

/*
 *
 *  Fill a pre allocated array of GL_RGBA32UI bit map with white.
 *
 */
void fill_white(GLuint* data, GLuint width, GLuint height)
{
	GLuint y;
	GLuint x;

	for (y = 0; y < height; y++)
	{
	    for (x = 0; x < width; x++)
	    {
	    	data[width*y + x] = rgba(0xFF, 0xFF, 0xFF, 0xFF);
	    }
	}
}

/*
 *
 *
 *
 */
GLuint load_texture (int width, int height)
{
	GLuint* data;

	if (0 == pCairoBMP) trap();

	data = (GLuint*) pCairoBMP;

	fill_white((GLuint*) pCairoBMP, width, height);

	selected_view ();

	// allocate a texture name
	glGenTextures( 1, &texture );

	// select our current texture
	glBindTexture( GL_TEXTURE_2D, texture );

	// select modulate to mix texture with color for shading
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	// when texture area is small, bilinear filter the closest MIP map
//	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );

	// when texture area is large, bilinear filter the first MIP map
//	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

	// build our texture MIP maps
	gluBuild2DMipmaps(GL_TEXTURE_2D,    // texture to specify
					 GL_RGBA8,			// internal texture storage format
					 width,             // texture width
					 height,            // texture height
					 GL_RGBA,           // pixel format
					 GL_UNSIGNED_BYTE,	// colour component format
					 data);    			// pointer to texture image

	return texture;
}

/*
 *
 *
 *
 */
void free_texture( GLuint texture )
{
	glDeleteTextures( 1, &texture );
}

/*
 *
 *
 *
 */
void display (void)
{
	texture = load_texture(canvas_width, canvas_height);

	// OpenGL animation code goes here

	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
	glClear( GL_COLOR_BUFFER_BIT );

	// setup texture mapping
	glEnable( GL_TEXTURE_2D );
	glBindTexture( GL_TEXTURE_2D, texture );

	// GL draw textures upside down, so these co-ordinates have the y axis inverted.
	glPushMatrix();
	glBegin( GL_QUADS );
	glTexCoord2d(0.0,0.0); glVertex2d(-1.0,+1.0);
	glTexCoord2d(1.0,0.0); glVertex2d(+1.0,+1.0);
	glTexCoord2d(1.0,1.0); glVertex2d(+1.0,-1.0);
	glTexCoord2d(0.0,1.0); glVertex2d(-1.0,-1.0);

	glEnd();
	glPopMatrix();

	glutSwapBuffers();
}

/*
 *
 *
 *
 */
void keyPressed (unsigned char key, int x, int y)
{
	last_key = key;

	if ('q' == key)
	{
		bail_out = true;
	}

	draw_release();							// Discard old Cairo surface, and
	draw_init(canvas_width, canvas_height); // start a new one.

	glutPostRedisplay();
}

/*
 *
 *
 *
 */
void keyUp (unsigned char key, int x, int y)
{
}

/*
 *
 *
 *
 */
void selected_view (void)
{
	switch(last_key)
	{
		case 'a':	draw_text ();				break;
		case 'b':	draw_round_rect();			break;
		case 'c':	draw_arc ();				break;
		case 'd':	draw_arc_negative ();		break;
		case 'e':	draw_clip ();				break;
		case 'f':	draw_clip_image ();			break;
		case 'g':	draw_curve_rect ();			break;
		case 'h':	draw_curve_to ();			break;
		case 'i':	draw_dash ();				break;
		case 'j':	draw_fill_and_stroke2 ();	break;
		case 'k':	draw_fill_style ();			break;
		case 'l':	draw_gradient ();			break;
		case 'm':	draw_help ();				break;
		case 'n':	draw_image (); 				break;
		case 'o':	draw_image_pattern ();		break;
		case 'p':	draw_multi_segment_caps ();	break;
		case 'q':								break;  // bail out.
		case 'r':	draw_setline_cap ();		break;
		case 's':	draw_set_line_join ();		break;
		case 't':	draw_flag();				break;
		case 'u':	draw_text_align_center ();	break;
		case 'v':	draw_text_extents ();		break;
		case 'w':	draw_frame_no();			break;
		case 'x':	fill_white((GLuint*) pCairoBMP, canvas_width, canvas_height);			break;
		case 'y':	sunset((GLuint*) pCairoBMP, canvas_width, canvas_height);				break;
		default:	draw_help();				break;
	}
}

/*
 *
 *
 *
 */
int main (int argc, char **argv)
{
	bail_out = false;

	glutInit(&argc, argv); 										// Initialize GLUT
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); // Set up a basic display buffer (only single buffered for now)
	glutInitWindowSize (canvas_width, canvas_height); 			// Set the width and height of the window
	glutInitWindowPosition (100, 100); 							// Set the position of the window
	glutCreateWindow ("OpenGL with Cairo"); 					// Set the title for the window

	glutKeyboardFunc(keyPressed); 								// Tell GLUT to use the method "keyPressed" for key presses
	glutKeyboardUpFunc(keyUp); 									// Tell GLUT to use the method "keyUp" for key up events
	glutDisplayFunc(display); 									// Tell GLUT to use the method "display" for rendering

	if (GLEW_OK != glewInit())
	{
		// std::cout << "Couldn't initialize GLEW" << std::endl;
		trap();
	}

	// allocate bmp space;
	draw_init(canvas_width, canvas_height);

	// load our texture
	texture = load_texture(canvas_width, canvas_height);

	// Main loop.
	while (true)
	{
		glutMainLoopEvent();

		if (bail_out)
		{
			break;
		}
	}

	// free the texture
	free_texture( texture );

    draw_release();
}

/*
 *
 *
 *
 */
Screen Shot 2014-02-18 at 15.20.15

draw.cpp

//============================================================================
// Name        : draw.cpp
// Author      : 
// Version     :
// Copyright   : public domain
// Description : Cairo samples
//
//  see --  http://cairographics.org/samples/
//
// Cairo samples
//
// This file contains samples of cairo's rendered output and the code snippets used to create them.
// The snippets are meant to be short, and easy to understand.
//
// The original snippets were created by Øyvind Kolås for a paper submitted to GUADEC 2004.
// All of his original snippet code is considered to be part of the public domain.
//
//============================================================================

// http://cairographics.org/samples/

#include  // Allow us to print to the console
#include 
#include 
#include 

#include "draw.h"

/*
 *
 *
 *
 */
extern void trap (void);

/*
 *
 *
 *
 */
static int frame_no = 0;
// float M_PI = 3.14159;
static int width = 0;
static int height = 0;

/*
 *
 *
 *
 */
static cairo_surface_t *surface;
static cairo_t *cr;
void* pCairoBMP;

/*
 *
 *
 *
 */
void draw_init(int w, int h)
{
    int stride;

    frame_no = 0;
	width = w;
	height = h;

    stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
    pCairoBMP = malloc (stride * height);
    if (!pCairoBMP) trap();

    surface = cairo_image_surface_create_for_data ((unsigned char *)pCairoBMP, CAIRO_FORMAT_ARGB32, width, height, stride);
    cr =  cairo_create (surface);
}

/*
 *
 *
 *
 */
void draw_release(void)
{
    cairo_destroy (cr);
    cairo_surface_destroy (surface);
    if (pCairoBMP)
	{
		free(pCairoBMP);
	}
}

/*
 *
 *
 *
 */
void draw_get_width_height(int * width, int * height)
{
	if (!surface) trap();
	*width = cairo_image_surface_get_width (surface);
	*height = cairo_image_surface_get_height (surface);
}

/*
 *
 *
 *
 */
void draw_frame_no (void)
{
    cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size (cr, 64.0);

    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);

    cairo_move_to (cr, 10.0, 100.0);
    cairo_show_text (cr, "Hello, cairo");

	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);
	cairo_fill (cr);

    cairo_move_to (cr, 10.0, 200.0);
	char text_buf[60];
	sprintf (text_buf, "frame - %d", frame_no++);
	cairo_show_text (cr, (const char *)&text_buf);
}

/*
 *
 *
 *
 */
void draw_help (void)
{
    cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size (cr, 24.0);

    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);

    cairo_move_to (cr, 10.0, 50.0);
    cairo_show_text (cr, "Help - q to exit");

	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);
	cairo_fill (cr);

    cairo_move_to (cr, 10.0, 80.0);
	cairo_show_text (cr, "other letters to show examples ");
}

/*
 *
 *
 *
 */
void draw_round_rect (void)
{
	/* a custom shape that could be wrapped in a function */
	double x         = 25.6,        /* parameters like cairo_rectangle */
	       y         = 25.6,
	       width         = 204.8,
	       height        = 204.8,
	       aspect        = 1.0,     /* aspect ratio */
	       corner_radius = height / 10.0;   /* and corner curvature radius */

	double radius = corner_radius / aspect;
	double degrees = M_PI / 180.0;

	cairo_new_sub_path (cr);
	cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
	cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
	cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
	cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
	cairo_close_path (cr);

	cairo_set_source_rgb (cr, 0.5, 0.5, 1);
	cairo_fill_preserve (cr);
	cairo_set_source_rgba (cr, 0.5, 0, 0, 0.5);
	cairo_set_line_width (cr, 10.0);
	cairo_stroke (cr);

//	cairo_surface_write_to_png (surface, "round_rect.png");
}

/*
 *
 *	This does not work, on the .png or render surface.
 *
 */
void draw_flag (void)
{
	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_move_to (cr, 0, 0);
	cairo_line_to (cr, 1, 1);
	cairo_move_to (cr, 1, 0);
	cairo_line_to (cr, 0, 1);
	cairo_set_line_width (cr, 0.2);
	cairo_stroke (cr);

	cairo_rectangle (cr, 0, 0, 0.5, 0.5);
	cairo_set_source_rgba (cr, 1, 0, 0, 0.80);
	cairo_fill (cr);

	cairo_rectangle (cr, 0, 0.5, 0.5, 0.5);
	cairo_set_source_rgba (cr, 0, 1, 0, 0.60);
	cairo_fill (cr);

	cairo_rectangle (cr, 0.5, 0, 0.5, 0.5);
	cairo_set_source_rgba (cr, 0, 0, 1, 0.40);
	cairo_fill (cr);

//	cairo_surface_write_to_png (surface, "flag.png");
}

/*
 *
 *
 *
 */
void draw_arc (void)
{
	double xc = 128.0;
	double yc = 128.0;
	double radius = 100.0;
	double angle1 = 45.0  * (M_PI/180.0);  /* angles are specified */
	double angle2 = 180.0 * (M_PI/180.0);  /* in radians           */

	cairo_set_line_width (cr, 10.0);
	cairo_arc (cr, xc, yc, radius, angle1, angle2);
	cairo_stroke (cr);

	/* draw helping lines */
	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_set_line_width (cr, 6.0);

	cairo_arc (cr, xc, yc, 10.0, 0, 2*M_PI);
	cairo_fill (cr);

	cairo_arc (cr, xc, yc, radius, angle1, angle1);
	cairo_line_to (cr, xc, yc);
	cairo_arc (cr, xc, yc, radius, angle2, angle2);
	cairo_line_to (cr, xc, yc);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_arc_negative (void)
{
	double xc = 128.0;
	double yc = 128.0;
	double radius = 100.0;
	double angle1 = 45.0  * (M_PI/180.0);  /* angles are specified */
	double angle2 = 180.0 * (M_PI/180.0);  /* in radians           */

	cairo_set_line_width (cr, 10.0);
	cairo_arc_negative (cr, xc, yc, radius, angle1, angle2);
	cairo_stroke (cr);

	/* draw helping lines */
	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_set_line_width (cr, 6.0);

	cairo_arc (cr, xc, yc, 10.0, 0, 2*M_PI);
	cairo_fill (cr);

	cairo_arc (cr, xc, yc, radius, angle1, angle1);
	cairo_line_to (cr, xc, yc);
	cairo_arc (cr, xc, yc, radius, angle2, angle2);
	cairo_line_to (cr, xc, yc);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_clip (void)
{
	cairo_arc (cr, width/2, height/2, (width + height)/8, 0, 2 * M_PI);
	cairo_clip (cr);

	cairo_new_path (cr);  /* current path is not
	                         consumed by cairo_clip() */
	cairo_rectangle (cr, 0, 0, width, height);
	cairo_fill (cr);
	cairo_set_source_rgb (cr, 0, 1, 0);
	cairo_move_to (cr, 0, 0);
	cairo_line_to (cr, width, height);
	cairo_move_to (cr, width, 0);
	cairo_line_to (cr, 0, height);
	cairo_set_line_width (cr, 10.0);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_clip_image (void)
{
	int              w, h;
	cairo_surface_t *image;

	cairo_arc (cr, 128.0, 128.0, 76.8, 0, 2*M_PI);
	cairo_clip (cr);
	cairo_new_path (cr); /* path not consumed by clip()*/

	image = cairo_image_surface_create_from_png ("yolanda.png");
	w = cairo_image_surface_get_width (image);
	h = cairo_image_surface_get_height (image);

	cairo_scale (cr, 177.0/w, 256.0/h);

	cairo_set_source_surface (cr, image, 0, 0);
	cairo_paint (cr);

//	cairo_surface_write_to_png (surface, "clip_image.png");

	cairo_surface_destroy (image);
}

/*
 *
 *
 *
 */
void draw_curve_rect (void)
{
	/* a custom shape that could be wrapped in a function */
	double x0      = 25.6,   /* parameters like cairo_rectangle */
	       y0      = 25.6,
	       rect_width  = 204.8,
	       rect_height = 204.8,
	       radius = 102.4;   /* and an approximate curvature radius */

	double x1,y1;

	x1=x0+rect_width;
	y1=y0+rect_height;
	if (!rect_width || !rect_height)
	    return;
	if (rect_width/2<radius) {
	    if (rect_height/2<radius) {
	        cairo_move_to  (cr, x0, (y0 + y1)/2);
	        cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1)/2, y0);
	        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1)/2);
	        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0)/2, y1);
	        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1)/2);
	    } else {
	        cairo_move_to  (cr, x0, y0 + radius);
	        cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1)/2, y0);
	        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
	        cairo_line_to (cr, x1 , y1 - radius);
	        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0)/2, y1);
	        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius);
	    }
	} else {
	    if (rect_height/2<radius) {
	        cairo_move_to  (cr, x0, (y0 + y1)/2);
	        cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
	        cairo_line_to (cr, x1 - radius, y0);
	        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1)/2);
	        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
	        cairo_line_to (cr, x0 + radius, y1);
	        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1)/2);
	    } else {
	        cairo_move_to  (cr, x0, y0 + radius);
	        cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
	        cairo_line_to (cr, x1 - radius, y0);
	        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
	        cairo_line_to (cr, x1 , y1 - radius);
	        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
	        cairo_line_to (cr, x0 + radius, y1);
	        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius);
	    }
	}
	cairo_close_path (cr);

	cairo_set_source_rgb (cr, 0.5, 0.5, 1);
	cairo_fill_preserve (cr);
	cairo_set_source_rgba (cr, 0.5, 0, 0, 0.5);
	cairo_set_line_width (cr, 10.0);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_curve_to (void)
{
	double x=25.6,  y=128.0;
	double x1=102.4, y1=230.4,
	       x2=153.6, y2=25.6,
	       x3=230.4, y3=128.0;

	cairo_move_to (cr, x, y);
	cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);

	cairo_set_line_width (cr, 10.0);
	cairo_stroke (cr);

	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_set_line_width (cr, 6.0);
	cairo_move_to (cr,x,y);   cairo_line_to (cr,x1,y1);
	cairo_move_to (cr,x2,y2); cairo_line_to (cr,x3,y3);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_dash (void)
{
	double dashes[] = {50.0,  /* ink */
	                   10.0,  /* skip */
	                   10.0,  /* ink */
	                   10.0   /* skip*/
	                  };
	int    ndash  = sizeof (dashes)/sizeof(dashes[0]);
	double offset = -50.0;

	cairo_set_dash (cr, dashes, ndash, offset);
	cairo_set_line_width (cr, 10.0);

	cairo_move_to (cr, 128.0, 25.6);
	cairo_line_to (cr, 230.4, 230.4);
	cairo_rel_line_to (cr, -102.4, 0.0);
	cairo_curve_to (cr, 51.2, 230.4, 51.2, 128.0, 128.0, 128.0);

	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_fill_and_stroke2 (void)
{
	cairo_move_to (cr, 128.0, 25.6);
	cairo_line_to (cr, 230.4, 230.4);
	cairo_rel_line_to (cr, -102.4, 0.0);
	cairo_curve_to (cr, 51.2, 230.4, 51.2, 128.0, 128.0, 128.0);
	cairo_close_path (cr);

	cairo_move_to (cr, 64.0, 25.6);
	cairo_rel_line_to (cr, 51.2, 51.2);
	cairo_rel_line_to (cr, -51.2, 51.2);
	cairo_rel_line_to (cr, -51.2, -51.2);
	cairo_close_path (cr);

	cairo_set_line_width (cr, 10.0);
	cairo_set_source_rgb (cr, 0, 0, 1);
	cairo_fill_preserve (cr);
	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_fill_style (void)
{
	cairo_set_line_width (cr, 6);

	cairo_rectangle (cr, 12, 12, 232, 70);
	cairo_new_sub_path (cr); cairo_arc (cr, 64, 64, 40, 0, 2*M_PI);
	cairo_new_sub_path (cr); cairo_arc_negative (cr, 192, 64, 40, 0, -2*M_PI);

	cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
	cairo_set_source_rgb (cr, 0, 0.7, 0); cairo_fill_preserve (cr);
	cairo_set_source_rgb (cr, 0, 0, 0); cairo_stroke (cr);

	cairo_translate (cr, 0, 128);
	cairo_rectangle (cr, 12, 12, 232, 70);
	cairo_new_sub_path (cr); cairo_arc (cr, 64, 64, 40, 0, 2*M_PI);
	cairo_new_sub_path (cr); cairo_arc_negative (cr, 192, 64, 40, 0, -2*M_PI);

	cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
	cairo_set_source_rgb (cr, 0, 0, 0.9); cairo_fill_preserve (cr);
	cairo_set_source_rgb (cr, 0, 0, 0); cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_gradient (void)
{
	cairo_pattern_t *pat;

	pat = cairo_pattern_create_linear (0.0, 0.0,  0.0, 256.0);
	cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
	cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
	cairo_rectangle (cr, 0, 0, 256, 256);
	cairo_set_source (cr, pat);
	cairo_fill (cr);
	cairo_pattern_destroy (pat);

	pat = cairo_pattern_create_radial (115.2, 102.4, 25.6,
	                                   102.4,  102.4, 128.0);
	cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
	cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
	cairo_set_source (cr, pat);
	cairo_arc (cr, 128.0, 128.0, 76.8, 0, 2 * M_PI);
	cairo_fill (cr);
	cairo_pattern_destroy (pat);
}

/*
 *
 *
 *
 */
void draw_image (void)
{
	int              w, h;
	cairo_surface_t *image;

	image = cairo_image_surface_create_from_png ("yolanda.png");
	w = cairo_image_surface_get_width (image);
	h = cairo_image_surface_get_height (image);

	cairo_translate (cr, 128.0, 128.0);
//	cairo_rotate (cr, 45* M_PI/180);
	cairo_scale  (cr, 177.0/w, 256.0/h);
	cairo_translate (cr, -0.5*w, -0.5*h);

	cairo_set_source_surface (cr, image, 0, 0);
	cairo_paint (cr);
	cairo_surface_destroy (image);
}

/*
 *
 *
 *
 */
void draw_image_pattern (void)
{
	int              w, h;
	cairo_surface_t *image;
	cairo_pattern_t *pattern;
	cairo_matrix_t   matrix;

	image = cairo_image_surface_create_from_png ("sunset6.png");
	w = cairo_image_surface_get_width (image);
	h = cairo_image_surface_get_height (image);

	pattern = cairo_pattern_create_for_surface (image);
	cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);

	cairo_translate (cr, 128.0, 128.0);
	cairo_rotate (cr, M_PI / 4);
	cairo_scale (cr, 1 / sqrt (2), 1 / sqrt (2));
	cairo_translate (cr, -128.0, -128.0);

	cairo_matrix_init_scale (&matrix, w/256.0 * 5.0, h/256.0 * 5.0);
	cairo_pattern_set_matrix (pattern, &matrix);

	cairo_set_source (cr, pattern);

	cairo_rectangle (cr, 0, 0, 256.0, 256.0);
	cairo_fill (cr);

	cairo_pattern_destroy (pattern);
	cairo_surface_destroy (image);
}

/*
 *
 *
 *
 */
void draw_multi_segment_caps (void)
{
	cairo_move_to (cr, 50.0, 75.0);
	cairo_line_to (cr, 200.0, 75.0);

	cairo_move_to (cr, 50.0, 125.0);
	cairo_line_to (cr, 200.0, 125.0);

	cairo_move_to (cr, 50.0, 175.0);
	cairo_line_to (cr, 200.0, 175.0);

	cairo_set_line_width (cr, 30.0);
	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_setline_cap (void)
{
	cairo_set_line_width (cr, 30.0);
	cairo_set_line_cap  (cr, CAIRO_LINE_CAP_BUTT); /* default */
	cairo_move_to (cr, 64.0, 50.0); cairo_line_to (cr, 64.0, 200.0);
	cairo_stroke (cr);
	cairo_set_line_cap  (cr, CAIRO_LINE_CAP_ROUND);
	cairo_move_to (cr, 128.0, 50.0); cairo_line_to (cr, 128.0, 200.0);
	cairo_stroke (cr);
	cairo_set_line_cap  (cr, CAIRO_LINE_CAP_SQUARE);
	cairo_move_to (cr, 192.0, 50.0); cairo_line_to (cr, 192.0, 200.0);
	cairo_stroke (cr);

	/* draw helping lines */
	cairo_set_source_rgb (cr, 1, 0.2, 0.2);
	cairo_set_line_width (cr, 2.56);
	cairo_move_to (cr, 64.0, 50.0); cairo_line_to (cr, 64.0, 200.0);
	cairo_move_to (cr, 128.0, 50.0);  cairo_line_to (cr, 128.0, 200.0);
	cairo_move_to (cr, 192.0, 50.0); cairo_line_to (cr, 192.0, 200.0);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_set_line_join (void)
{
	cairo_set_line_width (cr, 40.96);
	cairo_move_to (cr, 76.8, 84.48);
	cairo_rel_line_to (cr, 51.2, -51.2);
	cairo_rel_line_to (cr, 51.2, 51.2);
	cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER); /* default */
	cairo_stroke (cr);

	cairo_move_to (cr, 76.8, 161.28);
	cairo_rel_line_to (cr, 51.2, -51.2);
	cairo_rel_line_to (cr, 51.2, 51.2);
	cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
	cairo_stroke (cr);

	cairo_move_to (cr, 76.8, 238.08);
	cairo_rel_line_to (cr, 51.2, -51.2);
	cairo_rel_line_to (cr, 51.2, 51.2);
	cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
	cairo_stroke (cr);
}

/*
 *
 *
 *
 */
void draw_text (void)
{
	cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
	                               CAIRO_FONT_WEIGHT_BOLD);
	cairo_set_font_size (cr, 90.0);

	cairo_move_to (cr, 10.0, 135.0);
	cairo_show_text (cr, "Hello");

	cairo_move_to (cr, 70.0, 165.0);
	cairo_text_path (cr, "void");
	cairo_set_source_rgb (cr, 0.5, 0.5, 1);
	cairo_fill_preserve (cr);
	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_set_line_width (cr, 2.56);
	cairo_stroke (cr);

	/* draw helping lines */
	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_arc (cr, 10.0, 135.0, 5.12, 0, 2*M_PI);
	cairo_close_path (cr);
	cairo_arc (cr, 70.0, 165.0, 5.12, 0, 2*M_PI);
	cairo_fill (cr);

}

/*
 *
 *
 *
 */
void draw_text_align_center (void)
{
	cairo_text_extents_t extents;

	const char *utf8 = "cairo";
	double x,y;

	cairo_select_font_face (cr, "Sans",
	    CAIRO_FONT_SLANT_NORMAL,
	    CAIRO_FONT_WEIGHT_NORMAL);

	cairo_set_font_size (cr, 52.0);
	cairo_text_extents (cr, utf8, &extents);
	x = 128.0-(extents.width/2 + extents.x_bearing);
	y = 128.0-(extents.height/2 + extents.y_bearing);

	cairo_move_to (cr, x, y);
	cairo_show_text (cr, utf8);

	/* draw helping lines */
	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_set_line_width (cr, 6.0);
	cairo_arc (cr, x, y, 10.0, 0, 2*M_PI);
	cairo_fill (cr);
	cairo_move_to (cr, 128.0, 0);
	cairo_rel_line_to (cr, 0, 256);
	cairo_move_to (cr, 0, 128.0);
	cairo_rel_line_to (cr, 256, 0);
	cairo_stroke (cr);

}

/*
 *
 *
 *
 */
void draw_text_extents (void)
{
	cairo_text_extents_t extents;

	const char *utf8 = "cairo";
	double x,y;

	cairo_select_font_face (cr, "Sans",
	    CAIRO_FONT_SLANT_NORMAL,
	    CAIRO_FONT_WEIGHT_NORMAL);

	cairo_set_font_size (cr, 100.0);
	cairo_text_extents (cr, utf8, &extents);

	x=25.0;
	y=150.0;

	cairo_move_to (cr, x,y);
	cairo_show_text (cr, utf8);

	/* draw helping lines */
	cairo_set_source_rgba (cr, 1, 0.2, 0.2, 0.6);
	cairo_set_line_width (cr, 6.0);
	cairo_arc (cr, x, y, 10.0, 0, 2*M_PI);
	cairo_fill (cr);
	cairo_move_to (cr, x,y);
	cairo_rel_line_to (cr, 0, -extents.height);
	cairo_rel_line_to (cr, extents.width, 0);
	cairo_rel_line_to (cr, extents.x_bearing, -extents.y_bearing);
	cairo_stroke (cr);
}

/*
 *
 * EOF
 *
 */

draw.h

//============================================================================
// Name        : draw.h
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#ifndef _DRAW_H_
#define _DRAW_H_

/*
 *
 *
 *
 */
#include 

/*
 *
 *
 *
 */
extern void* pCairoBMP;

/*
 *
 *
 *
 */

extern void draw_init(int w, int h);
extern void draw_release(void);
extern void draw_get_width_height(int * width, int * height);

extern void draw_frame_no (void);
extern void draw_round_rect (void);
extern void draw_flag (void);
extern void draw_help (void);

extern void draw_arc (void);
extern void draw_arc_negative (void);
extern void draw_clip (void);
extern void draw_clip_image (void);
extern void draw_curve_rect (void);
extern void draw_curve_to (void);
extern void draw_dash (void);
extern void draw_fill_and_stroke2 (void);
extern void draw_fill_style (void);
extern void draw_gradient (void);
extern void draw_image (void);
extern void draw_image_pattern (void);
extern void draw_multi_segment_caps (void);
extern void draw_setline_cap (void);
extern void draw_set_line_join (void);
extern void draw_text (void);
extern void draw_text_align_center (void);
extern void draw_text_extents (void);

/*
 *
 *
 *
 */
#endif //  _DRAW_H_

/*
 *
 *
 *
 */