© Jan Newmarch 2017

Jan Newmarch, Raspberry Pi GPU Audio Video Programming , 10.1007/978-1-4842-2472-4_17

17. Basic OpenVG on the Raspberry Pi

Jan Newmarch

(1)Oakleigh, Victoria, Australia

OpenVG is an application programming interface (API) for hardware-accelerated two-dimensional vector and raster graphics. It has not apparently proven immensely popular, but I will need it for drawing text overlays on video images in a later chapter. This is a minimal chapter on OpenVG.

Building Programs

You can build the programs in this chapter using the following Makefile:

DMX_INC =  -I/opt/vc/include/ -I /opt/vc/include/interface/vmcs_host/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux
EGL_INC =
OPENVG_INC =
INCLUDES = $(DMX_INC) $(EGL_INC) $(OPENVG_INC)


CFLAGS= $(INCLUDES)
CPPFLAGS =


DMX_LIBS =  -L/opt/vc/lib/ -lbcm_host -lvcos -lvchiq_arm -lpthread
EGL_LIBS = -L/opt/vc/lib/ -lEGL -lGLESv2
OPENVG_LIBS = -lOpenVG


LDFLAGS =  $(DMX_LIBS) $(EGL_LIBS) $(OPENVG_LIBS)

SRC = ellipse.c simple_shape.c window.c image.c

all: ellipse simple_shape window image

Introduction

The OpenVG API is for hardware-accelerated two-dimensional vector and raster graphics. According to the OpenVG specification, it is intended to provide the following:

  • SVG and Adobe Flash viewers: OpenVG can provide the drawing functionality required for high-performance SVG document viewers that are conformant with version 1.2 of the SVG Tiny profile.

  • Portable mapping applications: OpenVG can provide dynamic features for map display that would be difficult or impossible to do with an SVG or Flash viewer alone, such as dynamic placement and sizing of street names and markers and efficient viewport culling.

  • E-book readers: The OpenVG API can provide fast rendering of readable text in Western, Asian, and other scripts.

  • Games: The OpenVG API is useful for defining sprites, backgrounds, and textures for use in both 2D and 3D games. It can provide two-dimensional overlays (for example, for maps or scores) on top of 3D content.

  • Scalable user interfaces: OpenVG can be used to render scalable user interfaces, particularly for applications that want to present users with a unique look and feel that is consistent across different screen resolutions.

  • Low-level graphics device interface: OpenVG can be used as a low-level graphics device interface. Other graphical toolkits, such as windowing systems, may be implemented above OpenVG.

OpenVG hasn’t taken off in general. Mesa is the main open source supplier for many graphical toolkits such as OpenGL. While it previously supported OpenVG, this has recently been removed, according to http://cgit.freedesktop.org/mesa/mesa/commit/?id=3acd7a34ab05b87521b74f626ec637e7fdcc6595 .

OpenVG API seems to have dwindled away. The code would still be interesting if we wanted to implement NV_path_rendering, but given the trend of the next-gen graphics APIs, it seems unlikely that this becomes ARB or core.

The “next-gen” graphics API this is referring to is probably Vulkan. On the other hand, there are now more than 10 million RPi computers out there capable of running OpenVG. Will that make a difference?

Dispmanx and EGL

Just like OpenGL, OpenVG depends on an EGL surface, which on the RPi is created using Dispmanx. The bare-minimum program is just like window.c from Chapter 5 with these two changes:

  • The API is bound to OpenVG rather than OpenGL ES with the following:

eglBindAPI(EGL_OPENVG_API);
  • Drawing is done using OpenVG calls, where the minimum to clear and draw against a white background is as follows:

    float c = 1.0;
    float clearColor[4] = {c, c, c, 0.5}; // white, semi transparent
    vgSetfv(VG_CLEAR_COLOR, 4, clearColor);
    vgClear(0, 0, 1920, 1080); // window_width(), window_height());


    // do lots of drawing in here

    vgFlush();

