Canvas

The Canvas widget provides 2D graphics rendering capabilities based on NanoVG, supporting software-accelerated rendering and multiple image output formats.



Warning

Before usage, ensure: Sufficient frame buffer memory.

Component Overview

GUI Canvas is a 2D drawing component based on NanoVG, providing:

  • Basic shape drawing (rectangles, circles, arcs, etc.)

  • Support for RGBA/RGB565/PNG/JPG output formats

  • NanoVG software-accelerated rendering (via AGGE backend)

  • Direct buffer output functionality

  • Blank buffer initialization support

Core Features

Creation & Initialization

  1. Use gui_canvas_create to create a canvas widget, and bind a drawing callback with gui_canvas_set_canvas_cb.

    void test_circle_drawing(void)
    {
        gui_canvas_t *canvas = gui_canvas_create(gui_obj_get_root(), "test_circle", NULL, 0, 0, 300, 200);
        gui_canvas_set_canvas_cb(canvas, draw_circle_callback);
    }
    
  2. Inside the drawing callback, draw graphics through the NanoVG interface; the callback is invoked whenever the canvas needs refreshing.

    static void draw_circle_callback(gui_canvas_t *canvas)
    {
        nvgBeginPath(canvas->vg);
        nvgCircle(canvas->vg, 100, 100, 50);
        nvgFillColor(canvas->vg, nvgRGBA(255, 255, 0, 255));
        nvgFill(canvas->vg);
    
        nvgBeginPath(canvas->vg);
        nvgEllipse(canvas->vg, 200, 100, 80, 40);
        nvgStrokeColor(canvas->vg, nvgRGBA(0, 0, 255, 255));
        nvgStrokeWidth(canvas->vg, 2.0f);
        nvgStroke(canvas->vg);
    }
    

You can manually trigger a redraw by setting canvas->render = 1; the system will call the drawing callback on the next frame.

Image Output

Use gui_canvas_render_to_image_buffer to render the canvas content into a pre-allocated buffer, which is suitable for scenarios involving repeated rendering or limited memory. The buffer size is computed as width x height x bytes-per-pixel plus the size of the gui_rgb_data_head_t header, as shown below:

int image_h = 300;
int image_w = SCREEN_WIDTH;
int pixel_bytes = 4;
buffer_size = image_h * image_w * pixel_bytes + sizeof(gui_rgb_data_head_t);
if (img_data == NULL)
{
    img_data = gui_lower_malloc(buffer_size);
}
memset(img_data, 0, buffer_size);
gui_canvas_render_to_image_buffer(GUI_CANVAS_OUTPUT_RGBA, 0, image_w, image_h, draw_heartrate_graph,
                                  img_data);

Example Code

Complete card view usage example:

Enable the Kconfig option via menuconfig to run this example:

cd win32_sim
menuconfig ../Kconfig.gui

Select canvas demo (CONFIG_REALTEK_BUILD_REAL_CANVAS), then save to win32_sim/.config.

#include <math.h>
#include "guidef.h"
#include "gui_img.h"
#include "gui_obj.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "gui_server.h"
#include "gui_components_init.h"
#include "gui_canvas.h"

/* canvas draw callback start */
static void draw_circle_callback(gui_canvas_t *canvas)
{
    nvgBeginPath(canvas->vg);
    nvgCircle(canvas->vg, 100, 100, 50);
    nvgFillColor(canvas->vg, nvgRGBA(255, 255, 0, 255));
    nvgFill(canvas->vg);

    nvgBeginPath(canvas->vg);
    nvgEllipse(canvas->vg, 200, 100, 80, 40);
    nvgStrokeColor(canvas->vg, nvgRGBA(0, 0, 255, 255));
    nvgStrokeWidth(canvas->vg, 2.0f);
    nvgStroke(canvas->vg);
}
/* canvas draw callback end */

