Evas
Date
2000 (created)

Table of Contents

Introduction

Evas is a clean display canvas API for several target display systems that can draw anti-aliased text, smooth super and sub-sampled scaled images, alpha-blend objects and much more.

It abstracts any need to know much about what the characteristics of your display system are or what graphics calls are used to draw them and how. It deals on an object level where all you do is create and manipulate objects in a canvas, set their properties, and the rest is done for you.

Evas optimises the rendering pipeline to minimise effort in redrawing changes made to the canvas and so takes this work out of the programmers hand, saving a lot of time and energy.

It is small and lean, and is designed to work on embedded systems all the way to large and powerful multi-CPU workstations. It can be compiled to only have the features you need for your target platform if you so wish, thus keeping it small and lean. It has several display back-ends, letting it display on several display systems, making it portable for cross-device and cross-platform development.

What Evas is not?

Evas is not a widget set or widget toolkit, however it is their base. See Elementary (http://docs.enlightenment.org/auto/elementary/) for a toolkit based on Evas, Edje, Ecore and other Enlightenment technologies.

It is not dependent or aware of main loops, input or output systems. Input should be polled from various sources and fed to Evas. Similarly, it does not create windows or report windows updates to your system, but just draws the pixels and report to the user the areas that were changed. Of course these operations are quite common and thus they are ready to use in Ecore, particularly in Ecore_Evas wrapper/helper set of functions.

How does Evas work?

Evas is a canvas display library. This is markedly different from most display and windowing systems as a canvas is structural and is also a state engine, whereas most display and windowing systems are immediate mode display targets. Evas handles the logic between a structural display via its state engine, and controls the target windowing system in order to produce rendered results of the current canvas' state on the display.

Immediate mode display systems retain very little, or no state. A program executes a series of commands, as in the pseudo code:

draw line from position (0, 0) to position (100, 200);

draw rectangle from position (10, 30) to position (50, 500);

bitmap_handle = create_bitmap();
scale bitmap_handle to size 100 x 100;
draw image bitmap_handle at position (10, 30);

The series of commands is executed by the windowing system and the results are displayed on the screen (normally). Once the commands are executed the display system has little or no idea of how to reproduce this image again, and so has to be instructed by the application on how to redraw sections of the screen whenever needed. Each successive command is executed as instructed by the application and either emulated by software or sent to the graphics hardware on the device to be performed.

The advantage of such a system is that it is simple, and gives a program tight control over how something looks and is drawn. Given the increasing complexity of displays and demands by users to have better looking interfaces, more and more work is needing to be done at this level by the internals of widget sets, custom display widgets and other programs. This means that more and more logic and display rendering code needs to be written each time the application needs to figure out how to minimise redraws so that display is fast and interactive, and keeps track of redraw logic. The power comes at a high-price with lots of extra code and work. Programmers not very familiar with graphics programming often make mistakes at this level and produce code that is sub optimal. Those familiar with this kind of programming simply get bored by writing the same code again and again.

For example, if in the above scene, the windowing system requires the application to redraw the area from 0, 0 to 50, 50 (also referred as "expose event"), then the programmer must calculate manually the updates and repaint it again:

Redraw from position (0, 0) to position (50, 50):

// what is in area (0, 0, 50, 50)?

// 1. intersection part of line (0, 0) to (100, 200)?
   draw line from position (0, 0) to position (25, 50);

// 2. intersection part of rectangle (10, 30) to (50, 500)?
   draw rectangle from position (10, 30) to position (50, 50)

// 3. intersection part of image at (10, 30), size 100 x 100?
   bitmap_subimage = subregion from position (0, 0) to position (40, 20)
   draw image bitmap_subimage at position (10, 30);

You might have noticed that, if all elements in the above scene are opaque, then the system is doing useless paints: part of the line is behind the rectangle, and part of the rectangle is behind the image. These useless paints tend to be very costly, as pixels tend to be 4 bytes in size; thus an overlapping region of 100 x 100 pixels is around 40000 useless writes! You could write code to calculate the overlapping areas and avoid painting then, but then it should be mixed with the "expose event" handling mentioned above and you quickly realize that the initially simpler method became really complex.

Evas is a structural system in which the programmer creates and manages display objects and their properties, and as a result of this higher level state management, the canvas is able to redraw the set of objects when needed to represent the current state of the canvas.

For example, the pseudo code:

line_handle = create_line();
set line_handle from position (0, 0) to position (100, 200);
show line_handle;

rectangle_handle = create_rectangle();
move rectangle_handle to position (10, 30);
resize rectangle_handle to size 40 x 470;
show rectangle_handle;

bitmap_handle = create_bitmap();
scale bitmap_handle to size 100 x 100;
move bitmap_handle to position (10, 30);
show bitmap_handle;

render scene;

This may look longer, but when the display needs to be refreshed or updated, you move, resize, show, or hide the objects that need to change. You can simply think at the object logic level, and the canvas software does the rest of the work for you, figuring out what actually changed in the canvas since it had been last drawn, how to most efficiently redraw the canvas and its contents to reflect the current state, and then it can go off and do the actual drawing of the canvas.

This lets you think in a more natural way when dealing with a display, and saves time and effort of working out how to load and display images, render given the current display system, and so on. Since Evas also is portable across different display systems, this also gives you the ability to have their code ported and displayed on different display systems with very little work.

Evas can be seen as a display system that stands somewhere between a widget set and an immediate mode display system. It retains basic display logic, but does very little high-level logic such as scrollbars, sliders, and push buttons.

How to compile

Evas is a library your application links to. The procedure for this is very simple. You simply have to compile your application with the appropriate compiler flags that the pkg-config script outputs. For example:

Compiling C or C++ files into object files:

gcc -c -o main.o main.c `pkg-config --cflags evas`

Linking object files into a binary executable:

gcc -o my_application main.o `pkg-config --libs evas`

See pkgconfig

Next Steps

After you understood what Evas is and installed it in your system you should proceed understanding the programming interface for all objects, then see the specific for the most used elements. We'd recommend you to take a while to learn Ecore, Edje and Elementary (http://docs.enlightenment.org/auto/elementary/) as they will likely save you tons of work compared to using just Evas directly.

Recommended reading:

Introductory Example

#include <Evas.h>
#include <Evas_Engine_Buffer.h>
#include <stdio.h>
#include <errno.h>
#define WIDTH (320)
#define HEIGHT (240)
static Evas *create_canvas(int width, int height);
static void destroy_canvas(Evas *canvas);
static void draw_scene(Evas *canvas);
static void save_scene(Evas *canvas, const char *dest);
int main(void)
{
Evas *canvas;
Evas_Object *bg, *r1, *r2, *r3;
/* After turning Evas on, we create an Evas canvas to work in.
* Canvases are graphical workspaces used for placing and organizing
* graphical objects. Normally we'd be using Ecore-Evas to create
* the canvas, but for this example we'll hide the details in a
* separate routine for convenience.
*/
canvas = create_canvas(WIDTH, HEIGHT);
if (!canvas)
return -1;
/* Next set the background to solid white. This is typically done by
* creating a rectangle sized to the canvas, placed at the canvas
* origin.
*
* Note that if the canvas were to change size, our background
* rectangle will not automatically resize itself; we'd need to do
* that manually with another evas_object_resize() call. In a real
* application using Ecore-Evas, functionality in Ecore will take
* care of resizing things. For this example, we'll just keep the
* canvas dimensions fixed to avoid the problem.
*/
evas_object_color_set(bg, 255, 255, 255, 255); // white bg, no transparency
evas_object_move(bg, 0, 0); // at origin
evas_object_resize(bg, WIDTH, HEIGHT); // covers full canvas
puts("initial scene, with just background:");
draw_scene(canvas);
/* To make the scene interesting let's add a few more rectangles of
* various sizes and colors, starting with a big red one.
*
* By default all Evas objects are created in a 'hidden' state,
* meaning they are not visible, won't be checked for changes during
* canvas rendering, and won't receive input events. Thus, like we
* did for the background object we must call evas_object_show() to
* make our graphics objects usable.
*/
evas_object_color_set(r1, 255, 0, 0, 255); // 100% opaque red
evas_object_move(r1, 10, 10);
evas_object_resize(r1, 100, 100);
/* Let's add a partly transparent rectangle on top of the red one.
*
* Graphics objects are treated as a stack in the canvas for drawing
* purposes, so subsequent objects are drawn above the ones we've
* already added to the canvas. This is important in objects that
* have partially transparent fill coloring since we'll see part of
* what's "behind" our object.
*
* In Evas, color values are pre-multiplied by their alpha. This means
* that if we want a green rectangle that's half transparent, we'd have:
*
* non-premul: r=0, g=255, b=0 a=128 (50% alpha)
* premul:
* r_premul = r * a / 255 = 0 * 128 / 255 = 0
* g_premul = g * a / 255 = 255 * 128 / 255 = 128
* b_premul = b * a / 255 = 0 * 128 / 255 = 0
*
* Since we're placing our half transparent green rectangle on top of
* a red one, in the final output we will actually see a yellow square
* (since in RGBA color green + red = yellow).
*/
evas_object_color_set(r2, 0, 128, 0, 128); // 50% opaque green
evas_object_move(r2, 10, 10);
evas_object_resize(r2, 50, 50);
/* Lastly, for comparison add a dark green rectangle with no
* transparency. */
evas_object_color_set(r3, 0, 128, 0, 255); // 100% opaque dark green
evas_object_move(r3, 60, 60);
evas_object_resize(r3, 50, 50);
puts("final scene (note updates):");
draw_scene(canvas);
/* In addition to displaying the canvas to the screen, let's also
* output the buffer to a graphics file, for comparison. Evas
* supports a range of graphics file formats, but PPM is particularly
* trivial to write, so our save_scene routine will output as PPM.
*/
save_scene(canvas, "/tmp/evas-buffer-simple-render.ppm");
destroy_canvas(canvas);
return 0;
}
/* Convenience routine to allocate and initialize the canvas.
* In a real application we'd be using ecore_evas_buffer_new() instead.
*/
static Evas *create_canvas(int width, int height)
{
Evas *canvas;
Evas_Engine_Info_Buffer *einfo;
int method;
void *pixels;
/* Request a handle for the 'buffer' type of rendering engine. */
method = evas_render_method_lookup("buffer");
if (method <= 0)
{
fputs("ERROR: evas was not compiled with 'buffer' engine!\n", stderr);
return NULL;
}
/* Create a general canvas object.
* Note that we are responsible for freeing the canvas when we're done. */
canvas = evas_new();
if (!canvas)
{
fputs("ERROR: could not instantiate new evas canvas.\n", stderr);
return NULL;
}
/* Specify that the canvas will be rendering using the buffer engine method.
* We also size the canvas and viewport to the same width and height, with
* the viewport set to the origin of the canvas.
*/
evas_output_method_set(canvas, method);
evas_output_size_set(canvas, width, height);
evas_output_viewport_set(canvas, 0, 0, width, height);
/* Before we can use the engine, we *must* set its configuration
* parameters. The available parameters are kept in a struct
* named Evas_Engine_Info which is internal to Evas. Thus to set
* parameters we must first request the current info object from
* our canvas:
*/
einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas);
if (!einfo)
{
fputs("ERROR: could not get evas engine info!\n", stderr);
evas_free(canvas);
return NULL;
}
/* Create the underlying data buffer that our canvas will use. This
* is a simple array of ARGB32 pixels. Each color component
* (including alpha) is one byte, resulting in 4 bytes per pixel (or
* 32 bits). We can thus store each pixel in an integer data type,
* thus calculating our data buffer as W x H x sizeof(int) bytes in
* length.
*/
pixels = malloc(width * height * sizeof(int));
if (!pixels)
{
fputs("ERROR: could not allocate canvas pixels!\n", stderr);
evas_free(canvas);
return NULL;
}
/* Next set the various configuration parameters. We
* register the pixel buffer that the canvas will use,
* indicate the pixel format as ARGB32, and the size of
* each row of data. */
einfo->info.depth_type = EVAS_ENGINE_BUFFER_DEPTH_ARGB32;
einfo->info.dest_buffer = pixels;
einfo->info.dest_buffer_row_bytes = width * sizeof(int);
einfo->info.use_color_key = 0;
einfo->info.alpha_threshold = 0;
einfo->info.func.new_update_region = NULL;
einfo->info.func.free_update_region = NULL;
/* Finally, we configure the canvas with our chosen parameters. */
return canvas;
}
/* Convenience routine to shut down the canvas.
* In a real application we'd be using ecore_evas_free() instead
*/
static void destroy_canvas(Evas *canvas)
{
Evas_Engine_Info_Buffer *einfo;
einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas);
if (!einfo)
{
fputs("ERROR: could not get evas engine info!\n", stderr);
evas_free(canvas);
return;
}
/* Free the data buffer we allocated in create_buffer() */
free(einfo->info.dest_buffer);
/* Finally, free the canvas itself. */
evas_free(canvas);
}
/* Convenience routine to update the scene.
* In a real application Ecore Evas would be doing this for us.
*/
static void draw_scene(Evas *canvas)
{
Eina_List *updates, *n;
Eina_Rectangle *update;
/* Render the canvas, and get a list of the updated rectangles. */
updates = evas_render_updates(canvas);
/* Just for informative purposes, print out the areas being updated: */
EINA_LIST_FOREACH(updates, n, update)
printf("UPDATED REGION: pos: %3d, %3d size: %3dx%3d\n",
update->x, update->y, update->w, update->h);
/* Free the list of update rectangles */
}
/* Output the canvas buffer to a Portable Pixel Map (PPM) file */
static void save_scene(Evas *canvas, const char *dest)
{
Evas_Engine_Info_Buffer *einfo;
const unsigned int *pixels, *pixels_end;
int width, height;
FILE *f;
/* Retrieve the current data buffer. */
einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas);
if (!einfo)
{
fputs("ERROR: could not get evas engine info!\n", stderr);
return;
}
/* Retrieve the canvas dimensions */
evas_output_size_get(canvas, &width, &height);
/* Open our output PPM file for writing */
f = fopen(dest, "wb+");
if (!f)
{
fprintf(stderr, "ERROR: could not open for writing '%s': %s\n",
dest, strerror(errno));
return;
}
/* Write out the pixel data to the PPM file */
pixels = einfo->info.dest_buffer;
pixels_end = pixels + (width * height);
/* PPM P6 format is dead simple to write. First we output a magic
* number 'P6' to designate the file as PPM, then the width and
* height on their own line in ASCII decimal, followed by the maximum
* color value (255) on its own line in ASCII decimal, and finally a
* the pixel data in RGB order with each color component written as
* a char (byte). No alpha information is stored.
*/
fprintf(f, "P6\n%d %d\n255\n", width, height);
for (; pixels < pixels_end; pixels++)
{
int r, g, b;
r = ((*pixels) & 0xff0000) >> 16;
g = ((*pixels) & 0x00ff00) >> 8;
b = (*pixels) & 0x0000ff;
fprintf(f, "%c%c%c", r, g, b);
}
fclose(f);
printf("saved scene as '%s'\n", dest);
}

More examples can be found at Evas Examples.