The program is window.c.

/*
 * code adapted from openGL-RPi-tutorial-master/encode_OGL/
 */


#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>


#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <VG/openvg.h>
#include <VG/vgu.h>


typedef struct
{
    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;
} EGL_STATE_T;


struct egl_manager {
    EGLNativeDisplayType xdpy;
    EGLNativeWindowType xwin;
    EGLNativePixmapType xpix;


    EGLDisplay dpy;
    EGLConfig conf;
    EGLContext ctx;


    EGLSurface win;
    EGLSurface pix;
    EGLSurface pbuf;
    EGLImageKHR image;


    EGLBoolean verbose;
    EGLint major, minor;
};


EGL_STATE_T state, *p_state = &state;

void init_egl(EGL_STATE_T *state)
{
    EGLint num_configs;
    EGLBoolean result;


    //bcm_host_init();

    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
            EGL_NONE
        };


    static const EGLint context_attributes[] =
        {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };


    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);


    // get an appropriate EGL frame buffer configuration
    result = eglChooseConfig(state->display, attribute_list, &state->config, 1, &num_configs);
    assert(EGL_FALSE != result);


    //result = eglBindAPI(EGL_OPENGL_ES_API);
    result = eglBindAPI(EGL_OPENVG_API);
    assert(EGL_FALSE != result);


    // create an EGL rendering context
    state->context = eglCreateContext(state->display,
                                      state->config, EGL_NO_CONTEXT,
                                      NULL);
                                      // breaks if we use this: context_attributes);
    assert(state->context!=EGL_NO_CONTEXT);
}


void init_dispmanx(EGL_DISPMANX_WINDOW_T *nativewindow) {  
    int32_t success = 0;  
    uint32_t screen_width;
    uint32_t screen_height;


    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;


    bcm_host_init();

    // create an EGL window surface
    success = graphics_get_display_size(0 /* LCD */,
                                        &screen_width,
                                        &screen_height);
    assert( success >= 0 );


    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = screen_width;
    dst_rect.height = screen_height;


    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = screen_width << 16;
    src_rect.height = screen_height << 16;        


    dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    dispman_update = vc_dispmanx_update_start( 0 );


    dispman_element =
        vc_dispmanx_element_add(dispman_update, dispman_display,
                                0/*layer*/, &dst_rect, 0/*src*/,
                                &src_rect, DISPMANX_PROTECTION_NONE,
                                0 /*alpha*/, 0/*clamp*/, 0/*transform*/);


    // Build an EGL_DISPMANX_WINDOW_T from the Dispmanx window
    nativewindow->element = dispman_element;
    nativewindow->width = screen_width;
    nativewindow->height = screen_height;
    vc_dispmanx_update_submit_sync(dispman_update);


    printf("Got a Dispmanx window ");
}


void egl_from_dispmanx(EGL_STATE_T *state,
                       EGL_DISPMANX_WINDOW_T *nativewindow) {
    EGLBoolean result;


    state->surface = eglCreateWindowSurface(state->display,
                                            state->config,
                                            nativewindow, NULL );
    assert(state->surface != EGL_NO_SURFACE);


    // connect the context to the surface
    result = eglMakeCurrent(state->display, state->surface, state->surface, state->context);
    assert(EGL_FALSE != result);
}


void draw(){
    float c = 1.0;
    float clearColor[4] = {c, c, c, 0.5};  // white, no transparency
    vgSetfv(VG_CLEAR_COLOR, 4, clearColor);


    vgClear(0, 0, 1920, 1080); // window_width(), window_height());

    // do lots of drawing in here

   vgFlush();
}


int
main(int argc, char *argv[])
{
    EGL_DISPMANX_WINDOW_T nativewindow;


    init_egl(p_state);
    init_dispmanx(&nativewindow);
    egl_from_dispmanx(p_state, &nativewindow);


    draw();
    eglSwapBuffers(p_state->display, p_state->surface);


    sleep(10);
    eglTerminate(p_state->display);


    exit(0);
}