/* canvas create start */
void test_circle_drawing(void)
{
    gui_canvas_t *canvas = gui_canvas_create(gui_obj_get_root(), "test_circle", NULL, 0, 0, 300, 200);
    gui_canvas_set_canvas_cb(canvas, draw_circle_callback);
}
/* canvas create end */
static void canvas_rect_cb(gui_canvas_t *canvas)
{
    NVGcontext *vg = canvas->vg;
    nvgBeginPath(vg);
    nvgRoundedRect(vg, 150, 150, 200, 180, 20);

    nvgStrokeWidth(vg, 8.0f);
    nvgStrokeColor(vg, nvgRGB(255, 0, 0));
    nvgStroke(vg);

    NVGpaint gradient = nvgLinearGradient(vg, 150, 150, 350, 330, nvgRGB(255, 0, 0), nvgRGBA(0, 255, 0,
                                          255));
    nvgFillPaint(vg, gradient);
    nvgFill(vg);
}
void test_rect_drawing(void)
{

    gui_canvas_t *canvas = gui_canvas_create(gui_obj_get_root(), "canvas", 0, 0, 0, 480, 480);

    gui_canvas_set_canvas_cb(canvas, canvas_rect_cb);
}
static void arc_cb(gui_canvas_t *canvas)
{
    // static float  progress;
    // progress += 0.01f;
    nvgArc(canvas->vg, 480 / 2, 480 / 2, 150, 0, -6.14f, NVG_CCW);
    nvgStrokeWidth(canvas->vg, 20);
    nvgStrokeColor(canvas->vg, nvgRGB(178, 34, 34));
    nvgStroke(canvas->vg);
}

void test_arc_drawing(void)
{
    gui_canvas_t *canvas = gui_canvas_create(gui_obj_get_root(), "test_arc", NULL, 0, 0, 480, 480);
    gui_canvas_set_canvas_cb(canvas, arc_cb);
    canvas->render = 1;
}

static void draw_pre_canvas_cb(NVGcontext *vg)
{
    /* Yellow filled circle */
    nvgBeginPath(vg);
    nvgCircle(vg, 100, 100, 50);
    nvgFillColor(vg, nvgRGBA(255, 255, 0, 255));
    nvgFill(vg);

    /* Blue ellipse outline */
    nvgBeginPath(vg);
    nvgEllipse(vg, 200, 100, 80, 40);
    nvgStrokeColor(vg, nvgRGBA(0, 0, 255, 255));
    nvgStrokeWidth(vg, 2.0f);
    nvgStroke(vg);

    /* Rounded rect: gradient fill + red stroke share the same path */
    nvgBeginPath(vg);
    nvgRoundedRect(vg, 150, 150, 200, 180, 20);
    {
        NVGpaint gradient = nvgLinearGradient(vg, 150, 150, 350, 330,
                                              nvgRGB(255, 0, 0),
                                              nvgRGBA(0, 255, 0, 255));
        nvgFillPaint(vg, gradient);
        nvgFill(vg);
    }
    nvgStrokeWidth(vg, 8.0f);
    nvgStrokeColor(vg, nvgRGB(255, 0, 0));
    nvgStroke(vg);

    /* Dark red arc */
    nvgBeginPath(vg);
    nvgArc(vg, 480 / 2, 480 / 2, 150, 0, -6.14f, NVG_CCW);
    nvgStrokeWidth(vg, 20);
    nvgStrokeColor(vg, nvgRGB(178, 34, 34));
    nvgStroke(vg);
}

void test_pre_buffer_drawing(void)
{
    gui_dispdev_t *dc = gui_get_dc();
    dc->cache_need_clean = true;
    uint32_t image_w = dc->screen_width;
    uint32_t image_h = dc->screen_height;
    uint8_t *image_buffer = gui_lower_malloc(image_w * image_h * 2 + sizeof(gui_rgb_data_head_t));
    memset(image_buffer, 0, image_w * image_h * 2 + sizeof(gui_rgb_data_head_t));


#ifdef _HONEYGUI_SIMULATOR_
    gui_canvas_render_to_image_buffer(GUI_CANVAS_OUTPUT_RGB565, 0, image_w, image_h, draw_pre_canvas_cb,
                                      image_buffer);
#else
    extern uint32_t sys_timestamp_get_us(void);
    uint32_t start_time = sys_timestamp_get_us();
    gui_canvas_render_to_image_buffer(GUI_CANVAS_OUTPUT_RGB565, 0, image_w, image_h, draw_pre_canvas_cb,
                                      image_buffer);
    uint32_t end_time = sys_timestamp_get_us();
    gui_log("canvas render time: %d us\n", end_time - start_time);

    /* CRC32 over pixel bytes only (skip the gui_rgb_data_head_t prefix).
     * Uses the reflected polynomial 0xEDB88320 (same as zlib/PNG). Small
     * inline implementation -- table would cost 1 KB flash and is unnecessary
     * for a one-shot benchmark. Prints in hex so it's easy to eyeball. */
    const uint8_t *px = image_buffer + sizeof(gui_rgb_data_head_t);
    uint32_t n_bytes  = image_w * image_h * 2;
    uint32_t crc      = 0xFFFFFFFFu;
    for (uint32_t i = 0; i < n_bytes; ++i)
    {
        crc ^= px[i];
        for (int b = 0; b < 8; ++b)
        {
            uint32_t mask = -(int32_t)(crc & 1u);
            crc = (crc >> 1) ^ (0xEDB88320u & mask);
        }
    }
    crc ^= 0xFFFFFFFFu;
    gui_log("canvas fb crc32: 0x%08x  (%u bytes)\n", crc, n_bytes);
#endif

    gui_img_create_from_mem(gui_obj_get_root(), "canvas_img", image_buffer, 0, 0, 0,
                            0);
}


