Evas

e_big.png
Version:
1.0.0
Author:
Carsten Haitzler <raster@rasterman.com>
Till Adam <till@adam-lilienthal.de>
Steve Ireland <sireland@pobox.com>
Brett Nash <nash@nash.id.au>
Tilman Sauerbeck <tilman@code-monkey.de>
Corey Donohoe <atmos@atmos.org>
Yuri Hudobin <glassy_ape@users.sourceforge.net>
Nathan Ingersoll <ningerso@d.umn.edu>
Willem Monsuwe <willem@stack.nl>
Jose O Gonzalez <jose_ogp@juno.com>
Bernhard Nemec <Bernhard.Nemec@viasyshc.com>
Jorge Luis Zapata Muga <jorgeluis.zapata@gmail.com>
Cedric Bail <cedric.bail@free.fr>
Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
Vincent Torri <vtorri@univ-evry.fr>
Tim Horton <hortont424@gmail.com>
Tom Hacohen <tom@stosb.com>
Mathieu Taillefumier <mathieu.taillefumier@free.fr>
Iván Briano <ivan@profusion.mobi>
Gustavo Lima Chaves <glima@profusion.mobi>
Date:
2000-2010

Table of Contents

What is Evas?

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 much and 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's small and lean, 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 feed them to Evas. Similarly, it will not create windows or report windows updates to your system, rather just drawing the pixels and reporting 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 (http://docs.enlightenment.org/auto/ecore/).

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 canvases state on the display.

Immediate mode display systems retain very little, or no state. A program will execute 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 how to redraw sections of the screen whenever needed. Each successive command will be 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 more and more logic and display rendering code needs to be written time and time again, each time the application needs to figure out how to minimise redraws so that display is fast and interactive, and keep track of redraw logic. The power comes at a high-price, lots of extra code and work. Programmers not very familiar with graphics programming will often make mistakes at this level and produce code that is sub optimal. Those familiar with this kind of programming will 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 was 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);

The clever reader 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 tends 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! The developer 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 quickly one realizes 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, the programmer only moves, resizes, shows, hides etc. the objects that they need to change. The programmer simply thinks at the object logic level, and the canvas software does the rest of the work for them, figuring out what actually changed in the canvas since it was last drawn, how to most efficiently redraw he 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 the programmer 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 etc. Since Evas also is portable across different display systems, this also gives the programmer the ability to have their code ported and display 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, push buttons etc.