This is run by window and just clears the screen and shows a white background.

OpenVG Pipeline

OpenVG, like any graphics system, has a multistage pipeline to process drawing. The OpenVG specification shows these for drawing a colored dashed line on a scene of images and various shapes.

A435266_1_En_17_Figa_HTML.jpg
Figure a.

To actually draw anything means specifying what might happen at each of these stages.

Drawing a Pink Triangle

In OpenVG you draw and fill paths. In the simplest cases, the paths are made up of lines. These can be solid or made up of various dots and dashes. They can be straight or curved in various ways. Where lines join, there are several ways the joins can be managed. The path can be filled with a color, and the lines themselves can be colored. These can all be set by calls to vgSetxxx, where xxx is used to tell the data type being set. For example, vgSetfv means the parameter is a vector of four float values. At the conclusion of setting a path, the path is drawn by vgDrawPath.

A pink dashed triangle is drawn with the following:

    VGint cap_style = VG_CAP_BUTT;
    VGint join_style = VG_JOIN_MITER;
    VGfloat color[4] = {1.0, 0.1, 1.0, 0.2}; // pink
    VGPaint fill;


    static const VGubyte cmds[] = {VG_MOVE_TO_ABS,
                                   VG_LINE_TO_ABS,
                                   VG_LINE_TO_ABS,
                                   VG_CLOSE_PATH
    };


    static const VGfloat coords[]   = {630, 630,
                                       902, 630,
                                       750, 924,
                                       630, 630
    };


    VGfloat dash_pattern[2] = { 20.f, 20.f };
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
                               VG_PATH_DATATYPE_F, 1, 0, 0, 0,
                               VG_PATH_CAPABILITY_APPEND_TO);
    vgAppendPathData(path, 4, cmds, coords);


    fill = vgCreatePaint();
    vgSetParameterfv(fill, VG_PAINT_COLOR, 4, color);
    vgSetPaint(fill, VG_FILL_PATH);
    vgSetPaint(fill, VG_STROKE_PATH);


    vgSetfv(VG_CLEAR_COLOR, 4, white_color);
    vgSetf(VG_STROKE_LINE_WIDTH, 20);
    vgSeti(VG_STROKE_CAP_STYLE, cap_style);
    vgSeti(VG_STROKE_JOIN_STYLE, join_style);
    vgSetfv(VG_STROKE_DASH_PATTERN, 2, dash_pattern);
    vgSetf(VG_STROKE_DASH_PHASE, 0.0f);


    vgDrawPath(path, VG_STROKE_PATH|VG_FILL_PATH);

The program is simple_shape.c.

/*
 * code adapted from openGL-RPi-tutorial-master/encode_OGL/
 */


#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>


#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <VG/openvg.h>
#include <VG/vgu.h>


typedef struct
{
    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;
} EGL_STATE_T;


struct egl_manager {
    EGLNativeDisplayType xdpy;
    EGLNativeWindowType xwin;
    EGLNativePixmapType xpix;


    EGLDisplay dpy;
    EGLConfig conf;
    EGLContext ctx;


    EGLSurface win;
    EGLSurface pix;
    EGLSurface pbuf;
    EGLImageKHR image;


    EGLBoolean verbose;
    EGLint major, minor;
};


EGL_STATE_T state, *p_state = &state;

void init_egl(EGL_STATE_T *state)
{
    EGLint num_configs;
    EGLBoolean result;


    //bcm_host_init();

    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
            EGL_NONE
        };


    static const EGLint context_attributes[] =
        {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };


    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);


    // get an appropriate EGL frame buffer configuration
    result = eglChooseConfig(state->display, attribute_list, &state->config, 1, &num_configs);
    assert(EGL_FALSE != result);


    //result = eglBindAPI(EGL_OPENGL_ES_API);
    result = eglBindAPI(EGL_OPENVG_API);
    assert(EGL_FALSE != result);


    // create an EGL rendering context
    state->context = eglCreateContext(state->display,
                                      state->config, EGL_NO_CONTEXT,
                                      NULL);
                                      // breaks if we use this: context_attributes);
    assert(state->context!=EGL_NO_CONTEXT);
}


