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 more.

It abstracts the graphics drawing characteristics of the display system by implementing a canvas where graphical objects can be created, manipulated, and modified. It then handles the rendering pipeline in an optimal way for the underlying device in order to minimize redraws, via a programmatically efficient API.

A design goal for the system is to run well at both small and large scale, and be portable from embedded systems to multi-CPU workstations. Architecturally, this is achieved via 'backends' that provide the specialized display logic for specific devices. As well, there are various compile options to exclude feature support not required for a target platform to help minimize disk and memory requirements.

Evas can serve as a base for widget sets or toolkits (e.g. Elementary, http://docs.enlightenment.org/auto/elementary/) by handling pixel drawing and regional change reporting, but does not manage windows itself, nor deal with input or window update event propagation. In other words, it is intended for use in drawing scrollbars, sliders, and push buttons but not for high-level logic of how the widget operates and behaves. Under Enlightenment, window and widget management is handled by other software components, including Ecore (see Ecore_Evas wrapper/helper set of functions in particular); however Evas is designed to not be dependent on any particular main loop architecture, and also strives to be input and output system agnostic.

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 does Evas work?

The Evas canvas is a 'retained mode' renderer, which differs from the more traditional 'immediate mode' display and windowing systems by tracking drawing state information of its contained objects.

In an immediate mode rendering system, each frame is drawn from scratch by having each drawing element redraw itself. Once the commands are executed, the display system blits the frame to the screen but has no idea how to reproduce the image again, so the application has to run through the same sequence of drawing commands again. Very little or no state is kept from one frame draw to the next; while this is simple it forces each application to manually optimize their graphics code.

With retained mode systems like Evas, the application does not need to implement the display rendering code and associated logic, but merely updates the list of objects maintained in the canvas. Evas is then able to optimize the processing and rendering of the visible elements, and is better able to avoid redraws due to occlusion or opacity.

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, consider 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;

By expressing the drawing as a set of drawable objects, Evas is able to internally handle refreshing, updating, moving, resizing, showing, and hiding the objects, and to determine to most efficiently redraw the canvas and its contents to reflect the current state. This permits the application to focus on the higher level logic, which both reduces the amount of coding and allows a more natural way of dealing with the display. Importantly, abstracting the display logic like this also simplifies porting the application to different display systems, since its own code is less tied into how that system works.

How to compile the library

Evas compiles automatically within EFL's build system, and is automatically linked with Ecore and other components that need it. But it can also be built and used standalone, by compiling and linking your application with the compiler flags indicated by pkg-config. For example:

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

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

See pkgconfig

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.