Cairo
Cairo is an open source library for 2D vector graphics with support for multiple output devices. In Tizen, Cairo can support output to 2 different backends, such as the image and evas-gl (similar to gl) backend. This guide explains how you can link the Cairo image backend with Evas (in mobile and wearable applications), so that Cairo can draw on the image surface and an Evas object can get the image data from the Cairo image buffer.
Creating an Evas_Object Image
Cairo and Evas have completely different concepts:
- Evas knows the state of each object on the screen and manipulates the state. So when you create, for example, a rectangle with the evas_object_rectangle_add() function, it is not rendered on the screen when the function is called. In the rendering stage, the rectangle can be overlaid by an opaque image and never be rendered on the canvas.
- Cairo draws as a person on a paper sheet. Once something is drawn on the Cairo surface, it is rendered on the screen.
First, as shown in the following code snippet, you can define the appdata structure, which contains all the pointers to the objects to be manipulated:
typedef struct appdata { Evas_Object *win; Evas_Object *img; cairo_surface_t *surface; cairo_t *cairo; unsigned char *pixels; } appdata_s;
To create a new Evas_Object image, use the evas_object_image_add() function. The image object can be used for displaying as pixels on the screen:
appdata_s * ad; ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE); evas_object_show(ad->win); ad->img = evas_object_image_add(evas_object_evas_get(ad->win)); evas_object_show(ad->img);
Linking Evas_Object Images to Cairo
You can now create a Cairo image surface for the provided pixel and other data by using the cairo_image_surface_create_for_data() function. The pixel data is a pointer to a buffer supplied by the application in which you want to write content.
The size of the row stride is called by the cairo_format_stride_for_width() function. The function provides a stride value that respects all alignment requirements of the accelerated image-rendering code within Cairo.
int row_stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, WIDTH); ad->pixels = (unsigned char *) calloc(sizeof(unsigned char) * row_stride * HEIGHT, 1); ad->surface = cairo_image_surface_create_for_data(ad->pixels,CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT, row_stride);
To create the Cairo context used for all operations, use the following code.
cairo = cairo_create(ad->surface);
Associate the pixels (as raw data) to given image object. The pixels must be of the same size and colorspace as the image object.
evas_object_image_data_set(ad->img, ad->pixels);
Once you finish painting by using Cairo, Evas renders a particular rectangular region to be redrawn on the screen:
evas_object_image_data_update_add(ad->img, 0, 0, WIDTH, HEIGHT);
Drawing with Cairo
Before drawing a line, set the current line width or color as a style. For example, you can set the line width as 2 and the line color as opaque red:
cairo_set_line_width(ad->cairo, 2); cairo_set_source_rgba(ad->cairo, 1.0, 0.0, 0.0, 1.0);
You can draw various lines:
-
To set the start position with a user-specific offset, use the cairo_translate() function. It modifies the current transformation matrix (CTM) by translating the user-space origin by (x, y).
cairo_translate(ad->cairo, 40, 40);
-
Cairo uses a connect-the-dots style system when creating a path. To draw a line between 2 points (100,100 and 200,150) on the surface, use the cairo_move_to() and cairo_line_to() functions:
cairo_move_to(ad->cairo, 100, 100); cairo_line_to(ad->cairo, 200, 150);
-
To draw a line from the endpoint of the current path, use the cairo_rel_line_to() function. The offset by (dx, dy) must be specified as (100, -50).
cairo_rel_line_to(ad->cairo, 100, -50);
-
To draw a circular arc of the given radius (100 * sqrt(2)) to the current path, use the cairo_arc() function.
The arc is centered at (200, 200), begins at angle1 (-0.25 * M_PI) and proceeds in the direction of increasing angles to end at angle2 (0.25 * M_PI). If angle2 is less than angle1, it is progressively increased by 2*M_PI until it is greater than angle1.
cairo_arc(ad->cairo, 200, 200, 100 * sqrt(2), -0.25 * M_PI, 0.25 * M_PI);
-
To draw a cubic Bézier spline to the path from the end position of the previous path, use the cairo_rel_curve_to() function. You can use the points offset by (-100, -50) and (-100, 50) as the control points. After the call, the current point is offset by (-200, 0).
cairo_rel_curve_to(ad->cairo, -100, -50, -100, 50, -200, 0);
-
You can add a line segment to the path from the current point to the beginning of the current sub-path. After this call, the current point is at the joined endpoint of the sub-path. The cairo_close_path() function differs from simply calling the cairo_line_to() function with the equivalent coordinate in the case of stroking: there is a line join connecting the final and initial segments of the sub-path.
cairo_close_path(ad->cairo);
-
To create a rectangle, use the cairo_rectangle() function. This call draws a rectangle with 400 px in width and height from point (0, 0).
cairo_rectangle(ad->cairo, 0, 0, 400, 400);
-
To stroke the paths, use the cairo_stroke() function. It is a drawing operator that strokes the current path according to the current line width, line join, line cap, and dash settings. After the function call, the current path is cleared from the cairo context.
cairo_stroke(ad->cairo);
-
To ensure that any pending Cairo operation are drawn, use the cairo_surface_flush() function after finishing the Cairo drawing:
cairo_surface_flush(ad->surface);
You need to destroy Cairo objects before terminating your application:
cairo_destroy(ad->cairo); cairo_surface_destroy(ad->surface);
Figure: Drawing paths and a rectangle with Cairo