void init_dispmanx(EGL_DISPMANX_WINDOW_T *nativewindow) {  
    int32_t success = 0;  
    uint32_t screen_width;
    uint32_t screen_height;


    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;


    bcm_host_init();

    // create an EGL window surface
    success = graphics_get_display_size(0 /* LCD */,
                                        &screen_width,
                                        &screen_height);
    assert( success >= 0 );


    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = screen_width;
    dst_rect.height = screen_height;


    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = screen_width << 16;
    src_rect.height = screen_height << 16;        


    dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    dispman_update = vc_dispmanx_update_start( 0 );


    dispman_element =
        vc_dispmanx_element_add(dispman_update, dispman_display,
                                0/*layer*/, &dst_rect, 0/*src*/,
                                &src_rect, DISPMANX_PROTECTION_NONE,
                                0 /*alpha*/, 0/*clamp*/, 0/*transform*/);


    // Build an EGL_DISPMANX_WINDOW_T from the Dispmanx window
    nativewindow->element = dispman_element;
    nativewindow->width = screen_width;
    nativewindow->height = screen_height;
    vc_dispmanx_update_submit_sync(dispman_update);


    printf("Got a Dispmanx window ");
}


void egl_from_dispmanx(EGL_STATE_T *state,
                       EGL_DISPMANX_WINDOW_T *nativewindow) {
    EGLBoolean result;


    state->surface = eglCreateWindowSurface(state->display,
                                            state->config,
                                            nativewindow, NULL );
    assert(state->surface != EGL_NO_SURFACE);


    // connect the context to the surface
    result = eglMakeCurrent(state->display, state->surface, state->surface, state->context);
    assert(EGL_FALSE != result);
}


void simple_shape() {
    VGint cap_style = VG_CAP_BUTT;
    VGint join_style = VG_JOIN_MITER;
    VGfloat color[4] = {1.0, 0.1, 1.0, 0.2};
    VGfloat white_color[4] = {1.0, 1.0, 1.0, 0.0}; //1.0};
    VGPaint fill;


    static const VGubyte cmds[] = {VG_MOVE_TO_ABS,
                                   VG_LINE_TO_ABS,
                                   VG_LINE_TO_ABS,
                                   VG_CLOSE_PATH
    };


    static const VGfloat coords[]   = {630, 630,
                                       902, 630,
                                       750, 924
    };


    VGfloat dash_pattern[2] = { 20.f, 20.f };
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
                               VG_PATH_DATATYPE_F, 1, 0, 0, 0,
                               VG_PATH_CAPABILITY_APPEND_TO);
    vgAppendPathData(path, 4, cmds, coords);


    fill = vgCreatePaint();
    vgSetParameterfv(fill, VG_PAINT_COLOR, 4, color);
    vgSetPaint(fill, VG_FILL_PATH);
    vgSetPaint(fill, VG_STROKE_PATH);


    vgSetfv(VG_CLEAR_COLOR, 4, white_color);
    vgSetf(VG_STROKE_LINE_WIDTH, 20);
    vgSeti(VG_STROKE_CAP_STYLE, cap_style);
    vgSeti(VG_STROKE_JOIN_STYLE, join_style);
    vgSetfv(VG_STROKE_DASH_PATTERN, 2, dash_pattern);
    vgSetf(VG_STROKE_DASH_PHASE, 0.0f);


    vgDrawPath(path, VG_STROKE_PATH|VG_FILL_PATH);
}


void draw(){
    float c = 1.0;
    float clearColor[4] = {c, c, c, c};  // white, no transparency
    vgSetfv(VG_CLEAR_COLOR, 4, clearColor);


    vgClear(0, 0, 1920, 1080);

    simple_shape();

    vgFlush();
}


