Page MenuHomePhabricator

Chapter 3. Understanding the Evas Canvas
Updated 476 Days AgoPublic

Prev | Next | Index


By now you should know that Evas is a canvas. But what makes it different from the other solutions that already exist? Each mature toolkit provides its own Canvas widget which if used correctly can really give the edge to your application. Let's see a quick summary of Evas features versus the traditional solutions.

Table 3.1. Evas versus the competition

Your Canvas widgetThe Evas Canvas
An extra widget of the toolkitThe base of the toolkit (EWL/ETK)
Part of an ApplicationTHE application itself
Shape/Vector basedImage based
State un-awareState aware
X11 back-endX11, framebuffer, Xrender, OpenGL back-end
Desktop usageDesktop/Embedded usage

While most users will perceive Evas-based applications as impressive applications with low requirements for computer resources, for developers things are different. The way you program Evas is a refreshing change from what you have seen before. Most of the points presented on the table are fairly easy to understand.

The last point regarding the speed of Evas is the easiest to recognize once you start programming with EFL. Graphic routines are highly optimized and carefully profiled (even for the software back-end) so that Evas really feels fast for users. If you ever hated Canvas widgets for their lack of speed, problematic scrolling, and low-quality resizing, Evas might be just what you are looking for.

We devote two separate sections on the key features that we think make Evas truly evolutionary.

What "Image-based" Means

Since computers became capable of displaying graphics, pixel-based images comprise the bulk of visual output presented on user screens. Images stored in pixels take a lot of space to store, but are rather easy and fast to load and display. The pixel information is stored on a big 2D array (in the simplest case) and displaying graphics means simply copying the colour values from file to screen (not exactly but you get the idea).

Apart from the problem with storage space, pixel-based graphics suffer also from fixed quality and resolution dependence. If the user has a big screen, graphics will look small, while in the opposite case graphics will look really cramped since the screen estate is constrained. This problem is especially evident with application icons which can not be easily resized and keep their sharpness. Current solutions are forced to include different sizes of icons for 3 or 4 possible resolutions in order to accommodate for different uses. This not only increases disk space requirements but also adds complexity to loading and locating the appropriate icon file. Well known is the fact that wallpapers for common desktop environments come in different sizes. But what happens if you use 1600x1200 resolution and your favourite wallpaper comes in 1024x768?

The software platforms are slowly but steadily moving to vector graphics. Vector graphics are destined to replace pixel-based graphics in many fields. Files which contain vector-based graphics do not store any pixel information at all. They rather contain a mathematical representation of an image. When the file is displayed the computer plots the mathematical function in real time on screen, and the user sees an image which is actually the result of rendered graphics.

The big advantage of vector graphics is the fact that they are truly resolution independent. Because the graphical representation is computed in real-time for each display, graphics will look the same (and always sharp) no matter the canvas they are rendered on. If you have 1600x1200 display your image will be rendered with these coordinates while if you have 1024x768 graphics will again be recomputed to adapt to the new resolution. Both open-source desktop environments support vector-based icons (stored in the svg format) which showcase that icon resizing can result in big, sharp icons with no loss of quality.

On the downside of course, this real-time rendering puts a load on the computer processor, since it has to calculate the result of the function each time the image is displayed. A pixel-based image will be loaded in a fraction of time that the same image will be displayed if stored in vector format. The vector-based file will be significantly smaller though, than the pixel-based one since only the math behind the image is stored rather than all the pixels.

Vector-based graphics will never be used for digital photos of course since these need pixels for information. Computer interfaces on the other size are a natural candidate for vector graphics. Being resolution independent is a big plus for applications. And since processors get faster each year, the rendering overhead will slowly disappear.

This is is the reason why many Canvas widgets are vector based. They implement a complex mathematical layout engine (can be even postscript) which allows the programmer to display complex graphics with anti-aliasing, sharp edges and curved lines all in a rectangular window which is resolution agnostic. This might be perfect for mathematical and plotting applications (or even for custom widgets) but not for complete interfaces. This kind of canvas widgets can also not be used on embedded systems which have limited processing capacity and vector graphics will be inherently slow. The next figure outlines the shortcomings.

