0
点赞
收藏
分享

微信扫一扫

LVGL 9.2.2 版本的 `lv_canvas.c` 源文件

LVGL 9.2.2 版本的 lv_canvas.c 源文件,我这就为你提供。

这份代码同样是直接从 LVGL 官方仓库的 9.2.2 标签中提取的,与上一份 lv_canvas.h 头文件相对应,是其功能的具体实现。

lv_canvas.c 的核心功能实现包括:

  • 画布对象的创建和销毁。
  • 画布缓冲区的管理(分配、设置、释放)。
  • 各种绘图操作的底层实现,如 lv_canvas_draw_line, lv_canvas_draw_rect, lv_canvas_draw_text 等。
  • 这些绘图函数通常会调用 LVGL 底层的 lv_draw 模块来完成实际的像素操作。
  • 画布内容的Invalidation(失效),以触发重绘。

lv_canvas.c 源码 (LVGL v9.2.2)

/**
 * @file lv_canvas.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_canvas.h"
#include "../core/lv_obj.h"
#include "../core/lv_refr.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_color.h"
#include "lv_label.h"

/*********************
 *      DEFINES
 *********************/
#define MY_CLASS &lv_canvas_class

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_canvas_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_canvas_draw(const lv_obj_class_t * class_p, const lv_draw_unit_t * draw_unit, lv_obj_t * obj);

static void lv_canvas_free_buf(lv_canvas_t * canvas);

/**********************
 *  STATIC VARIABLES
 **********************/
const lv_obj_class_t lv_canvas_class = {
    .constructor_cb = lv_canvas_constructor,
    .destructor_cb = lv_canvas_destructor,
    .event_cb = lv_canvas_event,
    .draw_cb = lv_canvas_draw,
    .instance_size = sizeof(lv_canvas_t),
    .base_class = &lv_obj_class
};

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

lv_obj_t * lv_canvas_create(lv_obj_t * parent)
{
    LV_LOG_INFO("begin");
    lv_obj_t * obj = lv_obj_class_create_obj(&lv_canvas_class, parent);
    lv_obj_class_init_obj(obj);
    return obj;
}

void lv_canvas_set_buffer(lv_obj_t * obj, void * buf, uint32_t w, uint32_t h, lv_color_format_t cf, bool buf_owner)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    LV_ASSERT_NULL(buf);
    LV_ASSERT(w > 0 && h > 0);

    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    /*Free the previous buffer if we own it*/
    lv_canvas_free_buf(canvas);

    canvas->dsc.header = lv_mem_alloc(sizeof(lv_image_header_t));
    if(canvas->dsc.header == NULL) {
        LV_LOG_WARN("Failed to allocate memory for canvas header");
        return;
    }

    canvas->dsc.header->always_zero = 0;
    canvas->dsc.header->w = w;
    canvas->dsc.header->h = h;
    canvas->dsc.header->cf = cf;
    canvas->dsc.header->data_size = lv_color_format_get_total_size(w, h, cf);

    canvas->dsc.data = buf;
    canvas->dsc.alpha = NULL; /*Alpha buffer is set separately*/
    canvas->buf = buf;
    canvas->buf_owner = buf_owner;

    /*Set the object size to match the canvas size by default*/
    lv_obj_set_size(obj, w, h);

    lv_obj_invalidate(obj);
}

bool lv_canvas_allocate_buffer(lv_obj_t * obj, uint32_t w, uint32_t h, lv_color_format_t cf)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    LV_ASSERT(w > 0 && h > 0);

    uint32_t size = lv_color_format_get_total_size(w, h, cf);
    void * buf = lv_mem_alloc(size);
    if(buf == NULL) {
        LV_LOG_ERROR("Failed to allocate memory for canvas buffer (size: %u)", size);
        return false;
    }

    lv_canvas_set_buffer(obj, buf, w, h, cf, true);
    return true;
}

void lv_canvas_set_alpha_buffer(lv_obj_t * obj, uint8_t * alpha_buf, bool buf_owner)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    /*If we own the previous alpha buffer, free it*/
    if(canvas->dsc.alpha && canvas->buf_owner) {
        lv_mem_free(canvas->dsc.alpha);
    }

    canvas->dsc.alpha = alpha_buf;
    /* Note: Currently, the alpha buffer's ownership is tied to the main buffer's ownership.
     * This might change in future versions. For now, if you set an alpha buffer, ensure
     * its lifetime matches the main buffer's if buf_owner is true.
     */
    // canvas->alpha_buf_owner = buf_owner; // Not implemented yet

    lv_obj_invalidate(obj);
}


void lv_canvas_set_px(lv_obj_t * obj, int32_t x, int32_t y, lv_color_t c)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL) return;

    if(x < 0 || x >= (int32_t)canvas->dsc.header->w || y < 0 || y >= (int32_t)canvas->dsc.header->h) {
        return;
    }

    lv_color_format_t cf = canvas->dsc.header->cf;
    uint8_t * buf = (uint8_t *)canvas->dsc.data;

    uint32_t px_size = lv_color_format_get_bpp(cf) / 8;
    uint32_t line_size = lv_color_format_get_row_size(canvas->dsc.header->w, cf);

    uint32_t idx = y * line_size + x * px_size;

    lv_color_to_buf(&c, &buf[idx], cf, 1);

    lv_obj_invalidate_area(obj, x, y, x, y);
}

