Graphics library (libgraph)#

libgraph is a graphics library that allows for scheduling and (possibly hardware accelerated) execution of 2D graphics operations.

Source code: https://github.com/phoenix-rtos/phoenix-rtos-corelibs/tree/master/libgraph

Contents#

Graphics adapters#

The library supports the following graphics adapters:

  • virtio-gpu - virtualized graphics adapter compatible with VirtIO specification (available on QEMU or VirtualBox)

  • vga - VGA compatible graphics adapter

  • cirrus - Cirrus Logic GD5446 SuperVGA adapter

libgraph apps#

Examples of applications, which use graphics library (ia32-generic-qemu target architecture).

  • voxeldemo

    Output sample

    Source code can be found in the _user directory in phoenix-rtos-project repository.

    The app can be run using the following command:

    /usr/bin/voxeldemo
    
  • rotrectangle

    Image

    Source code can be also found in the _user directory in phoenix-rtos-project repository.

    The app can be run using the following command:

    /usr/bin/rotrectangle
    
  • test_graph

    Image

    Source code is available in the gfx directory in phoenix-rtos-tests repository.

    The test can be run using the following command:

    /bin/test_graph
    

libgraph interface#

libgraph functions take graph argument which is a pointer to graph_t structure initialized by graph_open(). graph_adapter_t is an enum used to distinguish between different graphics adapters.

  • graph_init - Initializes the graphics library and all required drivers.

    int graph_init(void)
    
  • graph_open - Initializes the graph_t structure and opens a context for the specified graphics adapter.

    int graph_open(graph_t *graph, graph_adapter_t adapter, unsigned int mem)
    

    The uninitialized graph_t structure should be passed in the graph argument and the graphics adapter should be chosen from the following list:

    • GRAPH_NONE - the graphics adapter isn’t specified, in this case, the function returns -ENODEV

    • GRAPH_VIRTIOGPU - generic VirtIO GPU graphics adapter

    • GRAPH_VGA - generic VGA graphics adapter

    • GRAPH_CIRRUS - Cirrus Logic graphics adapter

    • GRAPH_ANY - an available graphics adapter is chosen

    The mem argument specifies the total graphics tasks queue size. The bigger the mem the more graphics tasks we can queue up.

    The return value is set to EOK, or an error number, for example:

    • -EINVAL - specified memory value is too low

    • -ENOMEM - memory allocation for graphics context failed

    • -ENODEV - incorrect graphics adapter passed in adapter

  • graph_mode - Sets graphics mode with specified screen refresh rate frequency.

    int graph_mode(graph_t *graph, graph_mode_t mode, graph_freq_t freq)
    

    The initialized graph structure should be passed, and mode should be chosen from the graph_mode_t enum, and placed in the graph.h header.

    The common graphics modes are presented below:

    • GRAPH_DEFMODE - default graphics mode

    • GRAPH_ON - display enabled mode

    • GRAPH_OFF - display disabled mode

    • GRAPH_STANDBY - display standby mode

    • GRAPH_SUSPEND - display suspend mode

    • GRAPH_800x600x8 - 800x600 resolution, 8-bit indexed color mode

    • GRAPH_320x200x16 - 320x200 resolution, 16-bit color mode

    • GRAPH_1920x1080x32 - 1920x1080 resolution, 32-bit color mode

    There should also be specified the freq argument (graph_freq_t enum). The common screen refresh rates are presented below:

    • GRAPH_DEFFREQ - default refresh rate

    • GRAPH_24Hz - 24Hz refresh rate

    • GRAPH_60Hz - 60Hz refresh rate

    • GRAPH_120Hz - 120Hz refresh rate

    • GRAPH_360Hz - 360Hz refresh rate

  • graph_line - Draws a line for the specified graphics adapter context.

    int graph_line(graph_t *graph, unsigned int x, unsigned int y, int dx, int dy, unsigned int stroke, unsigned int
    color, graph_queue_t queue)
    

    The following arguments should be passed:

    • graph - initialized graph_t structure

    • x - start point x position, where 0 is the left edge of the screen

    • y - start point y position, where 0 is the upper edge of the screen

    • dx - line length in x-axis in pixels

    • dy - line length in y-axis in pixels

    • stroke - line thickness in pixels

    • color - line color

      • For 8-bit indexed color default VGA color palette is presented below (e.g. 0x0B represents cyan).

        Image

        Source: https://www.fountainware.com/EXPL/vga_color_palettes.htm

      • For 16-bit color depth - it’s the following format in bits: RRRRRGGGGGGBBBBB (e.g. 0x07E0 represents green)

      • For 24-bit color depth - it’s represented by 0xRRGGBB (e.g. 0x0000FF represents blue)

      • For 32-bit color depth - it’s the following format in hex: 0xAARRGGBB, where A represents alpha. With AA set to 0xFF opacity equals 100%, but by default transparency isn’t enabled, so there can be any value.

    • queue - graphics task queue, should be chosen from the graph_queue_t enum, where are following options:

      • GRAPH_QUEUE_HIGH - high priority queue

      • GRAPH_QUEUE_LOW - low priority queue

      • GRAPH_QUEUE_BOTH - any queue

      • GRAPH_QUEUE_DEFAULT - default queue

  • graph_rect - Draws a rectangle. Arguments are similar to those in graph_line() function. A drawn rectangle will be filled with a specified color.

    int graph_rect(graph_t *graph, unsigned int x, unsigned int y, unsigned int dx, unsigned int dy, unsigned int
    color, graph_queue_t queue)
    
  • graph_fill - Fills a closed figure with the color specified in the color argument ((x, y) should be any point inside the figure to fill).

    int graph_fill(graph_t *graph, unsigned int x, unsigned int y, unsigned int color, graph_fill_t type, graph_queue_t
    queue)
    

    The following graph_fill_t color fill methods are supported:

    • GRAPH_FILL_FLOOD - works like Windows paint bucket tool (floods homogeneous area, all pixels inside the polygon with color values same as the one at (x, y) flood origin point)

    • GRAPH_FILL_BOUND - fills the polygon until an edge of the same color as the fill color is found. It can’t fill the figure with a color different from the figure boundary

  • graph_print - Prints text pointed by the text argument. Font data should be passed to graph_font_t structure. The example is stored in gfx directory in phoenix-rtos-tests repository (font.h file). The remaining arguments are similar to those from the functions above.

    int graph_print(graph_t *graph, const graph_font_t *font, const char *text, unsigned int x, unsigned int y, unsigned
    char dx, unsigned char dy, unsigned int color, graph_queue_t queue)
    
  • graph_move - Moves data in the range specified by x, y, dx, dy arguments to the following point: (x + mx, y + my).

    int graph_move(graph_t *graph, unsigned int x, unsigned int y, unsigned int dx, unsigned int dy, int mx, int my,
    graph_queue_t queue)
    
  • graph_copy - Copies a bitmap pointed by the src argument into bitmap pointed by the dst argument. The area which is copied is limited by a rectangle with dx and dy dimensions. There should also be specified span arguments, which represent the total width of a source/destination bitmap multiplied by its color depth. When copying some part of a bitmap, src should point to the proper element, and the same applies to the destination buffer.

    int graph_copy(graph_t *graph, const void *src, void *dst, unsigned int dx, unsigned int dy, unsigned int srcspan,
    unsigned int dstspan, graph_queue_t queue)
    
  • graph_colorset - Sets a color palette used for 8-bit indexed color mode. A color map should be passed in cmap argument. The range of changing colors is set by passing first and last arguments. If a set color palette’s size is lower than a default one, the remaining colors are the same.

    int graph_colorset(graph_t *graph, const unsigned char *colors, unsigned char first, unsigned char last)
    
  • graph_colorget - Retrieves a color palette used in 8-bit indexed color mode. The retrieved color map from first to last element is passed to a buffer pointed by the colors argument.

    int graph_colorget(graph_t *graph, unsigned char *colors, unsigned char first, unsigned char last)
    
  • graph_cursorset - Sets cursor icon, amask (AND mask) and xmask (XOR mask) arguments determine the shape of the cursor. Default cursor shape is defined in cursor.h header file placed in gfx directory in phoenix-rtos-tests repository. There is a possibility to pass cursor colors - outline color (bg argument) and main color (fg argument). The following color format should be applied: 0xAARRGGBB, where A represents alpha, so when it’s set to 0xff 100% opacity is provided. Opacity isn’t supported for cirrus graphics adapter (default for ia32-generic-qemu target)

    int graph_cursorset(graph_t *graph, const unsigned char *amask, const unsigned char *xmask, unsigned int bg, unsigned
    int fg)
    
  • graph_cursorpos - Sets cursor position.

    int graph_cursorpos(graph_t *graph, unsigned int x, unsigned int y)
    
  • graph_cursorshow - Displays cursor.

    int graph_cursorshow(graph_t *graph)
    
  • graph_cursorhide - Hides cursor.

    int graph_cursorhide(graph_t *graph)
    
  • graph_commit - Commits frame buffer changes (flushes frame buffer) in the specified graphics adapter context.

    int graph_commit(graph_t *graph)
    
  • graph_trigger - Triggers next task from queue execution.

    int graph_trigger(graph_t *graph)
    
  • graph_stop - Disable adding new tasks to specified queue, for a graph context.

    int graph_stop(graph_t *graph, graph_queue_t queue)
    
  • graph_tasks - Returns number of tasks in queue.

    int graph_tasks(graph_t *graph, graph_queue_t queue)
    
  • graph_reset - Resets task queue.

    int graph_reset(graph_t *graph, graph_queue_t queue)
    
  • graph_vsync - Returns number of vertical synchronizations since the last call

    int graph_vsync(graph_t *graph)
    
  • graph_close - Closes a graph context, pointed by graph.

    void graph_close(graph_t *graph)
    
  • graph_done - Closes the graphics library.

    void graph_done(void
    

How to use the graphics library#

Few simple examples of libgraph functions usage. Default graphics adapter (cirrus) for ia32-generic-qemu running script is used, the default color depth is 4 bytes. Before calling mentioned functions the following initialization was applied:

#include <graph.h>
#include <time.h>

int main(void)
{
    graph_t graph;

    graph_init();
    graph_open(&graph, GRAPH_ANY, 0x2000);
    graph_mode(&graph, GRAPH_DEFMODE, GRAPH_DEFFREQ)
    /* clear screen */
    graph_rect(&graph, 0, 0, graph.width, graph.height, 0, GRAPH_QUEUE_HIGH)
}
  • Purple line

    graph_line(&graph, 0, 0, graph.width-50, graph.height/2-50, 2, 0x8282FF, GRAPH_QUEUE_HIGH);
    

    Image

  • Cyan filled rectangle

    graph_rect(&graph, 200, graph.height/4, graph.width/2, graph.height/2, 0x00FFFF, GRAPH_QUEUE_HIGH);
    

    Image

  • Examples of graph_fill() usage, both for GRAPH_FILL_FLOOD and GRAPH_FILL_BOUND option

    for (int i = 0; i < 2; i++) {
      graph_line(&graph, 50+i*350, 50, 0, 300, 2, 0x00000FF, GRAPH_QUEUE_HIGH);
      graph_line(&graph, 50+i*350, 50, 300, 0, 2, 0x00000FF, GRAPH_QUEUE_HIGH);
      graph_line(&graph, 350+i*350, 50, -300, 300, 2, 0x00000FF, GRAPH_QUEUE_HIGH);
      graph_line(&graph, 52+i*350, 52, 146, 146, 3, 0xFFFFFF, GRAPH_QUEUE_HIGH);
    }
    sleep(2);
    graph_fill(&graph, 52, 55, 0x00000FF, GRAPH_FILL_FLOOD, GRAPH_QUEUE_HIGH);
    graph_fill(&graph, 402, 55, 0x00000FF, GRAPH_FILL_BOUND, GRAPH_QUEUE_HIGH);
    

    Output sample

  • Printing text using libgraph

    Header file with font data in graph_font_t structure has to be included. The example of font.h is placed in gfx directory in phoenix-rtos-tests repository.

    graph_print(&graph, &font, "lorem ipsum", 300, 300, font.height, font.height, 0x00FF00, GRAPH_QUEUE_HIGH);
    

    Image

  • graph_move example

    graph_rect(&graph, 100, 100, 200, 200, 0xFFFF00, GRAPH_QUEUE_HIGH);
    sleep(3);
    graph_move(&graph, 100, 100, 100, 100, 300, 300, GRAPH_QUEUE_HIGH);
    

    Output sample

  • Copying raw bitmap into a screen

    Note that bitmaps are stored in memory in little endian format. So for 32-bit color depth first byte represents the blue color, next green, red, alpha, and so on.

    static const unsigned char greenSquareBitMap32[50][8] = {
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff },
    };
    
    graph_copy(&graph, greenSquareBitMap32, (void *)(uintptr_t)graph.data, 10, 10, graph.depth * 10, graph.depth * graph.width, GRAPH_QUEUE_HIGH);
    

    Image

  • Setting and getting a color palette

    The 8-bit color depth has to be applied, so for cirrus graphics adapter please use GRAPH_800x600x8 instead of GRAPH_DEFMODE. Note that in a default color palette when using the 8-bit mode, 0x01 refers to blue and for passed color map (cmap) it’s { 0xff, 0x00, 0x00} - red.

    unsigned char buff[2][3];
    static const unsigned char cmap[2][3] = {
    { 0xff, 0xff, 0xff},
    { 0xff, 0x00, 0x00}
    };
    
    graph_colorget(&graph, buff[0], 0, 1);
    graph_colorset(&graph, cmap[0], 0, 1);
    graph_rect(&graph, 200, 200, 200, 100, 0x01, GRAPH_QUEUE_HIGH);
    sleep(1);
    graph_colorset(&graph, buff[0], 0, 1);
    

    Output sample

  • Moving a cursor The cursor.h header file with cursor shape data (amask, xmask) has to be included.

    graph_cursorset(&graph, amask[0], xmask[0], 0xff0000ff, 0xffffffff);
    graph_cursorshow(&graph);
    for (int i = 0; i < 300; i++) {
      graph_cursorpos(&graph, i, i);
      usleep(10000);
    }
    graph_cursorhide(&graph);
    

    Output sample

Generating an image bitmap and displaying it using libgraph#

To create a raw bitmap, which can be displayed on the screen, it’s needed to use some graphic program, for example Gimp.

There are few steps to follow:

  • Open Gimp and paste the image

  • Set the desired resolution (image → scale image)

  • Depending on the desired format:

    • for 8-bit indexed color - change the file mode (image → mode → indexed) and export the file as raw binary data (file → export and choose raw image data format)

    • for other color depths - export the file to C source/header format (a dialog window pops up with additional options for color conversion)

  • At this point image binary data should be available (either as an array in .c or .h file or raw hex dump)

  • Custom image data formatting might be required

If the image bitmap is ready, there is a possibility to display it using graph_copy(). Please see the proper example in How to use libgraph chapter.

See also#

  1. Phoenix-RTOS core libraries

  2. Table of Contents