int
main(int argc, char *argv[])
{
    EGL_DISPMANX_WINDOW_T nativewindow;


    init_egl(p_state);
    init_dispmanx(&nativewindow);
    egl_from_dispmanx(p_state, &nativewindow);


    draw();
    eglSwapBuffers(p_state->display, p_state->surface);


    sleep(100);
    eglTerminate(p_state->display);


    exit(0);
}

This is run with simple_shape.

The figure drawn is as follows (image captured by raspi2png):

A435266_1_En_17_Figb_HTML.jpg
Figure b.

Drawing Standard Shapes

The VGU utility library is an optional library that provides a set of common shapes such as polygons, rectangles, and ellipses. The shapes still need to have the stroke and fill parameters set, and so on. The program ellipse.c draws a purple rectangle that decreases in height on each display.

#include <stdio.h>
#include <stdlib.h>
//#include <sys/stat.h>
#include <signal.h>


#include <assert.h>
#include <vc_dispmanx.h>
#include <bcm_host.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <VG/openvg.h>
#include <VG/vgu.h>


typedef struct
{
    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;
    EGLConfig config;
} EGL_STATE_T;


EGL_STATE_T state, *p_state = &state;

void init_egl(EGL_STATE_T *state)
{
    EGLint num_configs;
    EGLBoolean result;


    //bcm_host_init();

    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_SAMPLES, 1,
            EGL_NONE
        };


    static const EGLint context_attributes[] =
        {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };


    // get an EGL display connection
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);


    // initialize the EGL display connection
    result = eglInitialize(state->display, NULL, NULL);


    // get an appropriate EGL frame buffer configuration
    result = eglChooseConfig(state->display, attribute_list, &state->config, 1, &num_configs);
    assert(EGL_FALSE != result);


    // Choose the OpenVG API
    result = eglBindAPI(EGL_OPENVG_API);
    assert(EGL_FALSE != result);


    // create an EGL rendering context
    state->context = eglCreateContext(state->display,
                                      state->config,
                                      NULL, // EGL_NO_CONTEXT,
                                      NULL);
                                      // breaks if we use this: context_attributes);
    assert(state->context!=EGL_NO_CONTEXT);
}


void init_dispmanx(EGL_DISPMANX_WINDOW_T *nativewindow) {  
    int32_t success = 0;  
    uint32_t screen_width;
    uint32_t screen_height;


    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;


    bcm_host_init();

    // create an EGL window surface
    success = graphics_get_display_size(0 /* LCD */,
                                        &screen_width,
                                        &screen_height);
    assert( success >= 0 );


    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = screen_width;
    dst_rect.height = screen_height;


    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = screen_width << 16;
    src_rect.height = screen_height << 16;        


    dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    dispman_update = vc_dispmanx_update_start( 0 );


    dispman_element =
        vc_dispmanx_element_add(dispman_update, dispman_display,
                                0 /*layer*/, &dst_rect, 0 /*src*/,
                                &src_rect, DISPMANX_PROTECTION_NONE,
                                0 /*alpha*/, 0 /*clamp*/, 0 /*transform*/);


    // Build an EGL_DISPMANX_WINDOW_T from the Dispmanx window
    nativewindow->element = dispman_element;
    nativewindow->width = screen_width;
    nativewindow->height = screen_height;
    vc_dispmanx_update_submit_sync(dispman_update);


    printf("Got a Dispmanx window ");
}


void egl_from_dispmanx(EGL_STATE_T *state,
                       EGL_DISPMANX_WINDOW_T *nativewindow) {
    EGLBoolean result;
    static const EGLint attribute_list[] =
        {
            EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER,
            EGL_NONE
        };


    state->surface = eglCreateWindowSurface(state->display,
                                            state->config,
                                            nativewindow,
                                            NULL );
                                            //attribute_list);
    assert(state->surface != EGL_NO_SURFACE);


    // connect the context to the surface
    result = eglMakeCurrent(state->display, state->surface, state->surface, state->context);
    assert(EGL_FALSE != result);
}


// setfill sets the fill color
void setfill(float color[4]) {
    VGPaint fillPaint = vgCreatePaint();
    vgSetParameteri(fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
    vgSetParameterfv(fillPaint, VG_PAINT_COLOR, 4, color);
    vgSetPaint(fillPaint, VG_FILL_PATH);
    vgDestroyPaint(fillPaint);
}


// setstroke sets the stroke color and width
void setstroke(float color[4], float width) {
    VGPaint strokePaint = vgCreatePaint();
    vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
    vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color);
    vgSetPaint(strokePaint, VG_STROKE_PATH);
    vgSetf(VG_STROKE_LINE_WIDTH, width);
    vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT);
    vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_MITER);
    vgDestroyPaint(strokePaint);
}


// Ellipse makes an ellipse at the specified location and dimensions, applying style
void Ellipse(float x, float y, float w, float h, float sw, float fill[4], float stroke[4]) {
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
    vguEllipse(path, x, y, w, h);
    setfill(fill);
    setstroke(stroke, sw);
    vgDrawPath(path, VG_FILL_PATH | VG_STROKE_PATH);
    vgDestroyPath(path);
}


void draw() {
    EGL_DISPMANX_WINDOW_T nativewindow;


    init_egl(p_state);
    init_dispmanx(&nativewindow);


    egl_from_dispmanx(p_state, &nativewindow);

    vgClear(0, 0, 1920, 1080);

    VGfloat color[4] = {0.4, 0.1, 1.0, 1.0}; // purple
    float c = 1.0;    
    float clearColor[4] = {c, c, c, c}; // white non-transparent
    vgSetfv(VG_CLEAR_COLOR, 4, clearColor);
    vgClear(0, 0, 1920, 1080);


    vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
    static float ht = 200;
    while (1) {
        vgClear(0, 0, 1920, 1080);
        Ellipse(1920/2, 1080/2, 400, ht--, 0, color, color);
        if (ht <= 0) {
            ht = 200;
        }
        eglSwapBuffers(p_state->display, p_state->surface);
    }
    assert(vgGetError() == VG_NO_ERROR);


    vgFlush();

    eglSwapBuffers(p_state->display, p_state->surface);
}


void sig_handler(int sig) {
    eglTerminate(p_state->display);
    exit(1);
}


int main(int argc, char** argv) {
    signal(SIGINT, sig_handler);


    draw();

    exit(0);
}

This is run by ellipse.  

It looks like this:

A435266_1_En_17_Figc_HTML.jpg
Figure c.

Images

OpenVG has the type VGImage to represent images. An image has a format such as RGB, and each pixel takes a number of bytes. For example, the format VG_sXRGB_8888 takes up 4 bytes. Each image consists of rows of pixels, and some images round up the number of bytes per row to, for example, a multiple of 32 bytes. This rounded-up number is called the stride. For example, 100 pixels in VG_sXRGB_8888 format would take up 400 bytes, but this might be rounded up to a stride of 416 bytes.

Images can come from a number of sources .

  • An external file may hold an image in, for example, PNG format. This will have to be loaded into memory and decoded into the correct format for a VGImage.

  • A program may generate an image in memory in the format required for a VGImage.

  • An image may be created as an off-screen buffer, and VG drawing calls can be made into it.

The resulting image can be drawn to the current OpenVG surface, either in total or in part.

Creating a VGImage from Memory Data

You can create a grayscale image where each pixel takes 1 byte by creating an image of type VG_sL_8. You can choose the size as 256 × 256. The data for this will then be stored in a 256 × 256 unsigned char array. For simplicity, you just fill it with increasing values (overflowing when necessary).

    VGImage img;
    img = vgCreateImage(VG_sL_8,
                        256, 256,
                        VG_IMAGE_QUALITY_NONANTIALIASED);
    if (img == VG_INVALID_HANDLE) {
        fprintf(stderr, "Can't create simple image ");
        fprintf(stderr, "Error code %x ", vgGetError());
        exit(2);
    }
    unsigned char val;
    unsigned char data[256*256];
    int n;


    for (n = 0; n < 256*256; n++) {
        val = (unsigned char) n;
        data[n] = val;
    }

To load an array into an image , you use the function vgImageSubData. This takes the image, the data array, the image format, the stride, and the image coordinates.

    vgImageSubData(img, data,
                   256, VG_sL_8,
                   0, 0, 256, 256);

Drawing an Image

To draw the image, you must set up an “image-user-to-surface” transformation . This requires setting up a matrix to transform the image to user coordinates, such as a projective transformation. You just use an identity matrix. The resulting image may be subjected to operations such as translation to a different origin. The image can then be drawn as follows:

    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadIdentity();
    vgTranslate(200, 30);


    vgDrawImage(img);

A function to create and draw an image is then as follows:

void simple_image() {
    VGImage img;
    img = vgCreateImage(VG_sL_8,
                        256, 256,
                        VG_IMAGE_QUALITY_NONANTIALIASED);
    if (img == VG_INVALID_HANDLE) {
        fprintf(stderr, "Can't create simple image ");
        fprintf(stderr, "Error code %x ", vgGetError());
        exit(2);
    }
    unsigned char val;
    unsigned char data[256*256];
    int n;


    for (n = 0; n < 256*256; n++) {
        val = (unsigned char) n;
        data[n] = val;
    }
    vgImageSubData(img, data,
                   256, VG_sL_8,
                   0, 0, 256, 256);
    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadIdentity();
    vgTranslate(200, 30);


    vgDrawImage(img);

    vgDestroyImage(img);
}

The image looks like this:

A435266_1_En_17_Figd_HTML.jpg
Figure d.

Drawing an Image Using OpenVG Calls

OpenVG can also be used to draw into an off-screen buffer image . This is much more complicated because an image has to be created that is compatible with the display, and then an off-screen surface using a buffer has to be created from this image. The surface must then be made the current drawing surface, and then OpenVG calls can be made to it. The original drawing surface must be restored, and then the image can be drawn to it, as shown earlier.

In setting up the EGL surfaces, you had to create an EGL context . This used the EGLDisplay and also an EGLConfig. The configuration used a list of attributes that typically specify the RGBA characteristics required. This is done by passing to eglChooseConfig an attribute list such as the following:

 EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            ...
            EGL_NONE
        };

By experimenting I have found that for the RPi, both RGBA_8888 and RGB_565 but not much else seems to work for a window configuration. I couldn’t get a Luminance setting (grayscale) to work.

An off-screen image seems to be even fussier, and I have only been able to get a setting of RGBA_8888 to work. You choose a configuration and build context for the off-screen image using a pbuffer, as follows:

    EGLContext context;

    static const EGLint attribute_list[] =
        {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
            EGL_NONE
        };
    EGLConfig config;


    drawn_image = vgCreateImage(VG_sRGBA_8888,
                                 256, 256,
                                 VG_IMAGE_QUALITY_NONANTIALIASED);
    if (drawn_image == VG_INVALID_HANDLE) {
        fprintf(stderr, "Can't create drawn image ");
        fprintf(stderr, "Error code %x ", vgGetError());
        exit(2);
    }
    vgClearImage(drawn_image, 0, 0, 256, 256);


    int result, num_configs;
    result = eglChooseConfig(p_state->display,
                             attribute_list,
                             &config,
                             1,
                             &num_configs);
    assert(EGL_FALSE != result);
    context = eglCreateContext(p_state->display,
                               config, EGL_NO_CONTEXT,
                               NULL);

The next step is to create an off-screen surface using the call eglCreatePbufferFromClientBuffer. This takes the image coerced to type EGLClientBuffer.

    img_surface = eglCreatePbufferFromClientBuffer(p_state->display,
                                                   EGL_OPENVG_IMAGE,
                                                   (EGLClientBuffer) drawn_image,
                                                   config,
                                                   NULL);

This needs to be made into the current surface with the following:

    eglMakeCurrent(p_state->display, img_surface, img_surface,
                   context);

OpenVG calls can then be made: create a path , set objects into the path, set fill parameters, and so on, and then finally draw the path.

    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
                               VG_PATH_DATATYPE_F, 1.0f,
                               0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);


    float height = 40.0;
    float arcW = 15.0;
    float arcH = 15.0;


    vguRoundRect(path, 28, 10,
                 200, 60, arcW, arcH);
    vguEllipse(path, 128, 200, 60, 40);


    setfill(drawn_color);
    vgDrawPath(path, VG_FILL_PATH);

The final steps are to reset the drawing surface and then draw the image onto the surface .

    eglMakeCurrent(p_state->display, p_state->surface,
                   p_state->surface,
                   p_state->context);


    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadIdentity();
    vgTranslate(800, 300);


    vgDrawImage(drawn_image);

    vgDestroyImage(drawn_image);

The final function to do all this is as follows:

void drawn_image() {
    EGLSurface img_surface;
    VGImage drawn_image;
    EGLContext context;


    static const EGLint attribute_list[] =
        {
            //EGL_COLOR_BUFFER_TYPE, EGL_LUMINANCE_BUFFER,
            // EGL_LUMINANCE_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
            EGL_NONE
        };
    EGLConfig config;


    drawn_image = vgCreateImage(VG_sRGBA_8888,
                                 256, 256,
                                 VG_IMAGE_QUALITY_NONANTIALIASED);
    if (drawn_image == VG_INVALID_HANDLE) {
        fprintf(stderr, "Can't create drawn image ");
        fprintf(stderr, "Error code %x ", vgGetError());
        exit(2);
    }
    vgClearImage(drawn_image, 0, 0, 256, 256);


    int result, num_configs;
    result = eglChooseConfig(p_state->display,
                             attribute_list,
                             &config,
                             1,
                             &num_configs);
    assert(EGL_FALSE != result);gt;display,
    context = eglCreateContext(p_state->display,
                               config, EGL_NO_CONTEXT,
                               NULL);


    img_surface = eglCreatePbufferFromClientBuffer(p_state->display,
                                                   EGL_OPENVG_IMAGE,
                                                   (EGLClientBuffer) drawn_image,
                                                   config,
                                                   NULL);
    if (img_surface == EGL_NO_SURFACE) {
        fprintf(stderr, "Couldn't create pbuffer ");
        fprintf(stderr, "Error code %x ", eglGetError());
        exit(1);
    }
    eglMakeCurrent(p_state->display, img_surface, img_surface,
                   context); //p_state->context);


    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
                               VG_PATH_DATATYPE_F, 1.0f,
                               0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);


    float height = 40.0;
    float arcW = 15.0;
    float arcH = 15.0;


    vguRoundRect(path, 28, 10,
                 200, 60, arcW, arcH);
    vguEllipse(path, 128, 200, 60, 40);


    setfill(drawn_color);
    vgDrawPath(path, VG_FILL_PATH);


    eglMakeCurrent(p_state->display, p_state->surface,
                   p_state->surface,
                   p_state->context);


    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadIdentity();
    vgTranslate(800, 300);


    vgDrawImage(drawn_image);

    vgDestroyImage(drawn_image);
}

The image looks like this:

A435266_1_En_17_Fige_HTML.jpg
Figure e.

The complete program to draw the simple images is image.c.

Conclusion

This chapter showed you how to draw OpenVG surfaces on a Dispmanx window and introduced you to the OpenVG API.

Resources

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset