feat(draw): add lv_draw_letter support (#7490)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Benign X
2025-01-16 10:42:30 +08:00
committed by GitHub
parent 9ddde9e4bc
commit 62291fa174
19 changed files with 537 additions and 68 deletions

View File

@@ -51,3 +51,15 @@ Draw a vector graphic to the canvas
.. lv_example:: widgets/canvas/lv_example_canvas_8
:language: c
Draw Fancy Letter Effects
-------------------------
.. lv_example:: widgets/canvas/lv_example_canvas_9
:language: c
Draw Fancy Letter Effects 2
---------------------------
.. lv_example:: widgets/canvas/lv_example_canvas_10
:language: c

View File

@@ -0,0 +1,68 @@
#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 300
#define CANVAS_HEIGHT 200
static void timer_cb(lv_timer_t * timer)
{
static int32_t counter = 0;
const char * string = "windstorrrrrrrrrrrrrrrrm~>>>";
const int16_t string_len = lv_strlen(string);
lv_obj_t * canvas = lv_timer_get_user_data(timer);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER);
lv_draw_letter_dsc_t letter_dsc;
lv_draw_letter_dsc_init(&letter_dsc);
letter_dsc.color = lv_color_hex(0xff0000);
letter_dsc.font = lv_font_default();
{
#define CURVE2_X(t) ((t) * 2 + lv_trigo_cos((t) * 5) * 40 / 32767 - 10)
#define CURVE2_Y(t, T) ((t) * lv_trigo_sin(((t) + (T)) * 5) * 40 / 32767 / 80 + CANVAS_HEIGHT / 2)
int32_t pre_x = CURVE2_X(-1);
int32_t pre_y = CURVE2_Y(-1, 0);
for(int16_t i = 0; i < string_len; i++) {
const int32_t angle = i * 5;
const int32_t x = CURVE2_X(angle);
const int32_t y = CURVE2_Y(angle + 30, counter / 2);
letter_dsc.unicode = (uint32_t)string[i % string_len];
letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10;
letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100);
lv_draw_letter(&layer, &letter_dsc, &(lv_point_t) {
.x = x, .y = y
});
pre_x = x;
pre_y = y;
}
}
lv_canvas_finish_layer(canvas, &layer);
counter++;
}
void lv_example_canvas_10(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_obj_set_size(canvas, CANVAS_WIDTH, CANVAS_HEIGHT);
lv_obj_center(canvas);
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_timer_create(timer_cb, 16, canvas);
}
#endif

View File

@@ -0,0 +1,68 @@
#include "../../lv_examples.h"
#if LV_USE_CANVAS && LV_BUILD_EXAMPLES
#define CANVAS_WIDTH 300
#define CANVAS_HEIGHT 200
static void timer_cb(lv_timer_t * timer)
{
static int32_t counter = 0;
const char * string = "lol~ I'm wavvvvvvving~>>>";
const int16_t string_len = lv_strlen(string);
lv_obj_t * canvas = lv_timer_get_user_data(timer);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER);
lv_draw_letter_dsc_t letter_dsc;
lv_draw_letter_dsc_init(&letter_dsc);
letter_dsc.color = lv_color_hex(0xff0000);
letter_dsc.font = lv_font_default();
{
#define CURVE2_X(t) (t * 2 + 10)
#define CURVE2_Y(t) (lv_trigo_sin((t) * 5) * 40 / 32767 + CANVAS_HEIGHT / 2)
int32_t pre_x = CURVE2_X(-1);
int32_t pre_y = CURVE2_Y(-1);
for(int16_t i = 0; i < string_len; i++) {
const int32_t angle = i * 5;
const int32_t x = CURVE2_X(angle);
const int32_t y = CURVE2_Y(angle + counter / 2);
letter_dsc.unicode = (uint32_t)string[i % string_len];
letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10;
letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100);
lv_draw_letter(&layer, &letter_dsc, &(lv_point_t) {
.x = x, .y = y
});
pre_x = x;
pre_y = y;
}
}
lv_canvas_finish_layer(canvas, &layer);
counter++;
}
void lv_example_canvas_9(void)
{
/*Create a buffer for the canvas*/
LV_DRAW_BUF_DEFINE_STATIC(draw_buf, CANVAS_WIDTH, CANVAS_HEIGHT, LV_COLOR_FORMAT_ARGB8888);
LV_DRAW_BUF_INIT_STATIC(draw_buf);
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_obj_set_size(canvas, CANVAS_WIDTH, CANVAS_HEIGHT);
lv_obj_center(canvas);
lv_canvas_set_draw_buf(canvas, &draw_buf);
lv_timer_create(timer_cb, 16, canvas);
}
#endif

View File

@@ -57,6 +57,8 @@ void lv_example_canvas_5(void);
void lv_example_canvas_6(void);
void lv_example_canvas_7(void);
void lv_example_canvas_8(void);
void lv_example_canvas_9(void);
void lv_example_canvas_10(void);
void lv_example_chart_1(void);
void lv_example_chart_2(void);

View File

@@ -48,6 +48,7 @@ typedef enum {
LV_DRAW_TASK_TYPE_FILL,
LV_DRAW_TASK_TYPE_BORDER,
LV_DRAW_TASK_TYPE_BOX_SHADOW,
LV_DRAW_TASK_TYPE_LETTER,
LV_DRAW_TASK_TYPE_LABEL,
LV_DRAW_TASK_TYPE_IMAGE,
LV_DRAW_TASK_TYPE_LAYER,

View File

@@ -43,8 +43,6 @@ typedef unsigned char cmd_state_t;
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb);
/**********************
* STATIC VARIABLES
@@ -62,6 +60,18 @@ static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc,
* GLOBAL FUNCTIONS
**********************/
void lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_letter_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->font = LV_FONT_DEFAULT;
dsc->rotation = 0;
dsc->scale_x = LV_SCALE_NONE;
dsc->scale_y = LV_SCALE_NONE;
dsc->base.dsc_size = sizeof(lv_draw_letter_dsc_t);
}
void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memzero(dsc, sizeof(lv_draw_label_dsc_t));
@@ -155,6 +165,42 @@ void LV_ATTRIBUTE_FAST_MEM lv_draw_character(lv_layer_t * layer, lv_draw_label_d
LV_PROFILER_DRAW_END;
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_letter(lv_layer_t * layer, lv_draw_letter_dsc_t * dsc, const lv_point_t * point)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
const lv_font_t * font = dsc->font;
LV_PROFILER_DRAW_BEGIN;
lv_font_glyph_dsc_t g;
lv_font_get_glyph_dsc(font, &g, dsc->unicode, 0);
font = g.resolved_font ? g.resolved_font : dsc->font;
lv_area_t a;
a.x1 = point->x;
a.y1 = point->y;
a.x2 = a.x1 + g.adv_w;
a.y2 = a.y1 + lv_font_get_line_height(font);
dsc->pivot.x = g.adv_w / 2 ;
dsc->pivot.y = font->line_height - font->base_line;
lv_draw_task_t * t = lv_draw_add_task(layer, &a);
t->draw_dsc = lv_malloc(sizeof(*dsc));
LV_ASSERT_MALLOC(t->draw_dsc);
lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
t->type = LV_DRAW_TASK_TYPE_LETTER;
lv_draw_finalize_task_creation(layer, t);
LV_PROFILER_DRAW_END;
}
void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords,
lv_draw_glyph_cb_t cb)
@@ -266,6 +312,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
draw_letter_dsc.opa = dsc->opa;
draw_letter_dsc.bg_coords = &bg_coords;
draw_letter_dsc.color = dsc->color;
draw_letter_dsc.rotation = dsc->rotation;
lv_draw_fill_dsc_t fill_dsc;
lv_draw_fill_dsc_init(&fill_dsc);
@@ -432,7 +479,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_
draw_letter_dsc.color = dsc->color;
}
draw_letter(draw_unit, &draw_letter_dsc, &pos, font, letter, cb);
lv_draw_unit_draw_letter(draw_unit, &draw_letter_dsc, &pos, font, letter, cb);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
@@ -491,8 +538,9 @@ static uint8_t hex_char_to_num(char hex)
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
return 'A' <= hex && hex <= 'F' ? hex - 'A' + 10 : 0;
}
static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb)
void lv_draw_unit_draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb)
{
lv_font_glyph_dsc_t g;
@@ -517,9 +565,11 @@ static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc,
letter_coords.x2 = letter_coords.x1 + g.box_w - 1;
letter_coords.y1 = pos->y + (font->line_height - font->base_line) - g.box_h - g.ofs_y;
letter_coords.y2 = letter_coords.y1 + g.box_h - 1;
lv_area_move(&letter_coords, -dsc->pivot.x, -dsc->pivot.y);
/*If the letter is completely out of mask don't draw it*/
if(lv_area_is_out(&letter_coords, draw_unit->clip_area, 0) &&
dsc->bg_coords &&
lv_area_is_out(dsc->bg_coords, draw_unit->clip_area, 0)) {
LV_PROFILER_DRAW_END;
return;

View File

@@ -44,6 +44,7 @@ typedef struct {
int32_t letter_space;
int32_t ofs_x;
int32_t ofs_y;
int32_t rotation;
lv_opa_t opa;
lv_base_dir_t bidi_dir;
lv_text_align_t align;
@@ -62,6 +63,25 @@ typedef struct {
lv_draw_label_hint_t * hint;
} lv_draw_label_dsc_t;
typedef struct {
lv_draw_dsc_base_t base;
uint32_t unicode;
const lv_font_t * font;
lv_color_t color;
int32_t rotation;
int32_t scale_x;
int32_t scale_y;
int32_t skew_x;
int32_t skew_y;
lv_point_t pivot;
lv_opa_t opa;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode : 3;
} lv_draw_letter_dsc_t;
/**
* Passed as a parameter to `lv_draw_label_iterate_characters` to
* draw the characters one by one
@@ -81,6 +101,8 @@ typedef void(*lv_draw_glyph_cb_t)(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_
* GLOBAL PROTOTYPES
**********************/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_letter_dsc_init(lv_draw_letter_dsc_t * dsc);
/**
* Initialize a label draw descriptor
* @param dsc pointer to a draw descriptor
@@ -120,6 +142,15 @@ void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_label(lv_layer_t * layer, const lv_draw
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_character(lv_layer_t * layer, lv_draw_label_dsc_t * dsc,
const lv_point_t * point, uint32_t unicode_letter);
/**
* Draw a single letter
* @param layer pointer to a layer
* @param dsc pointer to draw descriptor
* @param point position of the label
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_letter(lv_layer_t * layer, lv_draw_letter_dsc_t * dsc,
const lv_point_t * point);
/**
* Should be used during rendering the characters to get the position and other
* parameters of the characters
@@ -131,6 +162,25 @@ void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_character(lv_layer_t * layer, lv_draw_l
void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, lv_draw_glyph_cb_t cb);
/**
* @brief Draw a single letter using the provided draw unit, glyph descriptor, position, font, and callback.
*
* This function is responsible for rendering a single character from a text string,
* applying the necessary styling described by the glyph descriptor (`dsc`). It handles
* the retrieval of the glyph's description, checks its visibility within the clipping area,
* and invokes the callback (`cb`) to render the glyph at the specified position (`pos`)
* using the given font (`font`).
*
* @param draw_unit Pointer to the drawing unit handling the rendering context.
* @param dsc Pointer to the descriptor containing styling for the glyph to be drawn.
* @param pos Pointer to the point coordinates where the letter should be drawn.
* @param font Pointer to the font containing the glyph.
* @param letter The Unicode code point of the letter to be drawn.
* @param cb Callback function to execute the actual rendering of the glyph.
*/
void lv_draw_unit_draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, const lv_point_t * pos,
const lv_font_t * font, uint32_t letter, lv_draw_glyph_cb_t cb);
/***********************
* GLOBAL VARIABLES
***********************/

View File

@@ -50,6 +50,8 @@ struct _lv_draw_glyph_dsc_t {
lv_font_glyph_dsc_t * g;
lv_color_t color;
lv_opa_t opa;
int32_t rotation;
lv_point_t pivot; /**< Rotation pivot point associated with total glyph including line_height */
lv_draw_buf_t * _draw_buf; /**< a shared draw buf for get_bitmap, do not use it directly, use glyph_data instead */
};

View File

@@ -261,6 +261,9 @@ static void execute_drawing(lv_draw_sw_unit_t * u)
case LV_DRAW_TASK_TYPE_BOX_SHADOW:
lv_draw_sw_box_shadow((lv_draw_unit_t *)u, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LETTER:
lv_draw_sw_letter((lv_draw_unit_t *)u, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LABEL:
lv_draw_sw_label((lv_draw_unit_t *)u, t->draw_dsc, &t->area);
break;

View File

@@ -81,6 +81,8 @@ void lv_draw_sw_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_
void lv_draw_sw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
const lv_area_t * coords);
void lv_draw_sw_letter(lv_draw_unit_t * draw_unit, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords);
/**
* Draw a label with SW render.
* @param draw_unit pointer to a draw unit

View File

@@ -51,6 +51,32 @@ static void /* LV_ATTRIBUTE_FAST_MEM */ draw_letter_cb(lv_draw_unit_t * draw_uni
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_letter(lv_draw_unit_t * draw_unit, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN)
return;
lv_draw_glyph_dsc_t glyph_dsc;
lv_draw_glyph_dsc_init(&glyph_dsc);
glyph_dsc.opa = dsc->opa;
glyph_dsc.bg_coords = NULL;
glyph_dsc.color = dsc->color;
glyph_dsc.rotation = dsc->rotation;
glyph_dsc.pivot = dsc->pivot;
LV_PROFILER_BEGIN;
lv_draw_unit_draw_letter(draw_unit, &glyph_dsc, &(lv_point_t) {
.x = coords->x1, .y = coords->y1
},
dsc->font, dsc->unicode, draw_letter_cb);
LV_PROFILER_END;
if(glyph_dsc._draw_buf) {
lv_draw_buf_destroy(glyph_dsc._draw_buf);
glyph_dsc._draw_buf = NULL;
}
}
void lv_draw_sw_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN) return;
@@ -89,40 +115,45 @@ static void LV_ATTRIBUTE_FAST_MEM draw_letter_cb(lv_draw_unit_t * draw_unit, lv_
case LV_FONT_GLYPH_FORMAT_A1_ALIGNED:
case LV_FONT_GLYPH_FORMAT_A2_ALIGNED:
case LV_FONT_GLYPH_FORMAT_A4_ALIGNED:
case LV_FONT_GLYPH_FORMAT_A8_ALIGNED: {
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
lv_area_t mask_area = *glyph_draw_dsc->letter_coords;
mask_area.x2 = mask_area.x1 + lv_draw_buf_width_to_stride(lv_area_get_width(&mask_area), LV_COLOR_FORMAT_A8) - 1;
lv_draw_sw_blend_dsc_t blend_dsc;
lv_memzero(&blend_dsc, sizeof(blend_dsc));
blend_dsc.color = glyph_draw_dsc->color;
blend_dsc.opa = glyph_draw_dsc->opa;
const lv_draw_buf_t * draw_buf = glyph_draw_dsc->glyph_data;
blend_dsc.mask_buf = draw_buf->data;
blend_dsc.mask_area = &mask_area;
blend_dsc.mask_stride = draw_buf->header.stride;
blend_dsc.blend_area = glyph_draw_dsc->letter_coords;
blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
lv_draw_sw_blend(draw_unit, &blend_dsc);
}
break;
case LV_FONT_GLYPH_FORMAT_A8_ALIGNED:
case LV_FONT_GLYPH_FORMAT_IMAGE: {
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
lv_draw_image_dsc_t img_dsc;
lv_draw_image_dsc_init(&img_dsc);
img_dsc.rotation = 0;
img_dsc.scale_x = LV_SCALE_NONE;
img_dsc.scale_y = LV_SCALE_NONE;
img_dsc.opa = glyph_draw_dsc->opa;
img_dsc.src = glyph_draw_dsc->glyph_data;
lv_draw_sw_image(draw_unit, &img_dsc, glyph_draw_dsc->letter_coords);
if(glyph_draw_dsc->rotation % 3600 == 0 && glyph_draw_dsc->format != LV_FONT_GLYPH_FORMAT_IMAGE) {
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
lv_area_t mask_area = *glyph_draw_dsc->letter_coords;
mask_area.x2 = mask_area.x1 + lv_draw_buf_width_to_stride(lv_area_get_width(&mask_area), LV_COLOR_FORMAT_A8) - 1;
lv_draw_sw_blend_dsc_t blend_dsc;
lv_memzero(&blend_dsc, sizeof(blend_dsc));
blend_dsc.color = glyph_draw_dsc->color;
blend_dsc.opa = glyph_draw_dsc->opa;
const lv_draw_buf_t * draw_buf = glyph_draw_dsc->glyph_data;
blend_dsc.mask_buf = draw_buf->data;
blend_dsc.mask_area = &mask_area;
blend_dsc.mask_stride = draw_buf->header.stride;
blend_dsc.blend_area = glyph_draw_dsc->letter_coords;
blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
lv_draw_sw_blend(draw_unit, &blend_dsc);
}
else {
glyph_draw_dsc->glyph_data = lv_font_get_glyph_bitmap(glyph_draw_dsc->g, glyph_draw_dsc->_draw_buf);
lv_draw_image_dsc_t img_dsc;
lv_draw_image_dsc_init(&img_dsc);
img_dsc.rotation = glyph_draw_dsc->rotation;
img_dsc.scale_x = LV_SCALE_NONE;
img_dsc.scale_y = LV_SCALE_NONE;
img_dsc.opa = glyph_draw_dsc->opa;
img_dsc.src = glyph_draw_dsc->glyph_data;
img_dsc.recolor = glyph_draw_dsc->color;
img_dsc.pivot = (lv_point_t) {
.x = glyph_draw_dsc->pivot.x,
.y = glyph_draw_dsc->g->box_h + glyph_draw_dsc->g->ofs_y
};
lv_draw_sw_image(draw_unit, &img_dsc, glyph_draw_dsc->letter_coords);
}
break;
}
break;
default:
break;
}
}
if(fill_draw_dsc && fill_area) {

View File

@@ -143,6 +143,9 @@ static void draw_execute(lv_draw_vg_lite_unit_t * u)
#endif
switch(t->type) {
case LV_DRAW_TASK_TYPE_LETTER:
lv_draw_vg_lite_letter(draw_unit, t->draw_dsc, &t->area);
break;
case LV_DRAW_TASK_TYPE_LABEL:
lv_draw_vg_lite_label(draw_unit, t->draw_dsc, &t->area);
break;
@@ -240,6 +243,7 @@ static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
}
switch(task->type) {
case LV_DRAW_TASK_TYPE_LETTER:
case LV_DRAW_TASK_TYPE_LABEL:
case LV_DRAW_TASK_TYPE_FILL:
case LV_DRAW_TASK_TYPE_BORDER:

View File

@@ -62,6 +62,8 @@ void lv_draw_vg_lite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t *
void lv_draw_vg_lite_label_init(lv_draw_unit_t * draw_unit);
void lv_draw_vg_lite_letter(lv_draw_unit_t * draw_unit, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_vg_lite_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords);

View File

@@ -19,6 +19,7 @@
#include "lv_vg_lite_utils.h"
#include "lv_vg_lite_path.h"
#include "lv_draw_vg_lite_type.h"
#include <float.h>
/*********************
* DEFINES
@@ -80,6 +81,32 @@ void lv_draw_vg_lite_label_init(lv_draw_unit_t * draw_unit)
#endif /* LV_USE_FREETYPE */
}
void lv_draw_vg_lite_letter(lv_draw_unit_t * draw_unit, const lv_draw_letter_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->opa <= LV_OPA_MIN)
return;
lv_draw_glyph_dsc_t glyph_dsc;
lv_draw_glyph_dsc_init(&glyph_dsc);
glyph_dsc.opa = dsc->opa;
glyph_dsc.bg_coords = NULL;
glyph_dsc.color = dsc->color;
glyph_dsc.rotation = dsc->rotation;
glyph_dsc.pivot = dsc->pivot;
LV_PROFILER_BEGIN;
lv_draw_unit_draw_letter(draw_unit, &glyph_dsc, &(lv_point_t) {
.x = coords->x1, .y = coords->y1
},
dsc->font, dsc->unicode, draw_letter_cb);
LV_PROFILER_END;
if(glyph_dsc._draw_buf) {
lv_draw_buf_destroy(glyph_dsc._draw_buf);
glyph_dsc._draw_buf = NULL;
}
}
void lv_draw_vg_lite_label(lv_draw_unit_t * draw_unit, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords)
{
@@ -128,6 +155,7 @@ static void draw_letter_cb(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * gly
lv_draw_image_dsc_init(&image_dsc);
image_dsc.opa = glyph_draw_dsc->opa;
image_dsc.src = glyph_draw_dsc->glyph_data;
image_dsc.rotation = glyph_draw_dsc->rotation;
lv_draw_vg_lite_img(draw_unit, &image_dsc, glyph_draw_dsc->letter_coords, false);
}
break;
@@ -170,17 +198,26 @@ static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_d
LV_PROFILER_DRAW_BEGIN;
lv_area_t image_area = *dsc->letter_coords;
const lv_area_t image_area = *dsc->letter_coords;
vg_lite_matrix_t matrix = u->global_matrix;
vg_lite_translate(image_area.x1, image_area.y1, &matrix);
const bool is_rotated = dsc->rotation % 3600 != 0;
if(!is_rotated) {
vg_lite_translate(image_area.x1, image_area.y1, &matrix);
}
else {
vg_lite_translate(image_area.x1 + dsc->pivot.x, image_area.y1 + (dsc->g->box_h + dsc->g->ofs_y), &matrix);
vg_lite_rotate(dsc->rotation / 10.0f, &matrix);
vg_lite_translate(-dsc->pivot.x, -dsc->g->box_h - dsc->g->ofs_y, &matrix);
}
vg_lite_buffer_t src_buf;
const lv_draw_buf_t * draw_buf = dsc->glyph_data;
lv_vg_lite_buffer_from_draw_buf(&src_buf, draw_buf);
vg_lite_color_t color;
color = lv_vg_lite_color(dsc->color, dsc->opa, true);
const vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true);
LV_VG_LITE_ASSERT_SRC_BUFFER(&src_buf);
LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer);
@@ -220,6 +257,7 @@ static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_d
LV_VG_LITE_ASSERT_PATH(vg_lite_path);
vg_lite_matrix_t path_matrix = u->global_matrix;
if(is_rotated) vg_lite_rotate(dsc->rotation / 10.0f, &path_matrix);
LV_VG_LITE_ASSERT_MATRIX(&path_matrix);
LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw_pattern");
@@ -265,32 +303,61 @@ static void draw_letter_outline(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_
path_clip_area.y2++;
lv_vg_lite_path_t * outline = (lv_vg_lite_path_t *)dsc->glyph_data;
lv_point_t pos = {dsc->letter_coords->x1, dsc->letter_coords->y1};
const lv_point_t pos = {dsc->letter_coords->x1, dsc->letter_coords->y1};
/* scale size */
float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(dsc->g->resolved_font));
const float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(dsc->g->resolved_font));
const bool is_rotated = dsc->rotation % 3600 != 0;
/* calc convert matrix */
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
vg_lite_translate(pos.x - dsc->g->ofs_x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix);
vg_lite_scale(scale, scale, &matrix);
/* calc inverse matrix */
vg_lite_matrix_t result;
if(!lv_vg_lite_matrix_inverse(&result, &matrix)) {
LV_LOG_ERROR("no inverse matrix");
LV_PROFILER_DRAW_END;
return;
if(!is_rotated) {
vg_lite_translate(pos.x - dsc->g->ofs_x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix);
vg_lite_scale(scale, scale, &matrix);
}
else {
vg_lite_translate(pos.x - dsc->g->ofs_x + dsc->pivot.x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix);
vg_lite_rotate(dsc->rotation / 10.0f, &matrix);
vg_lite_translate(-dsc->pivot.x, 0, &matrix);
vg_lite_scale(scale, scale, &matrix);
}
lv_point_precise_t p1 = { path_clip_area.x1, path_clip_area.y1 };
lv_point_precise_t p1_res = lv_vg_lite_matrix_transform_point(&result, &p1);
if(vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR)) {
/* set scissor area */
lv_vg_lite_set_scissor_area(u->base_unit.clip_area);
lv_point_precise_t p2 = { path_clip_area.x2, path_clip_area.y2 };
lv_point_precise_t p2_res = lv_vg_lite_matrix_transform_point(&result, &p2);
/* no bounding box */
lv_vg_lite_path_set_bounding_box(outline,
(float)PATH_COORD_MIN, (float)PATH_COORD_MIN,
(float)PATH_COORD_MAX, (float)PATH_COORD_MAX);
}
else {
if(is_rotated) {
LV_LOG_WARN("clip may be incorrect when vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR) is false");
}
/* Since the font uses Cartesian coordinates, the y coordinates need to be reversed */
lv_vg_lite_path_set_bounding_box(outline, p1_res.x, p2_res.y, p2_res.x, p1_res.y);
/* calc inverse matrix */
vg_lite_matrix_t result;
if(!lv_vg_lite_matrix_inverse(&result, &matrix)) {
LV_LOG_ERROR("no inverse matrix");
LV_PROFILER_DRAW_END;
return;
}
const lv_point_precise_t p1 = { path_clip_area.x1, path_clip_area.y1 };
const lv_point_precise_t p1_res = lv_vg_lite_matrix_transform_point(&result, &p1);
const lv_point_precise_t p2 = { path_clip_area.x2, path_clip_area.y2 };
const lv_point_precise_t p2_res = lv_vg_lite_matrix_transform_point(&result, &p2);
/* Since the font uses Cartesian coordinates, the y coordinates need to be reversed */
if(is_rotated)
lv_vg_lite_path_set_bounding_box(outline,
(float)PATH_COORD_MIN, (float)PATH_COORD_MIN,
(float)PATH_COORD_MAX, (float)PATH_COORD_MAX);
else lv_vg_lite_path_set_bounding_box(outline, p1_res.x, p2_res.y, p2_res.x, p1_res.y);
}
/* matrix for drawing, different from matrix for calculating the bounding box */
vg_lite_matrix_t draw_matrix = u->global_matrix;

