examples: Flesh out the evas-buffer-simple example
Summary: This serves as an early example for new Evas programmers to introduce the buffer engine as well as very basic canvas usage, so we're going into a lot more detail for both of those than we'll do in subsequent examples. Reviewers: cedric Subscribers: jpeg Differential Revision: https://phab.enlightenment.org/D4950
This commit is contained in:
parent
8110e09a8b
commit
aa9f161802
|
@ -1,8 +1,29 @@
|
|||
/**
|
||||
* Example of using the buffer engine in Evas.
|
||||
*
|
||||
* You must have Evas compiled with the buffer engine, and have the
|
||||
* evas-software-buffer pkg-config files installed.
|
||||
* In the evas-init-shutdown.c example we looked at how to turn Evas on
|
||||
* and off. In this example we'll turn it on and off but also do
|
||||
* something in between. We'll set up a canvas and add a few rectangles
|
||||
* to show how graphics objects are configured.
|
||||
*
|
||||
* In Evas, graphic rendering is done by a 'backend engine', which can
|
||||
* be changed to match the capabilities of the underlying hardware. For
|
||||
* this example to keep things simple we will use the 'buffer engine'.
|
||||
* The buffer engine simply does all the rendering directly into a memory
|
||||
* buffer, with no hardware acceleration.
|
||||
*
|
||||
* In real world usage you probably would not be using the raw buffer
|
||||
* access that we'll be looking at here, but instead using higher level
|
||||
* functionality from Ecore and Ecore's Ecore-Evas submodule. Ecore
|
||||
* provides convenience routines for creating and managing the canvas,
|
||||
* integrating with the main loop, and automating scene drawing. Since
|
||||
* we're not yet ready to dig into Ecore's functionality, we'll
|
||||
* substitute our own dummy routines create_canvas(), destroy_canvas(),
|
||||
* and draw_scene(), so we can focus on the overall process flow in
|
||||
* main().
|
||||
*
|
||||
* For this example you must have Evas compiled with the buffer engine,
|
||||
* and have the evas-software-buffer pkg-config files installed.
|
||||
*
|
||||
* @verbatim
|
||||
* gcc -o evas-buffer-simple evas-buffer-simple.c `pkg-config --libs --cflags evas evas-software-buffer`
|
||||
|
@ -17,21 +38,9 @@
|
|||
#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)
|
||||
|
@ -41,14 +50,29 @@ int main(void)
|
|||
|
||||
evas_init();
|
||||
|
||||
// create your canvas
|
||||
// NOTE: consider using ecore_evas_buffer_new() instead!
|
||||
/* 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.
|
||||
*/
|
||||
bg = evas_object_rectangle_add(canvas);
|
||||
evas_object_color_set(bg, 255, 255, 255, 255); // white bg
|
||||
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
|
||||
evas_object_show(bg);
|
||||
|
@ -56,28 +80,50 @@ int main(void)
|
|||
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.
|
||||
*/
|
||||
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)
|
||||
/* 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).
|
||||
*/
|
||||
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);
|
||||
|
||||
/* Lastly, for comparison add a dark green rectangle with no
|
||||
* transparency. */
|
||||
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);
|
||||
|
@ -86,9 +132,14 @@ int main(void)
|
|||
|
||||
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");
|
||||
|
||||
// NOTE: use ecore_evas_buffer_new() and here ecore_evas_free()
|
||||
destroy_canvas(canvas);
|
||||
|
||||
evas_shutdown();
|
||||
|
@ -96,6 +147,9 @@ int main(void)
|
|||
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;
|
||||
|
@ -103,6 +157,7 @@ static Evas *create_canvas(int width, int height)
|
|||
int method;
|
||||
void *pixels;
|
||||
|
||||
/* Request a handle for the 'buffer' type of rendering engine. */
|
||||
method = evas_render_method_lookup("buffer");
|
||||
if (method <= 0)
|
||||
{
|
||||
|
@ -110,6 +165,8 @@ static Evas *create_canvas(int width, int height)
|
|||
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)
|
||||
{
|
||||
|
@ -117,10 +174,20 @@ static Evas *create_canvas(int width, int height)
|
|||
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)
|
||||
{
|
||||
|
@ -129,7 +196,13 @@ static Evas *create_canvas(int width, int height)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// ARGB32 is sizeof(int), that is 4 bytes, per pixel
|
||||
/* 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)
|
||||
{
|
||||
|
@ -138,6 +211,10 @@ static Evas *create_canvas(int width, int height)
|
|||
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);
|
||||
|
@ -145,11 +222,16 @@ static Evas *create_canvas(int width, int height)
|
|||
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. */
|
||||
evas_engine_info_set(canvas, (Evas_Engine_Info *)einfo);
|
||||
|
||||
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;
|
||||
|
@ -162,27 +244,34 @@ static void destroy_canvas(Evas *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 and get the updated rectangles:
|
||||
/* Render the canvas, and get a list of the updated rectangles. */
|
||||
updates = evas_render_updates(canvas);
|
||||
|
||||
// informative only here, just print the updated areas:
|
||||
/* 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 list of updates
|
||||
/* Free the list of update rectangles */
|
||||
evas_render_updates_free(updates);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
@ -190,14 +279,18 @@ static void save_scene(Evas *canvas, const char *dest)
|
|||
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)
|
||||
{
|
||||
|
@ -206,10 +299,17 @@ static void save_scene(Evas *canvas, const char *dest)
|
|||
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:
|
||||
/* 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++)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue