Page Example

Cairo EvasGL Sample Overview

Mobile native

The Cairo EvasGL sample application demonstrates how to draw vector graphics using the Cairo GL backend with the Evas_GL surface using the Tizen SDK.

The application presented in this overview is not a full Tizen application, since it does not use the Application Framework. The application simply starts and runs.

The following figure illustrates the main screen of the Cairo EvasGL.

Figure: Cairo EvasGL screen

Cairo EvasGL screen


To create a basic application that provides a UI application skeleton which already makes available the window object that can contain a Cairo drawing:

  1. Build the environment.

    For using the Cairo GL backend on the Evas_GL surface, include the following header files.

    #include <cairo.h>
    #include <Evas_GL.h>
    #include <cairo-evas-gl.h>
    #include <math.h>

    Define the appdata structure that contains all the pointers to objects to be manipulated:

    typedef struct appdata 
       Evas_Object *win;	
       Evas_Object *img;
       cairo_surface_t *surface;
       cairo_t *cairo;
       cairo_device_t *cairo_device;
       Evas_GL *evas_gl;
       Evas_GL_Config *evas_gl_config;
       Evas_GL_Surface *evas_gl_surface;
       Evas_GL_Context *evas_gl_context;
    } appdata_s;
  2. Create the Elm_window for EvasGL.

    For using the EvasGL backend, you have to add this before the window creation. This makes it possible for the Cairo drawings on EvasGL to be displayed on the screen.

    ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
    elm_win_autodel_set(ad->win, EINA_TRUE);
    if (elm_win_wm_rotation_supported_get(ad->win))
       int rots[4] = {0, 90, 180, 270};
       elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
    evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);
    eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
  3. Create the Evas_Object image:
    ad->img = evas_object_image_filled_add(evas_object_evas_get(ad->win));
  4. Initialize the variables for EvasGL.

    Before initializing EvasGL and creating the Evas_GL surface, get the window size.

    evas_object_geometry_get(ad->win, NULL, NULL, &ad->width, &ad->height);

    Initialize the variables related to the Evas_GL within the appdata structure.

    For creating the EvasGL object, you can use the Evas_Object image created before.

    Evas_Native_Surface ns;
    ad->evas_gl = evas_gl_new(evas_object_evas_get(ad->img));
    ad->evas_gl_config = evas_gl_config_new();
    ad->evas_gl_config->color_format = EVAS_GL_RGBA_8888;
    ad->evas_gl_config->stencil_bits = EVAS_GL_STANCIL_BIT_8;
    ad->evas_gl_config->multisample_bits = EVAS_GL_MULTISAMPLE_MED;
    ad->evas_gl_surface = evas_gl_surface_create(ad->evas_gl, ad->evas_gl_config, ad->width, ad->height);
    ad->evas_gl_context = evas_gl_context_create(ad->evas_gl, NULL);
    evas_gl_native_surface_get(ad->evas_gl, ad->evas_gl_surface, &ns);
    evas_object_image_native_surface_set(ad->img, &ns);
    evas_object_image_pixels_get_callback_set(ad->img, cairo_drawing, ad);
  5. Link the Evas callback function to draw the Cairo image.
    evas_object_image_pixels_get_callback_set(ad->img, cairo_drawing, NULL);
  6. Create Cairo with EvasGL.

    To create the Cairo surface by using Evas_GL, the Cairo device must be provided. Set the CAIRO_GL_COMPOSITOR as msaa for using full GPU acceleration. In addition, to prevent unnecessary context switches in the Cairo GL backend, use the cairo_gl_device_set_thread_aware() function.

    setenv("CAIRO_GL_COMPOSITOR", "msaa", 1);
    ad->cairo_device = (cairo_device_t *)cairo_evas_gl_device_create(ad->evas_gl, ad->evas_gl_context);
    cairo_gl_device_set_thread_aware(ad->cairo_device, 0);
    ad->surface = (cairo_surface_t *)cairo_gl_surface_create_for_evas_gl(ad->cairo_device, ad->evas_gl_surface, ad->evas_gl_config, ad->width, ad->height);
    ad->cairo = cairo_create(ad->surface);
  7. Draw random items (rectangles, circles, and triangles) with the cairo_drawing() function:
    void cairo_drawing(void *data)
       appdata_s *ad = data;
       int i;
       double r, g, b, a;
       // Clear background as white
       cairo_set_source_rgba(ad->cairo, 1, 1, 1, 1);
       cairo_set_operator(ad->cairo, CAIRO_OPERATOR_OVER);
       for (i = 0; i < 100; i++)
          int shape = drand48() *3;
          float width = drand48() * 50 + 1;
          int line_cap = drand48() * 3;
          cairo_line_cap_t line_cap_style = CAIRO_LINE_CAP_BUTT;
          if (line_cap == 1)
             line_cap_style = CAIRO_LINE_CAP_ROUND;
          else if (line_cap == 2)
             line_cap_style = CAIRO_LINE_CAP_SQUARE;
          int line_join = drand48() * 3;
          cairo_line_join_t line_join_style = CAIRO_LINE_JOIN_MITER;
          if (line_join == 1)
             line_join_style = CAIRO_LINE_JOIN_ROUND;
          else if (line_join == 2)
             line_join_style = CAIRO_LINE_JOIN_BEVEL;
          double dash[] = {0.0, 0.0};
          dash[0] = drand48() * 50;
          dash[1] = drand48() * 50;
          cairo_set_dash(ad->cairo, dash, 2, 0);
          cairo_set_line_width(ad->cairo, width);
          cairo_set_line_join(ad->cairo, line_join_style);
          cairo_set_line_cap(ad->cairo, line_cap_style);
          // Random color
          r = drand48();
          g = drand48();
          b = drand48();
          a = drand48();
          cairo_set_source_rgba(ad->cairo, r, g, b, a);
          // Random position
          float x = drand48() * ad->width;
          float y = drand48() * ad->height;
          float side = drand48() * 300;
          if (shape == 0)
             // Draw a square
             cairo_rectangle(ad->cairo, x, y, side, side);
          else if (shape == 1)
             // Draw a circle
             cairo_arc(ad->cairo, x, y, side/2, 0.0, 2.0 * M_PI);
             // Draw a triangle
             cairo_move_to(ad->cairo, x, y);
             cairo_line_to(ad->cairo, x + side, y);
             cairo_line_to(ad->cairo, x, y + side);
  8. Add the animator function callback.

    The following is an example of a default update refresh rate. You can add the animator after creating Cairo and EvasGL.

    Add the animator function callback with the ecore_animator_add() function. The evas_object_image_pixels_dirty_set() function updates the Evas_Object image which is connected to EvasGL. After this, the Evas_Object image updates with the evas_object_image_pixels_get_callback_set() callback function calls whenever Evas renders.

    static Eina_Bool _animate_cb(void *data)
       Evas_Object *obj = (Evas_Object *)data;
       evas_object_image_pixels_dirty_set(obj, EINA_TRUE);
       return EINA_TRUE;
    static bool app_create(void *data)
       appdata_s *ad = data;
       Ecore_Animator *animator = ecore_animator_add(_animate_cb, (void *)ad->img);
       return true;
  9. Delete Cairo and EvasGL.

    The delete callback is triggered when the Window is destroyed from the main loop.

    static void win_delete_request_cb(void *data, Evas_Object *obj, void *event_info)
       appdata_s *ad = data;
       evas_gl_surface_destroy(ad->evas_gl, ad->evas_gl_surface);
       evas_gl_context_destroy(ad->evas_gl, ad->evas_gl_context);