static int app_init(void)
{
    // test_rect_drawing();
    // test_arc_drawing();
    // test_circle_drawing();
    test_pre_buffer_drawing();
    return 0;
}

GUI_INIT_APP_EXPORT(app_init);

API

Defines

GUI_CANVAS_OUTPUT_PNG 1
GUI_CANVAS_OUTPUT_JPG 2
GUI_CANVAS_OUTPUT_RGBA 3
GUI_CANVAS_OUTPUT_RGB565 4
GUI_CANVAS_OUTPUT_RGBA_NOMIX 5

Typedefs

typedef void (*gui_canvas_render_function)(NVGcontext *vg)

Functions

gui_canvas_t *gui_canvas_create(void *parent, const char *name, void *addr, int16_t x, int16_t y, int16_t w, int16_t h)

Create a canvas widget used to draw graphics in nanovg.

Parameters:
  • parent -- Father widget nested in.

  • name -- Canvas widget's name.

  • addr -- Address of the canvas.

  • x -- X-axis coordinate relative to parent widget.

  • y -- Y-axis coordinate relative to parent widget.

  • w -- Width of the canvas.

  • h -- Height of the canvas.

Returns:

Pointer to the created canvas widget.

void gui_canvas_set_canvas_cb(gui_canvas_t *this_widget, void (*cb)(gui_canvas_t *this_widget))

Set the callback function for drawing specific shapes.

Parameters:
  • this_widget -- Pointer to the canvas widget.

  • cb -- Callback function for drawing specific shapes.

void *gui_canvas_render_to_image_buffer(int format, bool compression, int image_width, int image_height, gui_canvas_render_function renderer, uint8_t *target_buffer)

Render a graphical canvas to a specified buffer in a chosen image format.

Utilizes the provided renderer function to generate graphical content within a canvas, subsequently outputting the rendered image into the designated target buffer in accordance with the specified format. Optional compression is available based on the format.

Parameters:
  • format -- Format of the output image. It should be specified as one of the following options:

    • GUI_CANVAS_OUTPUT_PNG for PNG format.

    • GUI_CANVAS_OUTPUT_JPG for JPG format.

    • GUI_CANVAS_OUTPUT_RGBA for RGBA format.

    • GUI_CANVAS_OUTPUT_RGB565 for RGB565 format.

  • compression -- Boolean flag indicating whether to compress the output image data. Note: Compression is only applicable for RGBA/RGB565 formats.

  • image_width -- Width of the output image, specified in pixels.

  • image_height -- Height of the output image, specified in pixels.

  • renderer -- Function pointer to the rendering callback function, responsible for executing drawing operations within the rendering context (NVGcontext).

  • target_buffer -- Pointer to the pre-allocated buffer designated for storing the rendered image data. Ensure the buffer size is adequate to hold the image data in the specified format.

Returns:

Pointer to the rendered image buffer.

struct gui_canvas_t

Canvas structure.

Public Members

gui_obj_t base
NVGcontext *vg
void (*nanovg_canvas_cb)(struct gui_canvas *this_widget)
uint8_t checksum
uint8_t render