Figure 3.1. Workflow in your favourite Canvas widget

Evas takes a completely different approach. While most Canvas widgets can also display pixel-based images as an extra feature, Evas is actually targeted on them. The Evas primitive list is rather short. Text, rectangles, lines, polygons, and images are the most basic primitives. You won't find arcs, circles, arrows, bezier curves, or any other traditional Canvas methods if you search the Evas documentation. To create an Evas based interface you store all elements in normal pixel-based files (e.g. in PNG format) and have Evas load and resize them when the interface is activated. The interface elements themselves can be processed with Gimp as normal images or even be created as vectors in Inkscape and later converted to pixels and distributed as a theme.

This means that graphics are no longer a job of the programmer but become a job of the Gimp artist/Guru where they belong. Evas does not suffer from complex layout mathematical languages and therefore can be used on embedded systems too. Evas graphics are not resolution independent, but since Evas has rather sophisticated resizing algorithms (as imlib2) for most cases graphics will maintain their quality. The following figure shows the main idea.

Figure 3.2. Workflow in Evas.

So will Evas boost your applications? If your canvas is going to plot complex mathematic functions for a scientific application then Evas won't help you much. But if you intend to create a beautiful application with interface elements scrolling around the screen, windows sliding in and out, and crazy animations composed of many picture frames, then Evas will certainly come to rescue you.

What "State-aware" Means

Canvas widgets for pre-rendered graphics are not anything new of course. Evas is a well-thought solution heavily optimized for images and is also field tested in the Enlightenment window manager and the rest of EFL applications which are under development. If you were reading the previous chapters, shaking your head and thinking that there is nothing extraordinary about Evas, then you might want to reconsider after reading this chapter.

Evas does its own bookkeeping. It knows the whole state of the Canvas at any given moment. While most other Canvas widgets are one layer above the X abstractions (exposing windows and stuff), Evas is rather smart and sophisticated. It frees the programmer from keeping his/her own data-structures for objects shown on screen, and allows one to concentrate on the actual interaction of these objects rather than their memory management.

We could spend a lot of paragraphs talking about this subject, but you could easily understand if we show some pseudocode. We noted on the beginning that this is not a technical document, so don't expect to see details of Evas API. The code presented here is only describing concepts and not showing how Evas works.

Joe Programmer is attempting to create his first canvas application. Joe is a seasoned C programmer but also knows C++ and maybe Java. He knows that a C API can be centered around "objects" (think GTK+) and he has some minimal exposure to X11 toolkits. Before starting actual development, Joe thinks how the API of the Canvas should look. Since he does not believe that a Canvas is something extraordinary he concludes that adding some more statements to the program main should do the trick. Here is what a Canvas should look like in his mind.

Example 3.1. A simple Canvas in object-oriented style (even for C)

int main()
{
	Canvas *a_canvas;
	Rectangle *rect;
	Image *image;
	
	a_canvas=create_new_canvas(800,600); 

	rect=create_new_rectangle();
	draw_rect_on_canvas(a_canvas,rect);
	move_rect(rect,10,50);
	resize_rect(rect, 100,100);
	
	image=create_new_from_file("button.png");
	draw_image_on_canvas(a_canvas,image);
	move_image(image, 150,200);

	show_rect(rect);
	show_image(image);
	show_canvas(canvas);

	//Continue with rest of the program
	[...]
}

Looks rather logical. With that in mind Joe starts to look at the API of the Canvas widget he uses only to find that things are a bit more complex. He learns that the canvas has a paint function which is run when the canvas is redrawn (either at short time periods or after an on-screen event). In order to make the Canvas draw something he must collect all objects that need to be drawn and pass them to the canvas. He also has to override/extend/call/redefine (pick your favourite) the paint function of the canvas. After some coding he finally gets the Canvas to show stuff with the following code listing:

Example 3.2. What happens in reality

//Include files which contain implementation
//of linked lists or other data structures.
[...]

