Simple Evas canvas example

The canvas will here use the buffer engine.

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