画布(Canvas)

画布控件提供基于 NanoVG 的 2D 图形绘制能力,并支持软件加速渲染和多种图像输出格式。



警告

使用前需确保:帧缓冲区有足够内存。

组件概述

GUI Canvas 是基于 NanoVG 的 2D 绘图组件,提供:

  • 基础形状绘制(矩形/圆形/弧形等)

  • 支持 RGBA/RGB565/ PNG/JPG输出格式

  • NanoVG 软件加速渲染(通过 AGGE 后端)

  • 直接缓冲区输出功能

  • 空白缓冲区初始化支持

核心功能

创建与初始化

  1. 使用 gui_canvas_create 创建画布控件,并通过 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. 绘制回调函数中通过 NanoVG 接口绘制图形,当需要刷新画布时系统会调用该回调。

    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->render = 1 手动触发重绘,系统会在下一帧调用绘制回调。

图像输出

使用 gui_canvas_render_to_image_buffer 函数可将画布内容渲染到预先分配的缓冲区,适用于需要重复渲染或内存受限的场景。缓冲区大小需按 宽 × 高 × 每像素字节数,再加上头部 gui_rgb_data_head_t 的大小来计算,示例如下:

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);

示例代码

完整卡片视图使用示例:

请通过 menuconfig 启用 Kconfig 选项来运行该示例:

cd simulator/win32
menuconfig ../../Kconfig.gui

选择画布示例( CONFIG_REALTEK_BUILD_REAL_CANVAS),保存到 simulator/win32/.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.

参数:
  • 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.

返回:

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.

参数:
  • 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.

参数:
  • 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.

返回:

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