int main()
{
	Canvas *a_canvas;
	List *objects_to_be_drawn;
	
	a_canvas=create_new_canvas(800,600);

	//Setup a callback. VERY IMPORTANT
	set_paint_function_of_canvas(a_canvas,my_repaint); 

	rect=create_new_rectangle();
	set_coords_rect(rect,10,50);
	set_size_rect(rect, 100,100);
	//Append the rectangle to objects drawn by Canvas
	add_rect(objects_to_be_drawn,rect); 
	
	image=create_new_from_file("button.png");
	set_coord_image(150,200);
	//Append the image to objects drawn by Canvas
	add_image(objects_to_be_drawn,image); 

	show_canvas(canvas);
	repaint(canvas); //Here the my_repaint function is called.

	//Continue with rest of the program
	[...]
}

//Function which smells X11 internals (what happens after an expose event)
void my_repaint(canvas *where)
{
	canvas_object *current;
	while(objects_to_be_drawn !=EMPTY)
	{
		current=get_next_object(objects_to_be_drawn);
		draw_object(where,current); //Finally each object is drawn.
	}
}

While the actual code is not a lot longer than what he had in mind, Joe makes a couple of observations. First, he is forced to keep by himself all canvas objects in a separate data structure. The List objects_to_be_drawn holds everything that should appear on screen. Joe never actually draws directly on the Canvas. He just adds objects to this List and informs the Canvas (via a separate callback function) that it should process it for the actual drawing. The second important observation is the fact that whichever function would like to access the canvas cannot directly access the Canvas object. Instead it needs both the Canvas object and the objects_to_be_drawn List which represents its state. To actually change anything, this List should be changed first and then the Canvas should be ordered to redraw again.

Later on, Joe needs to do something simple. He wants the user to be able to click an object on the canvas and have that object move on the top left of the screen (at x=10 and y=10). He searches again and again the Canvas API only to discover that he has to write a lot of code himself. The Canvas API is full of functions that draw new objects on screen but almost no functions that deal with existing objects on screen. After some effort and not sure about himself anymore he reaches the following code:

Example 3.3. Moving an object

void user_clicked_on_canvas(canvas *where)
{
	canvas_object *clicked_by_the_user;
	int x;
	int y;

	x=get_pointer_x(where);
	y=get_pointer_y(where);

	clicked_by_the_user=search_what_object_is_there(x,y,objects_to_be_drawn);

	//Let's say it is a rectangle for simplicity
	set_coords_rect(clicked_by_the_user,10,10);
	repaint(canvas); //Here the canvas is repainted;
}

canvas_object *search_what_object_is_there(x,y,objects_to_be_drawn)
{

	canvas_object *current;
	while(objects_to_be_drawn!=NULL)
	{
		current=get_next_object(objects_to_be_drawn);
		if(is_x_in_object(x,current)==1);
		{
			if(is_y_in_object(y,current)==1);
			return current; //Found it
		}
	}
}

Joe is a bit disappointed now. He sees that he has to keep the state of canvas in custom data structures, and manually control when the canvas is updated. He also sees that once objects are drawn it is hard to find them again. Complex functions which look into the custom structures must be implemented if the Canvas is to do anything useful rather than act as a simple scenery view. Joe starts to think that he is spending more code on Canvas management instead of the application code. He finally realizes the sad truth. The canvas is just a 2D array of pixels. Once an object is drawn on the Canvas it loses all character. Its pixels are the same as the rest of the Canvas which are not drawn. This Canvas is essentially dumb and "state unaware".

Joe starts to ponder why the situation is like this. If he had a "state-aware" canvas, coding would be much easier. Such a Canvas would know all of its objects. It would be able to access and move them on demand. It would not bother the programmer with Canvas management. The programmer would concentrate on the functionality and not on the Canvas widget. With such a canvas the previous example becomes trivial.

Example 3.4. Moving an object with a state-aware Canvas

void user_clicked_on_canvas(canvas *where)
{
	canvas_object *clicked_by_the_user;
	int x;
	int y;

	x=get_pointer_x(where);
	y=get_pointer_y(where);

	//The next  function is already implemented by the canvas itself.
	clicked_by_the_user=check_which_object_is_in(x,y,canvas); 

	//Let's say it is a rectangle for simplicity
	move_rect(clicked_by_the_user,10,10);
}