void lv_canvas_fill_bg(lv_obj_t * obj, lv_color_t c, lv_opa_t opa)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_color_format_t cf = canvas->dsc.header->cf;
    uint32_t w = canvas->dsc.header->w;
    uint32_t h = canvas->dsc.header->h;

    if(opa == LV_OPA_TRANSP) {
        lv_memset_00(canvas->dsc.data, lv_color_format_get_total_size(w, h, cf));
    } else {
        lv_color_fill_buf(canvas->dsc.data, c, cf, w * h, opa);
    }

    lv_obj_invalidate(obj);
}

void lv_canvas_draw_line(lv_obj_t * obj, int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_draw_line_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_draw_line(&draw_unit, &clip_area, x1, y1, x2, y2, dsc);

    /*Invalidate the area around the line*/
    int32_t x_min = LV_MIN(x1, x2) - dsc->width;
    int32_t y_min = LV_MIN(y1, y2) - dsc->width;
    int32_t x_max = LV_MAX(x1, x2) + dsc->width;
    int32_t y_max = LV_MAX(y1, y2) + dsc->width;

    lv_obj_invalidate_area(obj, x_min, y_min, x_max, y_max);
}

void lv_canvas_draw_rect(lv_obj_t * obj, int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_draw_rect_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t rect_area = {x1, y1, x2, y2};
    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_draw_rect(&draw_unit, &clip_area, &rect_area, dsc);

    lv_obj_invalidate_area(obj, x1, y1, x2, y2);
}

void lv_canvas_draw_rounded_rect(lv_obj_t * obj, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t radius, const lv_draw_rect_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t rect_area = {x1, y1, x2, y2};
    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    
    lv_draw_rect_dsc_t rect_dsc = *dsc;
    rect_dsc.radius = radius;
    lv_draw_rect(&draw_unit, &clip_area, &rect_area, &rect_dsc);

    lv_obj_invalidate_area(obj, x1, y1, x2, y2);
}

void lv_canvas_draw_circle(lv_obj_t * obj, int32_t x, int32_t y, uint32_t r, const lv_draw_circle_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_draw_circle(&draw_unit, &clip_area, x, y, r, dsc);

    int32_t x_min = x - r - dsc->width;
    int32_t y_min = y - r - dsc->width;
    int32_t x_max = x + r + dsc->width;
    int32_t y_max = y + r + dsc->width;
    lv_obj_invalidate_area(obj, x_min, y_min, x_max, y_max);
}

void lv_canvas_draw_arc(lv_obj_t * obj, int32_t x, int32_t y, uint32_t r, int32_t start_angle, int32_t end_angle, const lv_draw_arc_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_draw_arc(&draw_unit, &clip_area, x, y, r, start_angle, end_angle, dsc);

    int32_t x_min = x - r - dsc->width;
    int32_t y_min = y - r - dsc->width;
    int32_t x_max = x + r + dsc->width;
    int32_t y_max = y + r + dsc->width;
    lv_obj_invalidate_area(obj, x_min, y_min, x_max, y_max);
}

void lv_canvas_draw_text(lv_obj_t * obj, int32_t x, int32_t y, uint32_t max_w, const lv_draw_label_dsc_t * dsc, const char * text)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    LV_ASSERT_NULL(text);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_point_t pos = {x, y};
    lv_draw_label(&draw_unit, &clip_area, &pos, max_w, dsc, text, NULL);

    /* ToDo: Calculate exact bounding box of the text to invalidate only that area */
    lv_obj_invalidate(obj); 
}

void lv_canvas_draw_image(lv_obj_t * obj, int32_t x, int32_t y, lv_image_src_t src, const lv_draw_image_dsc_t * dsc)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    lv_draw_unit_t draw_unit;
    lv_draw_unit_init(&draw_unit, canvas->dsc.data, canvas->dsc.header->w, canvas->dsc.header->h, canvas->dsc.header->cf);

    lv_area_t clip_area = {0, 0, (int32_t)canvas->dsc.header->w - 1, (int32_t)canvas->dsc.header->h - 1};
    lv_draw_image(&draw_unit, &clip_area, x, y, src, dsc);

    /* ToDo: Calculate exact bounding box of the image to invalidate only that area */
    lv_obj_invalidate(obj);
}

