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 adaptercirrus
- Cirrus Logic GD5446 SuperVGA adapter
libgraph apps#
Examples of applications, which use graphics library (ia32-generic-qemu
target architecture).
voxeldemo
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
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
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 thegraph_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 thegraph
argument and the graphicsadapter
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 adapterGRAPH_VGA
- generic VGA graphics adapterGRAPH_CIRRUS
- Cirrus Logic graphics adapterGRAPH_ANY
- an available graphics adapter is chosen
The
mem
argument specifies the total graphics tasks queue size. The bigger themem
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 inadapter
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, andmode
should be chosen from thegraph_mode_t
enum, and placed in thegraph.h
header.The common graphics modes are presented below:
GRAPH_DEFMODE
- default graphics modeGRAPH_ON
- display enabled modeGRAPH_OFF
- display disabled modeGRAPH_STANDBY
- display standby modeGRAPH_SUSPEND
- display suspend modeGRAPH_800x600x8
- 800x600 resolution, 8-bit indexed color modeGRAPH_320x200x16
- 320x200 resolution, 16-bit color modeGRAPH_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 rateGRAPH_24Hz
- 24Hz refresh rateGRAPH_60Hz
- 60Hz refresh rateGRAPH_120Hz
- 120Hz refresh rateGRAPH_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
- initializedgraph_t
structurex
- start point x position, where 0 is the left edge of the screeny
- start point y position, where 0 is the upper edge of the screendx
- line length in x-axis in pixelsdy
- line length in y-axis in pixelsstroke
- line thickness in pixelscolor
- line colorFor
8-bit
indexed color default VGA color palette is presented below (e.g.0x0B
represents cyan).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 by0xRRGGBB
(e.g.0x0000FF
represents blue)For
32-bit
color depth - it’s the following format in hex:0xAARRGGBB
, whereA
represents alpha. WithAA
set to0xFF
opacity equals 100%, but by default transparency isn’t enabled, so there can be any value.
queue
- graphics task queue, should be chosen from thegraph_queue_t
enum, where are following options:GRAPH_QUEUE_HIGH
- high priority queueGRAPH_QUEUE_LOW
- low priority queueGRAPH_QUEUE_BOTH
- any queueGRAPH_QUEUE_DEFAULT
- default queue
graph_rect
- Draws a rectangle. Arguments are similar to those ingraph_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 thecolor
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 thetext
argument. Font data should be passed tograph_font_t
structure. The example is stored ingfx
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 byx
,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 thesrc
argument into bitmap pointed by thedst
argument. The area which is copied is limited by a rectangle withdx
anddy
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 incmap
argument. The range of changing colors is set by passingfirst
andlast
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 fromfirst
tolast
element is passed to a buffer pointed by thecolors
argument.int graph_colorget(graph_t *graph, unsigned char *colors, unsigned char first, unsigned char last)
graph_cursorset
- Sets cursor icon,amask
(AND
mask) andxmask
(XOR
mask) arguments determine the shape of the cursor. Default cursor shape is defined incursor.h
header file placed ingfx
directory inphoenix-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
, whereA
represents alpha, so when it’s set to0xff
100% opacity is provided. Opacity isn’t supported for cirrus graphics adapter (default foria32-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 agraph
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 callint graph_vsync(graph_t *graph)
graph_close
- Closes a graph context, pointed bygraph
.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);
Cyan filled rectangle
graph_rect(&graph, 200, graph.height/4, graph.width/2, graph.height/2, 0x00FFFF, GRAPH_QUEUE_HIGH);
Examples of
graph_fill()
usage, both forGRAPH_FILL_FLOOD
andGRAPH_FILL_BOUND
optionfor (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);
Printing text using libgraph
Header file with font data in
graph_font_t
structure has to be included. The example offont.h
is placed ingfx
directory in phoenix-rtos-tests repository.graph_print(&graph, &font, "lorem ipsum", 300, 300, font.height, font.height, 0x00FF00, GRAPH_QUEUE_HIGH);
graph_move
examplegraph_rect(&graph, 100, 100, 200, 200, 0xFFFF00, GRAPH_QUEUE_HIGH); sleep(3); graph_move(&graph, 100, 100, 100, 100, 300, 300, GRAPH_QUEUE_HIGH);
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);
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 ofGRAPH_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);
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);
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 imageSet 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.