View File

@@ -25,20 +25,6 @@
* DEFINES
*********************/
#if LV_USE_VG_LITE_THORVG
/**
* It is found that thorvg cannot handle large coordinates well.
* When the coordinates are larger than 4096, the calculation of tvgSwRle module will overflow in 32-bit system.
* So we use FLT_MAX and FLT_MIN to write the mark to bounding_box to tell vg_lite_tvg not to add clip path to the current path.
*/
#define PATH_COORD_MAX FLT_MAX
#define PATH_COORD_MIN FLT_MIN
#else
/* 18 bits is enough to represent the coordinates of path bounding box */
#define PATH_COORD_MAX (1 << 18)
#define PATH_COORD_MIN (-PATH_COORD_MAX)
#endif
/**********************
* TYPEDEFS
**********************/

View File

@@ -22,6 +22,20 @@ extern "C" {
* DEFINES
*********************/
#if LV_USE_VG_LITE_THORVG
/**
* It is found that thorvg cannot handle large coordinates well.
* When the coordinates are larger than 4096, the calculation of tvgSwRle module will overflow in 32-bit system.
* So we use FLT_MAX and FLT_MIN to write the mark to bounding_box to tell vg_lite_tvg not to add clip path to the current path.
*/
#define PATH_COORD_MAX FLT_MAX
#define PATH_COORD_MIN FLT_MIN
#else
/* 18 bits is enough to represent the coordinates of path bounding box */
#define PATH_COORD_MAX (1 << 18)
#define PATH_COORD_MIN (-PATH_COORD_MAX)
#endif
#define LV_VG_LITE_PATH_SET_OP_CODE(PTR, TYPE, OP_CODE) (*((TYPE*)PTR) = (OP_CODE))
#define LV_VG_LITE_PATH_GET_OP_CODE(PTR) (*((uint8_t*)PTR))

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,107 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "../../lvgl_private.h"
#include "unity/unity.h"
void setUp(void)
{
/* Function run before every test */
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY);
}
void tearDown(void)
{
/* Function run after every test */
lv_obj_clean(lv_screen_active());
}
static lv_obj_t * canvas_create(void)
{
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_obj_set_size(canvas, 500, 360);
lv_draw_buf_t * draw_buf = lv_draw_buf_create(500, 360, LV_COLOR_FORMAT_ARGB8888, LV_STRIDE_AUTO);
lv_draw_buf_clear(draw_buf, NULL);
lv_canvas_set_draw_buf(canvas, draw_buf);
return canvas;
}
static void canvas_destroy(lv_obj_t * canvas)
{
lv_draw_buf_destroy(lv_canvas_get_draw_buf(canvas));
lv_obj_delete(canvas);
}
void test_draw_sin_wave(void)
{
const char * string = "lol~ I'm wavvvvvvving~";
const uint32_t string_len = lv_strlen(string);
LV_FONT_DECLARE(test_font_montserrat_ascii_4bpp);
lv_obj_t * canvas = canvas_create();
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_draw_letter_dsc_t letter_dsc;
lv_draw_letter_dsc_init(&letter_dsc);
letter_dsc.color = lv_color_hex(0xff0000);
letter_dsc.font = &test_font_montserrat_ascii_4bpp;
{
#define CURVE1_X(t) (t * 2 + 20)
#define CURVE1_Y(t) (lv_trigo_sin(t) * 40 / 32767 + 80)
int32_t pre_x = CURVE1_X(-1);
int32_t pre_y = CURVE1_Y(-1);
for(int16_t i = 0; i < 30; i++) {
const int32_t angle = i * 10;
const int32_t x = CURVE1_X(angle);
const int32_t y = CURVE1_Y(angle);
letter_dsc.unicode = (uint32_t)string[i % string_len];
letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x);
letter_dsc.rotation = (letter_dsc.rotation > 180 ? letter_dsc.rotation - 360 : letter_dsc.rotation) * 5;
lv_draw_letter(&layer, &letter_dsc, &(lv_point_t) {
.x = x, .y = y
});
pre_x = x;
pre_y = y;
}
}
{
#define CURVE2_X(t) (t * 3 + 20)
#define CURVE2_Y(t) (lv_trigo_sin((t) * 4) * 40 / 32767 + 230)
int32_t pre_x = CURVE2_X(-1);
int32_t pre_y = CURVE2_Y(-1);
for(int16_t i = 0; i < 30; i++) {
const int32_t angle = i * 5;
const int32_t x = CURVE2_X(angle);
const int32_t y = CURVE2_Y(angle);
letter_dsc.unicode = (uint32_t)string[i % string_len];
letter_dsc.rotation = lv_atan2(y - pre_y, x - pre_x) * 10;
letter_dsc.color = lv_color_hsv_to_rgb(i * 10, 100, 100);
lv_draw_letter(&layer, &letter_dsc, &(lv_point_t) {
.x = x, .y = y
});
pre_x = x;
pre_y = y;
}
}
lv_canvas_finish_layer(canvas, &layer);
#ifndef NON_AMD64_BUILD
TEST_ASSERT_EQUAL_SCREENSHOT("draw/letter_0.png");
#endif
canvas_destroy(canvas);
}
#endif