You can see where this is heading. This ideal "state-aware" Canvas is actually Evas. If you find yourself often in Joe's shoes, include Evas in your programs and never look back again. A whole lot of infrastructure code is already written for you. The Canvas is a self-contained object which is smart enough to know what is going on under the surface.

Evas also is the same Canvas Joe was thinking as ideal (see the first code-listing example). Evas implements a Canvas "as it should be done" in the first place. Everything about Evas is logical. The author of the document is clearly biased, but he believes that if you invest time in Evas, the reward will be great. For big programs where Canvas management overruns the actual application, Evas will show the difference and allow you to build your application as you were imagining it from the beginning, rather than searching all the time the Canvas API for things you need.

Evas is here now. Power your applications with it!

Available Programming Facilities

Now that you are familiar with the Evas development model, it is time to delve a bit deeper. This section outlines the general capabilities of Evas. It helps you understand what is available and what is not. Since Evas is not released officially yet, the complete details are a moving target.

The following table lists the Evas primitives at the disposal of the programmer. These are offered in the form of "Evas Objects" in the code.

Table 3.2. Evas primitives

PrimitiveShort description
RectTypical rectangle; useful for clipping too
ImageThe most common Evas object
TextText object; one line only
TextBlockMulti-line text object
LineLine object with start and end points
PolygonGeneral-purpose shape
GradientLinear, radial, rectangular, etc.

Many times you may have found yourself writing several lines of code in order to accomplish something, only to find out that your library already implements it. Avoiding this is easy. Reading beforehand what a library offers, rather than just diving into the code as soon as possible is the obvious solution. For example, Evas implements some basic data structures, so writing an Evas-only application does not mean that you will have to implement them yourself.

  • Clipping functions

Clipping means showing only the parts of an object that are contained (constrained) by a second object. Currently Evas supports clipping only to Rectangles.

  • Object data functions

You can attach and retrieve any type of data to an Evas primitive. Storage is performed on a key-data pair fashion.

  • Different display layers

Objects can have different Z-ordering. The layer number is not fixed. Transparency works as expected. That is, creating a transparent object on the upper layer will make objects on the lower level layer visible. Evas makes transparent interfaces really easy, and more importantly fast.

  • Pointer functions

Evas allows you to detect if the (mouse) pointer is within the Canvas, and what are its coordinates. You can even get the state of mouse buttons (if they are pressed or not). You can also translate from screen to Canvas coordinates and vice-versa.

  • Object finder functions

Since Evas knows the state of its objects as already explained, it gives you access to a lot of functions for retrieving currently drawn objects. You can search object by name, coordinates, and pointer position. You can even get a list of objects which are contained in a specific rectangular area of the Canvas.

  • Simple Data Structures

For your own convenience Evas comes with some useful Data Structures. Currently a hash-table implementation and a linked-list one are offered. Even if you use your own or exploit external third-party libraries, you should at least learn the linked List API. This is because all Evas functions which deal with collections of objects, strings, etc. actually return an Evas List.

  • Callback binding functions

Since Evas is a Canvas, it also offers the usual callback binding facilities. These are employed in order to respond to several external events including resizing and moving the canvas, using the mouse, or typing on the keyboard.

  • Evas Smart objects

Evas allows you to create you own Evas objects and have them implement the same API used for built-in objects too. Naturally, this is a great way to group several different primitives to form more complex objects without the need of custom glue and wrapper code. Not to be confused with the EFL Smart library.

  • Evas Engine functions

Most programmers will use the X11 back-end in general. Evas runs on other devices too. The framebuffer is another good candidate found on embedded systems (which do not run an X server).

  • Text effects and style

The Text object of Evas is not as limited as you might think. Evas includes some built-in styles, which will make text appear on screen a little more appealing. Shadows, outlines, and glows including combinations among them are already there.

As always, you should look into the code if you want to know everything that Evas offers. Not all features are documented yet and some of course are more experimental than others.


Prev: Chapter 2. The EFL Structure | Next: Chapter 4. Understanding the Edje Layout Engine | IntroductionToEFL Index

Last Author
ajwillia.ms
Last Edited
Jan 29 2018, 5:35 AM
Projects
None
Subscribers
None