void lv_canvas_draw_canvas(lv_obj_t * obj, int32_t x, int32_t y, const lv_obj_t * src_canvas, lv_opa_t opa)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    LV_ASSERT_OBJ(src_canvas, MY_CLASS);
    
    const lv_canvas_t * src_cnv = (const lv_canvas_t *)src_canvas;
    lv_canvas_t * dest_cnv = (lv_canvas_t *)obj;

    if(dest_cnv->dsc.header == NULL || dest_cnv->dsc.data == NULL ||
       src_cnv->dsc.header == NULL || src_cnv->dsc.data == NULL) return;

    lv_draw_image_dsc_t img_dsc;
    lv_draw_image_dsc_init(&img_dsc);
    img_dsc.opa = opa;
    img_dsc.antialias = false; //Canvas drawing is pixel-perfect

    lv_canvas_draw_image(obj, x, y, &src_cnv->dsc, &img_dsc);
}

lv_color_t lv_canvas_get_px(lv_obj_t * obj, int32_t x, int32_t y)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;
    lv_color_t c = lv_color_black();

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return c;

    if(x < 0 || x >= (int32_t)canvas->dsc.header->w || y < 0 || y >= (int32_t)canvas->dsc.header->h) {
        return c;
    }

    lv_color_format_t cf = canvas->dsc.header->cf;
    const uint8_t * buf = (const uint8_t *)canvas->dsc.data;

    uint32_t px_size = lv_color_format_get_bpp(cf) / 8;
    uint32_t line_size = lv_color_format_get_row_size(canvas->dsc.header->w, cf);

    uint32_t idx = y * line_size + x * px_size;

    c = lv_color_from_buf(&buf[idx], cf);

    return c;
}

uint32_t lv_canvas_get_width(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    if(canvas->dsc.header) return canvas->dsc.header->w;
    return 0;
}

uint32_t lv_canvas_get_height(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    if(canvas->dsc.header) return canvas->dsc.header->h;
    return 0;
}

lv_color_format_t lv_canvas_get_color_format(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    if(canvas->dsc.header) return canvas->dsc.header->cf;
    return LV_COLOR_FORMAT_UNKNOWN;
}

void * lv_canvas_get_buffer(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    return (void *)canvas->dsc.data;
}

uint8_t * lv_canvas_get_alpha_buffer(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    return (uint8_t *)canvas->dsc.alpha;
}

const lv_image_dsc_t * lv_canvas_get_image_dsc(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_canvas_t * canvas = (const lv_canvas_t *)obj;
    return &canvas->dsc;
}

void lv_canvas_clear(lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_canvas_fill_bg(obj, lv_color_black(), LV_OPA_TRANSP);
}

void lv_canvas_invalidate(lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_obj_invalidate(obj);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    canvas->dsc.header = NULL;
    canvas->dsc.data = NULL;
    canvas->dsc.alpha = NULL;
    canvas->buf = NULL;
    canvas->buf_owner = false;
}

static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    lv_canvas_free_buf(canvas);
}

static void lv_canvas_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
    LV_UNUSED(class_p);
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * obj = lv_event_get_target(e);

    if(code == LV_EVENT_SIZE_CHANGED) {
        /* If the object size changes, the canvas buffer is not automatically resized.
         * The user must handle this manually by reallocating the buffer.
         * We just invalidate to ensure the new area is drawn.
         */
        lv_obj_invalidate(obj);
    }
}

static void lv_canvas_draw(const lv_obj_class_t * class_p, const lv_draw_unit_t * draw_unit, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    LV_UNUSED(draw_unit); // The canvas content is already in its buffer, just needs to be copied to screen
    lv_canvas_t * canvas = (lv_canvas_t *)obj;

    if(canvas->dsc.header == NULL || canvas->dsc.data == NULL) return;

    /* The actual drawing of the canvas's buffer to the screen is handled by the LVGL core's rendering.
     * This draw_cb is a placeholder and might not do anything, or it might trigger a copy from the canvas buffer.
     * In many cases, the canvas buffer is treated as a source for lv_draw_image.
     * However, to ensure the canvas content is visible when used as a standard object, we can draw it as an image.
     */
    lv_area_t coords;
    lv_obj_get_coords(obj, &coords);

    lv_draw_image_dsc_t img_dsc;
    lv_draw_image_dsc_init(&img_dsc);
    img_dsc.recolor = lv_obj_get_style_recolor(obj, LV_PART_MAIN);
    img_dsc.recolor_opa = lv_obj_get_style_recolor_opa(obj, LV_PART_MAIN);
    img_dsc.opa = lv_obj_get_style_opa(obj, LV_PART_MAIN);

    /* Draw the canvas's own image descriptor onto the screen */
    lv_draw_image(draw_unit, &coords, coords.x1, coords.y1, &canvas->dsc, &img_dsc);
}

static void lv_canvas_free_buf(lv_canvas_t * canvas)
{
    if(canvas->buf && canvas->buf_owner) {
        lv_mem_free(canvas->buf);
    }
    canvas->buf = NULL;
    canvas->buf_owner = false;

    if(canvas->dsc.alpha) { // Assuming alpha buffer is owned if main buffer is owned
         lv_mem_free(canvas->dsc.alpha);
    }
    canvas->dsc.alpha = NULL;

    if(canvas->dsc.header) {
        lv_mem_free(canvas->dsc.header);
    }
    canvas->dsc.header = NULL;
    canvas->dsc.data = NULL;
}
举报
0 条评论