How to compile using Evas ?

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`

You simply have to make sure that pkg-config is in your shell's PATH (see the manual page for your appropriate shell) and evas.pc in /usr/lib/pkgconfig or its path is in the PKG_CONFIG_PATH environment variable. It's that simple to link and use Evas once you have written your code to use it.

Since the program is linked to Evas, it is now able to use any advertised API calls to display graphics in a canvas managed by Evas, as well as use the API calls provided to manage data as well.

You should make sure you add any extra compile and link flags to your compile commands that your application may need as well. The above example is only guaranteed to make Evas add it's own requirements.

How is it installed?

Simple:

./configure
make
su -
...
make install

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 (http://docs.enlightenment.org/auto/ecore/) and Edje (http://docs.enlightenment.org/auto/edje/) as they will likely save you tons of work compared to using just Evas directly.

Recommended reading:

Introductory Example

/**
 * Simple Evas example using the Buffer engine.
 *
 * You must have Evas compiled with the buffer engine, and have the
 * evas-software-buffer pkg-config files installed.
 *
 * Compile with:
 *
 * @verbatim
 * gcc -o evas-buffer-simple evas-buffer-simple.c `pkg-config --libs --cflags evas evas-software-buffer`
 * @endverbatim
 *
 */
#include <Evas.h>
#include <Evas_Engine_Buffer.h>
#include <stdio.h>
#include <errno.h>

#define WIDTH (320)
#define HEIGHT (240)

/*
 * create_canvas(), destroy_canvas() and draw_scene() are support functions.
 *
 * They are only required to use raw Evas, but for real world usage,
 * it is recommended to use ecore and its ecore-evas submodule, that
 * provide convenience canvas creators, integration with main loop and
 * automatic render of updates (draw_scene()) when system goes back to
 * main loop.
 */
static Evas *create_canvas(int width, int height);
static void destroy_canvas(Evas *canvas);
static void draw_scene(Evas *canvas);

// support function to save scene as PPM image
static void save_scene(Evas *canvas, const char *dest);

int main(void)
{
   Evas *canvas;
   Evas_Object *bg, *r1, *r2, *r3;

   evas_init();

   // create your canvas
   // NOTE: consider using ecore_evas_buffer_new() instead!
   canvas = create_canvas(WIDTH, HEIGHT);
   if (!canvas)
     return -1;

   bg = evas_object_rectangle_add(canvas);
   evas_object_color_set(bg, 255, 255, 255, 255); // white bg
   evas_object_move(bg, 0, 0);                    // at origin
   evas_object_resize(bg, WIDTH, HEIGHT);         // covers full canvas
   evas_object_show(bg);

   puts("initial scene, with just background:");
   draw_scene(canvas);

   r1 = evas_object_rectangle_add(canvas);
   evas_object_color_set(r1, 255, 0, 0, 255); // 100% opaque red
   evas_object_move(r1, 10, 10);
   evas_object_resize(r1, 100, 100);
   evas_object_show(r1);

   // pay attention to transparency! Evas color values are pre-multiplied by
   // alpha, so 50% opaque green is:
   // 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
   //
   // this 50% green is over a red background, so it will show in the
   // final output as yellow (green + red = yellow)
   r2 = evas_object_rectangle_add(canvas);
   evas_object_color_set(r2, 0, 128, 0, 128); // 50% opaque green
   evas_object_move(r2, 10, 10);
   evas_object_resize(r2, 50, 50);
   evas_object_show(r2);

   r3 = evas_object_rectangle_add(canvas);
   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);
   evas_object_show(r3);

   puts("final scene (note updates):");
   draw_scene(canvas);
   save_scene(canvas, "/tmp/evas-buffer-simple-render.ppm");

   // NOTE: use ecore_evas_buffer_new() and here ecore_evas_free()
   destroy_canvas(canvas);

   evas_shutdown();

   return 0;
}

static Evas *create_canvas(int width, int height)
{
   Evas *canvas;
   Evas_Engine_Info_Buffer *einfo;
   int method;
   void *pixels;

   method = evas_render_method_lookup("buffer");
   if (method <= 0)
     {
        fputs("ERROR: evas was not compiled with 'buffer' engine!\n", stderr);
        return NULL;
     }

   canvas = evas_new();
   if (!canvas)
     {
        fputs("ERROR: could not instantiate new evas canvas.\n", stderr);
        return NULL;
     }

   evas_output_method_set(canvas, method);
   evas_output_size_set(canvas, width, height);
   evas_output_viewport_set(canvas, 0, 0, width, height);

   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;
     }

   // ARGB32 is sizeof(int), that is 4 bytes, per pixel
   pixels = malloc(width * height * sizeof(int));
   if (!pixels)
     {
        fputs("ERROR: could not allocate canvas pixels!\n", stderr);
        evas_free(canvas);
        return NULL;
     }

   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;
   evas_engine_info_set(canvas, (Evas_Engine_Info *)einfo);

   return canvas;
}

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(einfo->info.dest_buffer);
   evas_free(canvas);
}

static void draw_scene(Evas *canvas)
{
   Eina_List *updates, *n;
   Eina_Rectangle *update;

   // render and get the updated rectangles:
   updates = evas_render_updates(canvas);

   // informative only here, just print the updated areas:
   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 list of updates
   evas_render_updates_free(updates);
}

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;

   einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(canvas);
   if (!einfo)
     {
        fputs("ERROR: could not get evas engine info!\n", stderr);
        return;
     }
   evas_output_size_get(canvas, &width, &height);

   f = fopen(dest, "wb+");
   if (!f)
     {
        fprintf(stderr, "ERROR: could not open for writing '%s': %s\n",
                dest, strerror(errno));
        return;
     }

   pixels = einfo->info.dest_buffer;
   pixels_end = pixels + (width * height);

   // PPM P6 format is dead simple to write:
   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);
}
Todo:

(1.0) Need a way ot scaling an image and just PRODUCING the output (scaling direct to target buffe r- no blend/copy etc.)

(1.0) Could improve evas's scaling down code to limit multiple samples per output pixel to maybe 2x2?

(1.0) Document API

(1.0) Evas needs to check delete_me member for all object functions

(1.0) Evas engine that renders to Evas_Objects

(1.0) OpenGL engine needs to use texture meshes

(1.0) OpenGL engine needs texture cache and size setting

(1.0) OpenGL Engine needs YUV import API to YUV texture

(1.0) All engines need pixel import API

(1.0) Add parital render through composite layer api to engines

(1.0) Move callback processing to a queue and do it asynchronously???

(1.0) Add button grabbing

(1.0) Add generic object method call system

(1.0) Add callbacks set for smart object parents to be set on all child smart objects too.

(1.0) Add font load query calls (so we know if a font load failed)

(1.0) Add font listing calls

(1.0) Add ability to check image comments & disk format

(1.0) Add fontset support

(1.0) Export engine rendering API cleanly to Evas API

(1.0) Add smart object ability to provide rendering callback

(1.1) Make freetype optional and put in optional graymap font engine

(1.1) Free images if object invisible (and put back in chache)

(1.1) Check robustness of malloc/calloc/realloc failures.

(1.1) Add memory use reduction code if any allocations fail

(1.1) If image loads fails due to memory allocatue failure, load reduced res version

(1.1) If image load fails due to memory allocation failure, try split it up into tiles and demand-load them

(1.2) Add external image loaders (application provided callbacks to load)

(1.2) Add loadable image loader module support (evas loads file.so)

(1.2) Add external image loader modules (application provides path to file.so)

(1.3) Add X11 primtive engine (ie pixmap)

(1.3) Add immediate mode drawing commands to image objects

(1.3) Fix FB engine to allocate vt and release properly

(1.4) Add ellipse objects (circle, arc, ellipse etc.)

(1.5) Make software engine draw lines & polys etc. with aa

(1.5) Add radial gradients to gradient objects

(1.5) Add Symbian Engine

(1.6) Add PalmOS Engine

(1.6) Add Apple OpenGL Engine

(1.7) Document engine API and other internals

(1.7) Allow any object to clip any other object, and not just rectangles

(1.8) Add more evas demos

(1.9) Write the error mechanism in evas_xcb_buffer.c

(1.9) Rewrite the render xcb engine

(1.10) Improve Win32 Direct3D Engine