From aa9f1618021fe8b86a41809c4704a06c15901d81 Mon Sep 17 00:00:00 2001 From: Bryce Harrington Date: Fri, 9 Jun 2017 12:09:54 +0900 Subject: [PATCH] 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 --- unsorted/evas/evas-buffer-simple.c | 166 +++++++++++++++++++++++------ 1 file changed, 133 insertions(+), 33 deletions(-) diff --git a/unsorted/evas/evas-buffer-simple.c b/unsorted/evas/evas-buffer-simple.c index e2d02e9a..96011082 100644 --- a/unsorted/evas/evas-buffer-simple.c +++ b/unsorted/evas/evas-buffer-simple.c @@ -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++) {