fix(vg_lite): fix rotated vector font clipping error (#8739)

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
VIFEX
2025-09-01 14:23:23 +08:00
committed by GitHub
parent 6eee76ef13
commit 0e03abce1c
38 changed files with 327 additions and 108 deletions
+223 -80
View File
@@ -20,6 +20,7 @@
#include "../../misc/lv_area_private.h"
#include "../../libs/freetype/lv_freetype_private.h"
#include "../lv_draw_label_private.h"
#include "../lv_draw_image_private.h"
/*********************
@@ -60,6 +61,7 @@ static void bitmap_cache_release_cb(void * entry, void * user_data);
#if LV_USE_FREETYPE
static void freetype_outline_event_cb(lv_event_t * e);
static void draw_letter_outline(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc);
static void outline_iter_cb(void * user_data, uint8_t op_code, const float * data, uint32_t len);
#endif /* LV_USE_FREETYPE */
/**********************
@@ -244,53 +246,120 @@ static void draw_letter_cb(lv_draw_task_t * t, lv_draw_glyph_dsc_t * glyph_draw_
}
}
static void draw_letter_bitmap(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc, vg_lite_buffer_t * src_buf)
static inline void convert_letter_matrix(vg_lite_matrix_t * matrix, const lv_draw_glyph_dsc_t * dsc)
{
lv_area_t clip_area;
lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)t->draw_unit;
if(!lv_area_intersect(&clip_area, &t->clip_area, dsc->letter_coords)) {
vg_lite_translate(dsc->letter_coords->x1, dsc->letter_coords->y1, matrix);
if(!dsc->rotation) {
return;
}
const lv_point_t pivot = {
.x = dsc->pivot.x,
.y = dsc->g->box_h + dsc->g->ofs_y
};
vg_lite_translate(pivot.x, pivot.y, matrix);
vg_lite_rotate(dsc->rotation / 10.0f, matrix);
vg_lite_translate(-pivot.x, -pivot.y, matrix);
}
static bool draw_letter_clip_areas(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc, lv_area_t * letter_area,
lv_area_t * cliped_area)
{
*letter_area = *dsc->letter_coords;
if(dsc->rotation) {
const lv_point_t pivot = {
.x = dsc->pivot.x,
.y = dsc->g->box_h + dsc->g->ofs_y
};
lv_image_buf_get_transformed_area(
letter_area,
lv_area_get_width(dsc->letter_coords),
lv_area_get_height(dsc->letter_coords),
dsc->rotation,
LV_SCALE_NONE,
LV_SCALE_NONE,
&pivot);
lv_area_move(letter_area, dsc->letter_coords->x1, dsc->letter_coords->y1);
}
if(!lv_area_intersect(cliped_area, &t->clip_area, letter_area)) {
return false;
}
return true;
}
static void draw_letter_bitmap(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc, vg_lite_buffer_t * src_buf)
{
LV_PROFILER_DRAW_BEGIN;
const lv_area_t image_area = *dsc->letter_coords;
lv_area_t image_area;
lv_area_t clip_area;
if(!draw_letter_clip_areas(t, dsc, &image_area, &clip_area)) {
LV_PROFILER_DRAW_END;
return;
}
lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)t->draw_unit;
vg_lite_matrix_t matrix = u->global_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);
}
convert_letter_matrix(&matrix, dsc);
const vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true);
vg_lite_rectangle_t rect = {
.x = clip_area.x1 - image_area.x1,
.y = clip_area.y1 - image_area.y1,
.width = lv_area_get_width(&clip_area),
.height = lv_area_get_height(&clip_area)
};
/* If rotation is not required, blit directly */
if(!dsc->rotation) {
vg_lite_rectangle_t rect = {
.x = clip_area.x1 - image_area.x1,
.y = clip_area.y1 - image_area.y1,
.width = lv_area_get_width(&clip_area),
.height = lv_area_get_height(&clip_area)
};
/* add offset for clipped area */
if(rect.x || rect.y) {
vg_lite_translate(rect.x, rect.y, &matrix);
/* add offset for clipped area */
if(rect.x || rect.y) {
vg_lite_translate(rect.x, rect.y, &matrix);
}
lv_vg_lite_blit_rect(
&u->target_buffer,
src_buf,
&rect,
&matrix,
VG_LITE_BLEND_SRC_OVER,
color,
VG_LITE_FILTER_LINEAR);
}
else {
lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_S16);
lv_vg_lite_path_append_rect(
path,
image_area.x1, image_area.y1,
lv_area_get_width(&image_area), lv_area_get_height(&image_area),
0);
lv_vg_lite_path_end(path);
lv_vg_lite_path_set_bounding_box_area(path, &clip_area);
lv_vg_lite_blit_rect(
&u->target_buffer,
src_buf,
&rect,
&matrix,
VG_LITE_BLEND_SRC_OVER,
color,
VG_LITE_FILTER_LINEAR);
vg_lite_matrix_t path_matrix = u->global_matrix;
lv_vg_lite_draw_pattern(
&u->target_buffer,
lv_vg_lite_path_get_path(path),
VG_LITE_FILL_EVEN_ODD,
&path_matrix,
src_buf,
&matrix,
VG_LITE_BLEND_SRC_OVER,
VG_LITE_PATTERN_COLOR,
0,
color,
VG_LITE_FILTER_LINEAR);
lv_vg_lite_path_drop(u, path);
}
/* Check if the data has cache and add it to the pending list */
if(dsc->g->entry) {
@@ -317,80 +386,118 @@ static void bitmap_cache_release_cb(void * entry, void * user_data)
static void draw_letter_outline(lv_draw_task_t * t, const lv_draw_glyph_dsc_t * dsc)
{
lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)t->draw_unit;
/* get clip area */
LV_PROFILER_DRAW_BEGIN;
lv_area_t letter_area;
lv_area_t path_clip_area;
if(!lv_area_intersect(&path_clip_area, &t->clip_area, dsc->letter_coords)) {
if(!draw_letter_clip_areas(t, dsc, &letter_area, &path_clip_area)) {
LV_PROFILER_DRAW_END;
return;
}
LV_PROFILER_DRAW_BEGIN;
lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)t->draw_unit;
/* vg-lite bounding_box will crop the pixels on the edge, so +1px is needed here */
path_clip_area.x2++;
path_clip_area.y2++;
lv_vg_lite_path_t * outline = (lv_vg_lite_path_t *)dsc->glyph_data;
const lv_point_t pos = {dsc->letter_coords->x1, dsc->letter_coords->y1};
const lv_point_t glyph_pos = {
dsc->letter_coords->x1 - dsc->g->ofs_x,
dsc->letter_coords->y1 + dsc->g->box_h + dsc->g->ofs_y
};
/* scale size */
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;
const bool has_rotation_with_cliped = dsc->rotation && !lv_area_is_in(&letter_area, &t->clip_area, false);
/* calc convert matrix */
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
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);
if(!has_rotation_with_cliped && dsc->rotation) {
vg_lite_translate(glyph_pos.x + dsc->pivot.x, glyph_pos.y, &matrix);
vg_lite_rotate(dsc->rotation / 10.0f, &matrix);
vg_lite_translate(-dsc->pivot.x, 0, &matrix);
vg_lite_scale(scale, scale, &matrix);
}
if(vg_lite_query_feature(gcFEATURE_BIT_VG_SCISSOR)) {
/* set scissor area */
lv_vg_lite_set_scissor_area(u, &t->clip_area);
/* 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");
}
/* 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);
vg_lite_translate(glyph_pos.x, glyph_pos.y, &matrix);
}
vg_lite_scale(scale, scale, &matrix);
/* matrix for drawing, different from matrix for calculating the bounding box */
vg_lite_matrix_t draw_matrix = u->global_matrix;
lv_vg_lite_matrix_multiply(&draw_matrix, &matrix);
/* calc inverse matrix */
vg_lite_matrix_t result;
if(!lv_vg_lite_matrix_inverse(&result, &matrix)) {
LV_LOG_ERROR("no inverse matrix");
lv_vg_lite_matrix_dump_info(&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);
if(has_rotation_with_cliped) {
/**
* When intersecting the clipping region,
* rotate the path contents without rotating the bounding box for cropping
*/
vg_lite_matrix_t internal_matrix;
vg_lite_identity(&internal_matrix);
const float pivot_x = dsc->pivot.x / scale;
const float pivot_y = dsc->g->box_h + dsc->g->ofs_y;
vg_lite_translate(pivot_x, pivot_y, &internal_matrix);
vg_lite_rotate(dsc->rotation / 10.0f, &internal_matrix);
vg_lite_translate(-pivot_x, -pivot_y, &internal_matrix);
lv_vg_lite_path_t * outline_transformed = lv_vg_lite_path_get(u, VG_LITE_FP32);
lv_vg_lite_path_set_transform(outline_transformed, &internal_matrix);
lv_vg_lite_path_for_each_data(lv_vg_lite_path_get_path(outline), outline_iter_cb, outline_transformed);
lv_vg_lite_path_set_bounding_box(outline_transformed, p1_res.x, p2_res.y, p2_res.x, p1_res.y);
lv_vg_lite_draw(
&u->target_buffer,
lv_vg_lite_path_get_path(outline_transformed),
VG_LITE_FILL_NON_ZERO,
&draw_matrix,
VG_LITE_BLEND_SRC_OVER,
lv_vg_lite_color(dsc->color, dsc->opa, true));
lv_vg_lite_path_drop(u, outline_transformed);
LV_PROFILER_DRAW_END;
return;
}
if(dsc->rotation) {
/* The bounding rectangle before scaling relative to the original coordinates of the path */
lv_area_t box_area;
box_area.x1 = dsc->g->ofs_x;
box_area.y1 = -dsc->g->box_h - dsc->g->ofs_y;
lv_area_set_width(&box_area, dsc->g->box_w);
lv_area_set_height(&box_area, dsc->g->box_h);
/* Workaround for loss of rotation precision */
lv_area_increase(&box_area, 5, 5);
/* Scale the path area to fit the original path data */
lv_vg_lite_path_set_bounding_box(outline,
box_area.x1 / scale,
box_area.y1 / scale,
box_area.x2 / scale,
box_area.y2 / scale);
}
else {
lv_vg_lite_path_set_bounding_box(outline, p1_res.x, p2_res.y, p2_res.x, p1_res.y);
}
lv_vg_lite_draw(
&u->target_buffer,
lv_vg_lite_path_get_path(outline),
@@ -463,6 +570,42 @@ static void freetype_outline_event_cb(lv_event_t * e)
LV_PROFILER_DRAW_END;
}
static void outline_iter_cb(void * user_data, uint8_t op_code, const float * data, uint32_t len)
{
LV_UNUSED(len);
typedef struct {
float x;
float y;
} point_t;
lv_vg_lite_path_t * path = user_data;
const point_t * pt = (point_t *)data;
switch(op_code) {
case VLC_OP_MOVE:
lv_vg_lite_path_move_to(path, pt->x, pt->y);
break;
case VLC_OP_LINE:
lv_vg_lite_path_line_to(path, pt->x, pt->y);
break;
case VLC_OP_QUAD:
lv_vg_lite_path_quad_to(path, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
break;
case VLC_OP_CUBIC:
lv_vg_lite_path_cubic_to(path, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x, pt[2].y);
break;
case VLC_OP_CLOSE:
lv_vg_lite_path_close(path);
break;
case VLC_OP_END:
lv_vg_lite_path_end(path);
break;
default:
LV_ASSERT_FORMAT_MSG(false, "unknown op_code: %d", op_code);
break;
}
}
#endif /* LV_USE_FREETYPE */
#endif /*LV_USE_DRAW_VG_LITE*/
+3
View File
@@ -647,6 +647,7 @@ void lv_vg_lite_path_for_each_data(const vg_lite_path_t * path, lv_vg_lite_path_
{
LV_ASSERT_NULL(path);
LV_ASSERT_NULL(cb);
LV_PROFILER_DRAW_BEGIN;
uint8_t fmt_len = lv_vg_lite_path_format_len(path->format);
uint8_t * cur = path->path;
@@ -688,6 +689,8 @@ void lv_vg_lite_path_for_each_data(const vg_lite_path_t * path, lv_vg_lite_path_
cb(user_data, op_code, tmp_data, arg_len);
}
LV_PROFILER_DRAW_END;
}
void lv_vg_lite_path_append_path(lv_vg_lite_path_t * dest, const lv_vg_lite_path_t * src)
-14
View File
@@ -23,20 +23,6 @@ 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: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 57 KiB

+101 -14
View File
@@ -4,6 +4,12 @@
#include "unity/unity.h"
#ifndef NON_AMD64_BUILD
#define TEST_ASSERT_EQUAL_LETTER_SCREENSHOT(path) TEST_ASSERT_EQUAL_SCREENSHOT(path)
#else
#define TEST_ASSERT_EQUAL_LETTER_SCREENSHOT(path) LV_UNUSED(path)
#endif
void setUp(void)
{
/* Function run before every test */
@@ -17,31 +23,34 @@ void tearDown(void)
lv_obj_clean(lv_screen_active());
}
static lv_obj_t * canvas_create(void)
static void on_canvas_delete(lv_event_t * e)
{
lv_obj_t * canvas = lv_event_get_current_target(e);
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(canvas);
TEST_ASSERT_NOT_NULL(draw_buf);
lv_draw_buf_destroy(draw_buf);
}
static lv_obj_t * canvas_create(uint32_t w, uint32_t h)
{
lv_obj_t * canvas = lv_canvas_create(lv_screen_active());
lv_obj_set_size(canvas, 500, 360);
lv_obj_set_size(canvas, w, h);
lv_draw_buf_t * draw_buf = lv_draw_buf_create(500, 360, LV_COLOR_FORMAT_ARGB8888, LV_STRIDE_AUTO);
lv_draw_buf_t * draw_buf = lv_draw_buf_create(w, h, LV_COLOR_FORMAT_ARGB8888, LV_STRIDE_AUTO);
lv_draw_buf_clear(draw_buf, NULL);
lv_canvas_set_draw_buf(canvas, draw_buf);
lv_obj_add_event_cb(canvas, on_canvas_delete, LV_EVENT_DELETE, NULL);
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_obj_t * canvas = canvas_create(500, 360);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
@@ -97,11 +106,89 @@ void test_draw_sin_wave(void)
lv_canvas_finish_layer(canvas, &layer);
#ifndef NON_AMD64_BUILD
TEST_ASSERT_EQUAL_SCREENSHOT("draw/letter_0.png");
#endif
TEST_ASSERT_EQUAL_LETTER_SCREENSHOT("draw/letter_0.png");
canvas_destroy(canvas);
lv_obj_delete(canvas);
}
static void draw_letter_with_rotation(lv_obj_t * canvas, lv_layer_t * layer, uint32_t unicode, int32_t rotation,
int32_t x, int32_t y)
{
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_obj_get_style_text_font(canvas, 0);
letter_dsc.unicode = unicode;
letter_dsc.rotation = rotation;
lv_draw_letter(layer, &letter_dsc, &(lv_point_t) {
.x = x, .y = y
});
}
static void test_draw_letter(lv_freetype_font_render_mode_t render_mode, uint32_t unicode, int32_t rotation,
const char * ref_img_path)
{
lv_obj_t * canvas = canvas_create(240, 240);
lv_font_t * font_normal = lv_freetype_font_create("./src/test_files/fonts/noto/NotoSansSC-Regular.ttf",
render_mode,
80,
LV_FREETYPE_FONT_STYLE_NORMAL);
TEST_ASSERT_NOT_NULL(font_normal);
lv_obj_set_style_text_font(canvas, font_normal, 0);
lv_layer_t layer;
lv_canvas_init_layer(canvas, &layer);
lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER);
/* drawing letter with clipping */
lv_area_t clip_area;
lv_area_set(&clip_area, 40, 40, 200 - 1, 200 - 1);
layer._clip_area = clip_area;
const int32_t offset_x = 40;
const int32_t offset_y = 70;
for(int i = 0; i < 9; i++) {
draw_letter_with_rotation(canvas, &layer, unicode, rotation, (i % 3) * 80 + offset_x, (i / 3) * 80 + offset_y);
}
lv_draw_border_dsc_t draw_border_dsc;
lv_draw_border_dsc_init(&draw_border_dsc);
draw_border_dsc.width = 1;
draw_border_dsc.color = lv_color_black();
lv_draw_border(&layer, &draw_border_dsc, &clip_area);
lv_canvas_finish_layer(canvas, &layer);
TEST_ASSERT_EQUAL_LETTER_SCREENSHOT(ref_img_path);
lv_obj_delete(canvas);
lv_freetype_font_delete(font_normal);
}
void test_draw_letter_bitmap(void)
{
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'A', 0, "draw/letter_bitmap_A_rotated_0.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'A', 450, "draw/letter_bitmap_A_rotated_45.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'A', 900, "draw/letter_bitmap_A_rotated_90.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'A', 1800, "draw/letter_bitmap_A_rotated_180.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'g', 0, "draw/letter_bitmap_g_rotated_0.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'g', 450, "draw/letter_bitmap_g_rotated_45.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'g', 900, "draw/letter_bitmap_g_rotated_90.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_BITMAP, 'g', 1800, "draw/letter_bitmap_g_rotated_180.png");
}
void test_draw_letter_outline(void)
{
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'A', 0, "draw/letter_outline_A_rotated_0.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'A', 450, "draw/letter_outline_A_rotated_45.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'A', 900, "draw/letter_outline_A_rotated_90.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'A', 1800, "draw/letter_outline_A_rotated_180.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'g', 0, "draw/letter_outline_g_rotated_0.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'g', 450, "draw/letter_outline_g_rotated_45.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'g', 900, "draw/letter_outline_g_rotated_90.png");
test_draw_letter(LV_FREETYPE_FONT_RENDER_MODE_OUTLINE, 'g', 1800, "draw/letter_outline_g_rotated_180.png");
}
#endif