From 9fc6801860b5766e56d98d300616293538ded23d Mon Sep 17 00:00:00 2001 From: _VIFEXTech Date: Mon, 18 Dec 2023 21:17:42 +0800 Subject: [PATCH] feat(draw): add vg-lite draw unit (#5010) Signed-off-by: pengyiqiang Co-authored-by: pengyiqiang --- Kconfig | 13 + lv_conf_template.h | 11 + src/draw/lv_draw_label.c | 2 + src/draw/lv_draw_label.h | 1 + src/draw/vg_lite/lv_draw_buf_vg_lite.c | 197 +++ src/draw/vg_lite/lv_draw_vg_lite.c | 241 ++++ src/draw/vg_lite/lv_draw_vg_lite.h | 84 ++ src/draw/vg_lite/lv_draw_vg_lite_arc.c | 200 +++ src/draw/vg_lite/lv_draw_vg_lite_border.c | 115 ++ src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c | 97 ++ src/draw/vg_lite/lv_draw_vg_lite_fill.c | 111 ++ src/draw/vg_lite/lv_draw_vg_lite_img.c | 155 +++ src/draw/vg_lite/lv_draw_vg_lite_label.c | 313 +++++ src/draw/vg_lite/lv_draw_vg_lite_layer.c | 72 ++ src/draw/vg_lite/lv_draw_vg_lite_line.c | 211 ++++ src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c | 94 ++ src/draw/vg_lite/lv_draw_vg_lite_triangle.c | 104 ++ src/draw/vg_lite/lv_draw_vg_lite_type.h | 55 + src/draw/vg_lite/lv_draw_vg_lite_vector.c | 317 +++++ src/draw/vg_lite/lv_vg_lite_decoder.c | 550 +++++++++ src/draw/vg_lite/lv_vg_lite_decoder.h | 49 + src/draw/vg_lite/lv_vg_lite_math.c | 60 + src/draw/vg_lite/lv_vg_lite_math.h | 75 ++ src/draw/vg_lite/lv_vg_lite_path.c | 606 ++++++++++ src/draw/vg_lite/lv_vg_lite_path.h | 124 ++ src/draw/vg_lite/lv_vg_lite_utils.c | 1070 +++++++++++++++++ src/draw/vg_lite/lv_vg_lite_utils.h | 188 +++ src/lv_conf_internal.h | 29 + src/lv_init.c | 11 + 29 files changed, 5155 insertions(+) create mode 100644 src/draw/vg_lite/lv_draw_buf_vg_lite.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite.h create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_arc.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_border.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_fill.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_img.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_label.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_layer.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_line.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_triangle.c create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_type.h create mode 100644 src/draw/vg_lite/lv_draw_vg_lite_vector.c create mode 100644 src/draw/vg_lite/lv_vg_lite_decoder.c create mode 100644 src/draw/vg_lite/lv_vg_lite_decoder.h create mode 100644 src/draw/vg_lite/lv_vg_lite_math.c create mode 100644 src/draw/vg_lite/lv_vg_lite_math.h create mode 100644 src/draw/vg_lite/lv_vg_lite_path.c create mode 100644 src/draw/vg_lite/lv_vg_lite_path.h create mode 100644 src/draw/vg_lite/lv_vg_lite_utils.c create mode 100644 src/draw/vg_lite/lv_vg_lite_utils.h diff --git a/Kconfig b/Kconfig index a80e34d30..c4c4ae4c3 100644 --- a/Kconfig +++ b/Kconfig @@ -357,6 +357,19 @@ menu "LVGL configuration" config LV_USE_DRAW_PXP bool "Use NXP's PXP on iMX RTxxx platforms." + config LV_USE_DRAW_VG_LITE + bool "Use VG-Lite GPU." + + config LV_VG_LITE_USE_GPU_INIT + bool "Enbale VG-Lite custom external 'gpu_init()' function." + default n + depends on LV_USE_DRAW_VG_LITE + + config LV_VG_LITE_USE_ASSERT + bool "Enable VG-Lite assert." + default n + depends on LV_USE_DRAW_VG_LITE + config LV_USE_GPU_SDL bool "Use SDL renderer API" default n diff --git a/lv_conf_template.h b/lv_conf_template.h index a63f46a11..28582db1c 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -128,6 +128,17 @@ /* Draw using cached SDL textures*/ #define LV_USE_DRAW_SDL 0 +/* Use VG-Lite GPU. */ +#define LV_USE_DRAW_VG_LITE 0 + +#if LV_USE_DRAW_VG_LITE +/* Enbale VG-Lite custom external 'gpu_init()' function */ +#define LV_VG_LITE_USE_GPU_INIT 0 + +/* Enable VG-Lite assert. */ +#define LV_VG_LITE_USE_ASSERT 0 +#endif + /*================= * OPERATING SYSTEM *=================*/ diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index affaa8ebe..6d2eba326 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -419,6 +419,8 @@ static void draw_letter(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * dsc, if(g.bpp == LV_IMGFONT_BPP) dsc->format = LV_DRAW_LETTER_BITMAP_FORMAT_IMAGE; else dsc->format = LV_DRAW_LETTER_BITMAP_FORMAT_A8; + dsc->g = &g; + cb(draw_unit, dsc, NULL, NULL); LV_PROFILER_END; } diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index 3f6bd9df8..295371f57 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -87,6 +87,7 @@ typedef struct { lv_draw_glyph_bitmap_format_t format; const lv_area_t * letter_coords; const lv_area_t * bg_coords; + const lv_font_glyph_dsc_t * g; lv_color_t color; lv_opa_t opa; } lv_draw_glyph_dsc_t; diff --git a/src/draw/vg_lite/lv_draw_buf_vg_lite.c b/src/draw/vg_lite/lv_draw_buf_vg_lite.c new file mode 100644 index 000000000..fd756e9a4 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_buf_vg_lite.c @@ -0,0 +1,197 @@ +/** + * @file lv_draw_buf_vg_lite.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void * buf_malloc(size_t size, lv_color_format_t color_format); +static void buf_free(void * buf); +static void * buf_align(void * buf, lv_color_format_t color_format); +static void invalidate_cache(void * buf, uint32_t stride, lv_color_format_t color_format, const lv_area_t * area); +static uint32_t width_to_stride(uint32_t w, lv_color_format_t color_format); +static void * buf_go_to_xy(const void * buf, uint32_t stride, lv_color_format_t color_format, int32_t x, + int32_t y); +static void buf_clear(void * buf, uint32_t w, uint32_t h, lv_color_format_t color_format, const lv_area_t * a); +static void buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const lv_area_t * dest_area_to_copy, + void * src_buf, uint32_t src_w, uint32_t src_h, const lv_area_t * src_area_to_copy, + lv_color_format_t color_format); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_buf_vg_lite_init_handlers(void) +{ + lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers(); + + handlers->buf_malloc_cb = buf_malloc; + handlers->buf_free_cb = buf_free; + handlers->align_pointer_cb = buf_align; + handlers->invalidate_cache_cb = invalidate_cache; + handlers->width_to_stride_cb = width_to_stride; + handlers->go_to_xy_cb = buf_go_to_xy; + handlers->buf_clear_cb = buf_clear; + handlers->buf_copy_cb = buf_copy; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void * buf_malloc(size_t size_bytes, lv_color_format_t color_format) +{ + LV_UNUSED(color_format); + size_bytes = LV_VG_LITE_ALIGN(size_bytes, LV_VG_LITE_BUF_ALIGN); + return aligned_alloc(LV_VG_LITE_BUF_ALIGN, size_bytes); +} + +static void buf_free(void * buf) +{ + free(buf); +} + +static void * buf_align(void * buf, lv_color_format_t color_format) +{ + LV_UNUSED(color_format); + return (void *)LV_VG_LITE_ALIGN((lv_uintptr_t)buf, LV_VG_LITE_BUF_ALIGN); +} + +static void invalidate_cache(void * buf, uint32_t stride, lv_color_format_t color_format, const lv_area_t * area) +{ +} + +static uint32_t width_to_stride(uint32_t w, lv_color_format_t color_format) +{ + return lv_vg_lite_width_to_stride(w, lv_vg_lite_vg_fmt(color_format)); +} + +static void * buf_go_to_xy(const void * buf, uint32_t stride, lv_color_format_t color_format, int32_t x, + int32_t y) +{ + const uint8_t * buf_tmp = buf; + buf_tmp += stride * y; + buf_tmp += x * lv_color_format_get_size(color_format); + + return (void *)buf_tmp; +} + +static void buf_clear(void * buf, uint32_t w, uint32_t h, lv_color_format_t color_format, const lv_area_t * a) +{ +#if 0 + if(LV_VG_LITE_IS_ALIGNED(buf, LV_VG_LITE_BUF_ALIGN)) { + /* finish outstanding buffers */ + LV_VG_LITE_CHECK_ERROR(vg_lite_finish()); + + vg_lite_buffer_t dest_buf; + LV_ASSERT(lv_vg_lite_buffer_init(&dest_buf, buf, w, h, lv_vg_lite_vg_fmt(color_format), false)); + LV_VG_LITE_ASSERT_DEST_BUFFER(&dest_buf); + + vg_lite_rectangle_t rect; + lv_vg_lite_rect(&rect, a); + LV_VG_LITE_CHECK_ERROR(vg_lite_clear(&dest_buf, &rect, 0)); + LV_VG_LITE_CHECK_ERROR(vg_lite_finish()); + return; + } +#endif + + uint8_t px_size = lv_color_format_get_size(color_format); + uint32_t stride = lv_draw_buf_width_to_stride(w, color_format); + uint8_t * bufc = buf; + + /*Got the first pixel of each buffer*/ + bufc += stride * a->y1; + bufc += a->x1 * px_size; + + uint32_t line_length = lv_area_get_width(a) * px_size; + int32_t y; + for(y = a->y1; y <= a->y2; y++) { + lv_memzero(bufc, line_length); + bufc += stride; + } +} + +static void buf_copy(void * dest_buf, uint32_t dest_w, uint32_t dest_h, const lv_area_t * dest_area_to_copy, + void * src_buf, uint32_t src_w, uint32_t src_h, const lv_area_t * src_area_to_copy, + lv_color_format_t color_format) +{ +#if 0 + if(LV_VG_LITE_IS_ALIGNED(dest_buf, LV_VG_LITE_BUF_ALIGN) + && LV_VG_LITE_IS_ALIGNED(src_buf, LV_VG_LITE_BUF_ALIGN)) { + vg_lite_buffer_t dest; + LV_ASSERT(lv_vg_lite_buffer_init(&dest, dest_buf, dest_w, dest_h, lv_vg_lite_vg_fmt(color_format), false)); + LV_VG_LITE_ASSERT_DEST_BUFFER(&dest); + + vg_lite_buffer_t src; + LV_ASSERT(lv_vg_lite_buffer_init(&src, src_buf, src_w, src_h, lv_vg_lite_vg_fmt(color_format), false)); + LV_VG_LITE_ASSERT_SRC_BUFFER(&src); + + vg_lite_rectangle_t src_rect; + lv_vg_lite_rect(&src_rect, src_area_to_copy); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + LV_VG_LITE_CHECK_ERROR(vg_lite_blit_rect(&dest, &src, + &src_rect, + &matrix, + VG_LITE_BLEND_NONE, 0, + VG_LITE_FILTER_POINT)); + LV_VG_LITE_CHECK_ERROR(vg_lite_finish()); + return; + } +#endif + + uint8_t px_size = lv_color_format_get_size(color_format); + uint8_t * dest_bufc = dest_buf; + uint8_t * src_bufc = src_buf; + + uint32_t dest_stride = lv_draw_buf_width_to_stride(dest_w, color_format); + uint32_t src_stride = lv_draw_buf_width_to_stride(src_w, color_format); + + /*Got the first pixel of each buffer*/ + dest_bufc += dest_stride * dest_area_to_copy->y1; + dest_bufc += dest_area_to_copy->x1 * px_size; + + src_bufc += src_stride * src_area_to_copy->y1; + src_bufc += src_area_to_copy->x1 * px_size; + + uint32_t line_length = lv_area_get_width(dest_area_to_copy) * px_size; + int32_t y; + for(y = dest_area_to_copy->y1; y <= dest_area_to_copy->y2; y++) { + lv_memcpy(dest_bufc, src_bufc, line_length); + dest_bufc += dest_stride; + src_bufc += src_stride; + } +} + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite.c b/src/draw/vg_lite/lv_draw_vg_lite.c new file mode 100644 index 000000000..0785e1097 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite.c @@ -0,0 +1,241 @@ +/** + * @file lv_vg_lite_draw.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "../lv_draw.h" +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" +#include "lv_vg_lite_decoder.h" + +/********************* + * DEFINES + *********************/ + +#define VG_LITE_DRAW_UNIT_ID 2 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static int32_t draw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer); + +static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task); + +static int32_t draw_delete(lv_draw_unit_t * draw_unit); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_init(void) +{ +#if LV_VG_LITE_USE_GPU_INIT + extern void gpu_init(void); + static bool inited = false; + if(!inited) { + gpu_init(); + inited = true; + } +#endif + + lv_vg_lite_dump_info(); + + lv_draw_buf_vg_lite_init_handlers(); + + lv_draw_vg_lite_unit_t * unit = lv_draw_create_unit(sizeof(lv_draw_vg_lite_unit_t)); + unit->base_unit.dispatch_cb = draw_dispatch; + unit->base_unit.evaluate_cb = draw_evaluate; + unit->base_unit.delete_cb = draw_delete; + + lv_vg_lite_path_init(unit); + + lv_vg_lite_decoder_init(); +} + +void lv_draw_vg_lite_deinit(void) +{ +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_execute(lv_draw_vg_lite_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u; + + lv_layer_t * layer = u->base_unit.target_layer; + + lv_vg_lite_buffer_init( + &u->target_buffer, + layer->buf, + lv_area_get_width(&layer->buf_area), + lv_area_get_height(&layer->buf_area), + lv_vg_lite_vg_fmt(layer->color_format), + false); + + vg_lite_identity(&u->global_matrix); + vg_lite_translate(-layer->buf_area.x1, -layer->buf_area.y1, &u->global_matrix); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_LABEL: + lv_draw_vg_lite_label(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_FILL: + lv_draw_vg_lite_fill(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_BORDER: + lv_draw_vg_lite_border(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_BOX_SHADOW: + lv_draw_vg_lite_box_shadow(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_draw_vg_lite_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_ARC: + lv_draw_vg_lite_arc(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_LINE: + lv_draw_vg_lite_line(draw_unit, t->draw_dsc); + break; + case LV_DRAW_TASK_TYPE_LAYER: + lv_draw_vg_lite_layer(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_TRIANGLE: + lv_draw_vg_lite_triangle(draw_unit, t->draw_dsc); + break; + case LV_DRAW_TASK_TYPE_MASK_RECTANGLE: + lv_draw_vg_lite_mask_rect(draw_unit, t->draw_dsc, &t->area); + break; +#if LV_USE_VECTOR_GRAPHIC + case LV_DRAW_TASK_TYPE_VECTOR: + lv_draw_vg_lite_vector(draw_unit, t->draw_dsc); + break; +#endif + default: + break; + } + +#if LV_USE_PARALLEL_DRAW_DEBUG + /* Layers manage it for themselves. */ + if(t->type != LV_DRAW_TASK_TYPE_LAYER) { + } +#endif + + LV_VG_LITE_CHECK_ERROR(vg_lite_finish()); +} + +static int32_t draw_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) +{ + lv_draw_vg_lite_unit_t * draw_vg_lite_unit = (lv_draw_vg_lite_unit_t *)draw_unit; + + /* Return immediately if it's busy with draw task. */ + if(draw_vg_lite_unit->task_act) { + return 0; + } + + /* Return if target buffer format is not supported. */ + if(!lv_vg_lite_is_dest_cf_supported(layer->color_format)) { + return -1; + } + + /* Try to get an ready to draw. */ + lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, VG_LITE_DRAW_UNIT_ID); + + /* Return 0 is no selection, some tasks can be supported by other units. */ + if(!t || t->preferred_draw_unit_id != VG_LITE_DRAW_UNIT_ID) { + return -1; + } + + void * buf = lv_draw_layer_alloc_buf(layer); + if(!buf) { + return -1; + } + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_vg_lite_unit->base_unit.target_layer = layer; + draw_vg_lite_unit->base_unit.clip_area = &t->clip_area; + draw_vg_lite_unit->task_act = t; + + draw_execute(draw_vg_lite_unit); + + draw_vg_lite_unit->task_act->state = LV_DRAW_TASK_STATE_READY; + draw_vg_lite_unit->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); + + return 1; +} + +static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task) +{ + LV_UNUSED(draw_unit); + + switch(task->type) { +#if LV_USE_FREETYPE && LV_FREETYPE_CACHE_TYPE == LV_FREETYPE_CACHE_TYPE_OUTLINE + case LV_DRAW_TASK_TYPE_LABEL: { + const lv_draw_label_dsc_t * label_dsc = task->draw_dsc; + if(lv_freetype_is_outline_font(label_dsc->font)) { + task->preference_score = 0; + task->preferred_draw_unit_id = VG_LITE_DRAW_UNIT_ID; + return 1; + } + return 0; + } +#endif + case LV_DRAW_TASK_TYPE_FILL: + case LV_DRAW_TASK_TYPE_BORDER: + case LV_DRAW_TASK_TYPE_BOX_SHADOW: + case LV_DRAW_TASK_TYPE_IMAGE: + case LV_DRAW_TASK_TYPE_LAYER: + case LV_DRAW_TASK_TYPE_LINE: + case LV_DRAW_TASK_TYPE_ARC: + case LV_DRAW_TASK_TYPE_TRIANGLE: + // case LV_DRAW_TASK_TYPE_MASK_RECTANGLE: + // case LV_DRAW_TASK_TYPE_MASK_BITMAP: +#if LV_USE_VECTOR_GRAPHIC + case LV_DRAW_TASK_TYPE_VECTOR: +#endif + task->preference_score = 80; + task->preferred_draw_unit_id = VG_LITE_DRAW_UNIT_ID; + return 1; + default: + break; + } + return 0; +} + +static int32_t draw_delete(lv_draw_unit_t * draw_unit) +{ + lv_draw_vg_lite_unit_t * unit = (lv_draw_vg_lite_unit_t *)draw_unit; + lv_vg_lite_path_deinit(unit); + lv_vg_lite_decoder_deinit(); + return 1; +} + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite.h b/src/draw/vg_lite/lv_draw_vg_lite.h new file mode 100644 index 000000000..b34f07ae9 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite.h @@ -0,0 +1,84 @@ +/** + * @file lv_vg_lite_draw.h + * + */ + +#ifndef LV_VG_LITE_DRAW_H +#define LV_VG_LITE_DRAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_DRAW_VG_LITE + +#include "../lv_draw.h" +#include "../../draw/lv_draw_vector.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_buf_vg_lite_init_handlers(void); + +void lv_draw_vg_lite_init(void); + +void lv_draw_vg_lite_deinit(void); + +void lv_draw_vg_lite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vg_lite_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vg_lite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vg_lite_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, + const lv_area_t * coords); + +void lv_draw_vg_lite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_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); + +void lv_draw_vg_lite_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords); + +void lv_draw_vg_lite_line(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc); + +void lv_draw_vg_lite_triangle(lv_draw_unit_t * draw_unit, const lv_draw_triangle_dsc_t * dsc); + +void lv_draw_vg_lite_mask_rect(lv_draw_unit_t * draw_unit, const lv_draw_mask_rect_dsc_t * dsc, + const lv_area_t * coords); + +#if LV_USE_VECTOR_GRAPHIC +void lv_draw_vg_lite_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc); +#endif + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VG_LITE_DRAW_H*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_arc.c b/src/draw/vg_lite/lv_draw_vg_lite_arc.c new file mode 100644 index 000000000..eff653635 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_arc.c @@ -0,0 +1,200 @@ +/** + * @file lv_draw_vg_lite_arc.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_math.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" +#include + +/********************* + * DEFINES + *********************/ + +#define PI 3.1415926535897932384626433832795 +#define TWO_PI 6.283185307179586476925286766559 +#define DEG_TO_RAD 0.017453292519943295769236907684886 +#define RAD_TO_DEG 57.295779513082320876798154814105 +#define radians(deg) ((deg) * DEG_TO_RAD) +#define degrees(rad) ((rad) * RAD_TO_DEG) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= LV_OPA_MIN) + return; + if(dsc->width <= 0) + return; + if(dsc->start_angle == dsc->end_angle) + return; + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) { + /*Fully clipped, nothing to do*/ + return; + } + + float start_angle = dsc->start_angle; + float end_angle = dsc->end_angle; + float sweep_angle = end_angle - start_angle; + + while(sweep_angle < 0) { + sweep_angle += 360; + } + + while(sweep_angle > 360) { + sweep_angle -= 360; + } + + /*If the angles are the same then there is nothing to draw*/ + if(math_zero(sweep_angle)) { + return; + } + + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_FP32); + lv_vg_lite_path_set_quality(path, VG_LITE_HIGH); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + + float radius_out = dsc->radius; + float radius_in = dsc->radius - dsc->width; + float half_width = dsc->width * 0.5f; + float radius_center = radius_out - half_width; + float cx = dsc->center.x; + float cy = dsc->center.y; + + vg_lite_fill_t fill = VG_LITE_FILL_NON_ZERO; + + if(math_equal(sweep_angle, 360)) { + lv_vg_lite_path_append_circle(path, cx, cy, radius_out, radius_out); + lv_vg_lite_path_append_circle(path, cx, cy, radius_in, radius_in); + fill = VG_LITE_FILL_EVEN_ODD; + } + else { + /* radius_out start point */ + float start_angle_rad = MATH_RADIANS(start_angle); + float start_x = radius_out * MATH_COSF(start_angle_rad) + cx; + float start_y = radius_out * MATH_SINF(start_angle_rad) + cy; + + /* radius_in start point */ + float end_angle_rad = MATH_RADIANS(end_angle); + float end_x = radius_in * MATH_COSF(end_angle_rad) + cx; + float end_y = radius_in * MATH_SINF(end_angle_rad) + cy; + + /* radius_out arc */ + lv_vg_lite_path_append_arc(path, + cx, cy, + radius_out, + start_angle, + sweep_angle, + false); + + /* line to radius_in */ + lv_vg_lite_path_line_to(path, end_x, end_y); + + /* radius_in arc */ + lv_vg_lite_path_append_arc(path, + cx, cy, + radius_in, + end_angle, + -sweep_angle, + false); + + /* close arc */ + lv_vg_lite_path_line_to(path, start_x, start_y); + lv_vg_lite_path_close(path); + + /* draw round */ + if(dsc->rounded && half_width > 0) { + float rcx1 = cx + radius_center * MATH_COSF(end_angle_rad); + float rcy1 = cy + radius_center * MATH_SINF(end_angle_rad); + lv_vg_lite_path_append_circle(path, rcx1, rcy1, half_width, half_width); + + float rcx2 = cx + radius_center * MATH_COSF(start_angle_rad); + float rcy2 = cy + radius_center * MATH_SINF(start_angle_rad); + lv_vg_lite_path_append_circle(path, rcx2, rcy2, half_width, half_width); + } + } + + lv_vg_lite_path_end(path); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_lite_path, + fill, + &matrix, + VG_LITE_BLEND_SRC_OVER, + color)); + + if(dsc->img_src) { + vg_lite_buffer_t src_buf; + lv_image_decoder_dsc_t decoder_dsc; + if(lv_vg_lite_buffer_open_image(&src_buf, &decoder_dsc, dsc->img_src)) { + vg_lite_matrix_t path_matrix; + vg_lite_identity(&path_matrix); + LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern( + &u->target_buffer, + vg_lite_path, + fill, + &path_matrix, + &src_buf, + &matrix, + VG_LITE_BLEND_SRC_OVER, + VG_LITE_PATTERN_COLOR, + 0, + color, + VG_LITE_FILTER_BI_LINEAR)); + lv_image_decoder_close(&decoder_dsc); + } + } + + lv_vg_lite_path_drop(u, path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_border.c b/src/draw/vg_lite/lv_draw_vg_lite_border.c new file mode 100644 index 000000000..757e2ea22 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_border.c @@ -0,0 +1,115 @@ +/** + * @file lv_draw_vg_lite_border.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_utils.h" +#include "lv_vg_lite_path.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= LV_OPA_MIN) + return; + if(dsc->width == 0) + return; + if(dsc->side == LV_BORDER_SIDE_NONE) + return; + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) { + /*Fully clipped, nothing to do*/ + return; + } + + int32_t w = lv_area_get_width(coords); + int32_t h = lv_area_get_height(coords); + int32_t r_out = dsc->radius; + if(r_out) { + int32_t r_short = LV_MIN(w, h) / 2; + r_out = LV_MIN(r_out, r_short); + } + + int32_t border_w = dsc->width; + int32_t r_in = LV_MAX(0, r_out - border_w); + + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_S16); + lv_vg_lite_path_set_quality(path, dsc->radius == 0 ? VG_LITE_LOW : VG_LITE_HIGH); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + + /* outer rect */ + lv_vg_lite_path_append_rect(path, + coords->x1, coords->y1, + w, h, + r_out, r_out); + + /* inner rect */ + lv_vg_lite_path_append_rect(path, + coords->x1 + border_w, coords->y1 + border_w, + w - border_w * 2, h - border_w * 2, + r_in, r_in); + + lv_vg_lite_path_end(path); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &matrix, + VG_LITE_BLEND_SRC_OVER, + color)); + + lv_vg_lite_path_drop(u, path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c b/src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c new file mode 100644 index 000000000..c9e00c4d3 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c @@ -0,0 +1,97 @@ +/** + * @file lv_draw_vg_lite_box_shadow.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_box_shadow(lv_draw_unit_t * draw_unit, const lv_draw_box_shadow_dsc_t * dsc, + const lv_area_t * coords) +{ + /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/ + lv_area_t core_area; + core_area.x1 = coords->x1 + dsc->ofs_x - dsc->spread; + core_area.x2 = coords->x2 + dsc->ofs_x + dsc->spread; + core_area.y1 = coords->y1 + dsc->ofs_y - dsc->spread; + core_area.y2 = coords->y2 + dsc->ofs_y + dsc->spread; + + /*Calculate the bounding box of the shadow*/ + lv_area_t shadow_area; + shadow_area.x1 = core_area.x1 - dsc->width / 2 - 1; + shadow_area.x2 = core_area.x2 + dsc->width / 2 + 1; + shadow_area.y1 = core_area.y1 - dsc->width / 2 - 1; + shadow_area.y2 = core_area.y2 + dsc->width / 2 + 1; + + lv_opa_t opa = dsc->opa; + if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; + + /*Get clipped draw area which is the real draw area. + *It is always the same or inside `shadow_area`*/ + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &shadow_area, draw_unit->clip_area)) return; + + lv_draw_border_dsc_t border_dsc; + lv_draw_border_dsc_init(&border_dsc); + border_dsc.width = 3; + border_dsc.color = dsc->color; + border_dsc.radius = dsc->radius; + + lv_area_move(&draw_area, dsc->ofs_x, dsc->ofs_y); + draw_area = core_area; + int32_t half_w = dsc->width / 2; + + for(int32_t w = 0; w < half_w; w++) { + border_dsc.opa = lv_map(w, 0, half_w, dsc->opa / 4, LV_OPA_0); + border_dsc.radius++; + lv_area_increase(&draw_area, 1, 1); + lv_draw_vg_lite_border(draw_unit, &border_dsc, &draw_area); + + /* fill center */ + if(dsc->ofs_x || dsc->ofs_y) { + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.radius = dsc->radius; + fill_dsc.opa = dsc->opa; + fill_dsc.color = dsc->color; + lv_draw_vg_lite_fill(draw_unit, &fill_dsc, &core_area); + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_fill.c b/src/draw/vg_lite/lv_draw_vg_lite_fill.c new file mode 100644 index 000000000..459fd47fb --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_fill.c @@ -0,0 +1,111 @@ +/** + * @file lv_draw_vg_lite_fill.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" + +/********************* + * DEFINES + *********************/ + +#if LV_GRADIENT_MAX_STOPS > VLC_MAX_GRADIENT_STOPS + #error "LV_GRADIENT_MAX_STOPS must be equal or less than VLC_MAX_GRADIENT_STOPS" +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->opa <= LV_OPA_MIN) { + return; + } + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) { + /*Fully clipped, nothing to do*/ + return; + } + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + int32_t w = lv_area_get_width(coords); + int32_t h = lv_area_get_height(coords); + int32_t r = dsc->radius; + if(r) { + int32_t r_short = LV_MIN(w, h) / 2; + r = LV_MIN(r, r_short); + } + + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_S16); + lv_vg_lite_path_set_quality(path, dsc->radius == 0 ? VG_LITE_LOW : VG_LITE_HIGH); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + lv_vg_lite_path_append_rect(path, coords->x1, coords->y1, w, h, r, r); + lv_vg_lite_path_end(path); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + if(dsc->grad.dir != LV_GRAD_DIR_NONE) { + lv_vg_lite_draw_linear_grad( + &u->target_buffer, + vg_lite_path, + coords, + &dsc->grad, + &matrix, + VG_LITE_FILL_EVEN_ODD, + VG_LITE_BLEND_SRC_OVER); + } + else { /* normal fill */ + vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true); + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &matrix, + VG_LITE_BLEND_SRC_OVER, + color)); + } + + lv_vg_lite_path_drop(u, path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_img.c b/src/draw/vg_lite/lv_draw_vg_lite_img.c new file mode 100644 index 000000000..e15e183bf --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_img.c @@ -0,0 +1,155 @@ +/** + * @file lv_draw_vg_lite_img.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_decoder.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc, + const lv_area_t * coords) +{ + if(dsc->opa <= LV_OPA_MIN) { + return; + } + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + /* The coordinates passed in by coords are not transformed, + * so the transformed area needs to be calculated once. + */ + lv_area_t image_tf_area; + _lv_image_buf_get_transformed_area( + &image_tf_area, + lv_area_get_width(coords), + lv_area_get_height(coords), + dsc->rotation, + dsc->scale_x, + dsc->scale_y, + &dsc->pivot); + lv_area_move(&image_tf_area, coords->x1, coords->y1); + + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, &image_tf_area, draw_unit->clip_area)) { + /*Fully clipped, nothing to do*/ + return; + } + + vg_lite_buffer_t src_buf; + lv_image_decoder_dsc_t decoder_dsc; + if(!lv_vg_lite_buffer_open_image(&src_buf, &decoder_dsc, dsc->src)) { + return; + } + + /* image opa */ + lv_opa_t opa = dsc->opa; + vg_lite_color_t color = 0; + if(opa < LV_OPA_MAX) { + lv_memset(&color, opa, sizeof(color)); + src_buf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE; + } + + bool has_recolor = dsc->recolor_opa >= LV_OPA_MIN; + bool has_trasform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE); + vg_lite_filter_t filter = has_trasform ? VG_LITE_FILTER_BI_LINEAR : VG_LITE_FILTER_POINT; + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + lv_vg_lite_image_matrix(&matrix, coords->x1, coords->y1, dsc); + + LV_VG_LITE_ASSERT_SRC_BUFFER(&src_buf); + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + + /* If clipping is not required, blit directly */ + if(_lv_area_is_in(&image_tf_area, draw_unit->clip_area, false) && !has_recolor) { + /* The image area is the coordinates relative to the image itself */ + lv_area_t src_area = *coords; + lv_area_move(&src_area, -coords->x1, -coords->y1); + + /* rect is used to crop the pixel-aligned padding area */ + vg_lite_rectangle_t rect; + lv_vg_lite_rect(&rect, &src_area); + LV_VG_LITE_CHECK_ERROR(vg_lite_blit_rect( + &u->target_buffer, + &src_buf, + &rect, + &matrix, + lv_vg_lite_blend_mode(dsc->blend_mode), + color, + filter)); + } + else { + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_S16); + lv_vg_lite_path_append_rect( + path, + clip_area.x1, clip_area.y1, + lv_area_get_width(&clip_area), lv_area_get_height(&clip_area), + 0, 0); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + lv_vg_lite_path_end(path); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + vg_lite_matrix_t path_matrix; + vg_lite_identity(&path_matrix); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &path_matrix, + &src_buf, + &matrix, + lv_vg_lite_blend_mode(dsc->blend_mode), + VG_LITE_PATTERN_COLOR, + lv_vg_lite_color(dsc->recolor, dsc->recolor_opa, true), + color, + filter)); + + lv_vg_lite_path_drop(u, path); + } + + lv_image_decoder_close(&decoder_dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_label.c b/src/draw/vg_lite/lv_draw_vg_lite_label.c new file mode 100644 index 000000000..ef7e64a3c --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_label.c @@ -0,0 +1,313 @@ +/** + * @file lv_draw_vg_lite_label.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#include "../../libs/freetype/lv_freetype.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" +#include "lv_vg_lite_path.h" +#include "lv_draw_vg_lite_type.h" + +/********************* + * DEFINES + *********************/ + +#define PATH_QUALITY VG_LITE_MEDIUM +#define PATH_DATA_COORD_FORMAT VG_LITE_S16 +#define PATH_REF_SIZE 128 +#define FT_F26DOT6_SHIFT 6 + +/** After converting the font reference size, it is also necessary to scale the 26dot6 data + * in the path to the real physical size + */ +#define FT_F26DOT6_TO_PATH_SCALE(x) (LV_FREETYPE_F26DOT6_TO_FLOAT(x) / (1 << FT_F26DOT6_SHIFT)) + +#define SUPPORT_OUTLINE_FONT (LV_USE_FREETYPE && LV_FREETYPE_CACHE_TYPE == LV_FREETYPE_CACHE_TYPE_OUTLINE) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void draw_letter_cb(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc, + lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area); + +static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc); + +#if SUPPORT_OUTLINE_FONT + static void freetype_outline_event_cb(lv_event_t * e); + static void draw_letter_outline(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc); +#endif + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_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; + +#if SUPPORT_OUTLINE_FONT + static bool is_init = false; + if(!is_init) { + lv_freetype_outline_set_ref_size(PATH_REF_SIZE); + lv_freetype_outline_add_event(freetype_outline_event_cb, LV_EVENT_ALL, draw_unit); + is_init = true; + } +#endif /*SUPPORT_OUTLINE_FONT*/ + + lv_draw_label_iterate_letters(draw_unit, dsc, coords, draw_letter_cb); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void draw_letter_cb(lv_draw_unit_t * draw_unit, lv_draw_glyph_dsc_t * glyph_draw_dsc, + lv_draw_fill_dsc_t * fill_draw_dsc, const lv_area_t * fill_area) +{ + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + if(glyph_draw_dsc) { + if(glyph_draw_dsc->bitmap == NULL) { +#if LV_USE_FONT_PLACEHOLDER + /* Draw a placeholder rectangle*/ + lv_draw_border_dsc_t border_draw_dsc; + lv_draw_border_dsc_init(&border_draw_dsc); + border_draw_dsc.opa = glyph_draw_dsc->opa; + border_draw_dsc.color = glyph_draw_dsc->color; + border_draw_dsc.width = 1; + lv_draw_vg_lite_border(draw_unit, &border_draw_dsc, glyph_draw_dsc->bg_coords); +#endif + } + else if(glyph_draw_dsc->format == LV_DRAW_LETTER_BITMAP_FORMAT_A8 + || glyph_draw_dsc->format == LV_DRAW_LETTER_BITMAP_FORMAT_IMAGE) { +#if SUPPORT_OUTLINE_FONT + if(lv_freetype_is_outline_font(glyph_draw_dsc->g->resolved_font)) { + draw_letter_outline(u, glyph_draw_dsc); + } + else +#endif /*SUPPORT_OUTLINE_FONT*/ + { + draw_letter_bitmap(u, glyph_draw_dsc); + } + } + } + + if(fill_draw_dsc && fill_area) { + lv_draw_vg_lite_fill(draw_unit, fill_draw_dsc, fill_area); + } +} + +static void draw_letter_bitmap(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc) +{ + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, u->base_unit.clip_area, dsc->letter_coords)) { + return; + } + + lv_area_t image_area = *dsc->letter_coords; + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + vg_lite_translate(image_area.x1, image_area.y1, &matrix); + + vg_lite_buffer_t src_buf; + lv_vg_lite_buffer_init( + &src_buf, + dsc->bitmap, + lv_area_get_width(&image_area), + lv_area_get_height(&image_area), + VG_LITE_A8, + false); + + vg_lite_color_t color; + 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); + + /* If clipping is not required, blit directly */ + if(_lv_area_is_in(&image_area, u->base_unit.clip_area, false)) { + /* The image area is the coordinates relative to the image itself */ + lv_area_t src_area = image_area; + lv_area_move(&src_area, -image_area.x1, -image_area.y1); + + /* rect is used to crop the pixel-aligned padding area */ + vg_lite_rectangle_t rect; + lv_vg_lite_rect(&rect, &src_area); + LV_VG_LITE_CHECK_ERROR(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, + clip_area.x1, clip_area.y1, + lv_area_get_width(&clip_area), lv_area_get_height(&clip_area), + 0, 0); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + lv_vg_lite_path_end(path); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + vg_lite_matrix_t path_matrix; + vg_lite_identity(&path_matrix); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &path_matrix, + &src_buf, + &matrix, + VG_LITE_BLEND_SRC_OVER, + VG_LITE_PATTERN_COLOR, + color, + color, + VG_LITE_FILTER_LINEAR)); + + lv_vg_lite_path_drop(u, path); + } +} + +#if SUPPORT_OUTLINE_FONT + +static void draw_letter_outline(lv_draw_vg_lite_unit_t * u, const lv_draw_glyph_dsc_t * dsc) +{ + /* get clip area */ + lv_area_t path_clip_area; + if(!_lv_area_intersect(&path_clip_area, u->base_unit.clip_area, dsc->letter_coords)) { + return; + } + + /* 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->bitmap; + lv_point_t pos = {dsc->letter_coords->x1, dsc->letter_coords->y1}; + + /* calc convert matrix */ + float scale = FT_F26DOT6_TO_PATH_SCALE(lv_freetype_outline_get_scale(dsc->g->resolved_font)); + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + /* convert to vg-lite coordinate */ + vg_lite_translate(pos.x - dsc->g->ofs_x, pos.y + dsc->g->box_h + dsc->g->ofs_y, &matrix); + + /* scale size */ + vg_lite_scale(scale, scale, &matrix); + + /* Cartesian coordinates to LCD coordinates */ + lv_vg_lite_matrix_flip_y(&matrix); + + /* calc inverse matrix */ + vg_lite_matrix_t result; + if(!lv_vg_lite_matrix_inverse(&result, &matrix)) { + LV_LOG_ERROR("no inverse matrix"); + return; + } + + 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); + + 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); + + /* Since the font uses Cartesian coordinates, the y coordinates need to be reversed */ + lv_vg_lite_path_set_bonding_box(outline, p1_res.x, p2_res.y, p2_res.x, p1_res.y); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(outline); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, vg_lite_path, VG_LITE_FILL_NON_ZERO, + &matrix, VG_LITE_BLEND_SRC_OVER, lv_vg_lite_color(dsc->color, dsc->opa, true))); +} + +static void vg_lite_outline_push(const lv_freetype_outline_event_param_t * param) +{ + lv_vg_lite_path_t * outline = param->outline; + LV_ASSERT_NULL(outline); + + lv_freetype_outline_type_t type = param->type; + switch(type) { + case LV_FREETYPE_OUTLINE_END: + lv_vg_lite_path_end(outline); + break; + case LV_FREETYPE_OUTLINE_MOVE_TO: + lv_vg_lite_path_move_to(outline, param->to.x, param->to.y); + break; + case LV_FREETYPE_OUTLINE_LINE_TO: + lv_vg_lite_path_line_to(outline, param->to.x, param->to.y); + break; + case LV_FREETYPE_OUTLINE_CUBIC_TO: + lv_vg_lite_path_cubic_to(outline, param->control1.x, param->control1.y, + param->control2.x, param->control2.y, + param->to.x, param->to.y); + break; + case LV_FREETYPE_OUTLINE_CONIC_TO: + lv_vg_lite_path_quad_to(outline, param->control1.x, param->control1.y, + param->to.x, param->to.y); + break; + default: + LV_LOG_ERROR("unknown point type: %d", type); + LV_ASSERT(false); + break; + } +} + +static void freetype_outline_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_freetype_outline_event_param_t * param = lv_event_get_param(e); + switch(code) { + case LV_EVENT_CREATE: + param->outline = lv_vg_lite_path_create(PATH_DATA_COORD_FORMAT); + break; + case LV_EVENT_DELETE: + lv_vg_lite_path_destroy(param->outline); + break; + case LV_EVENT_INSERT: + vg_lite_outline_push(param); + break; + default: + LV_LOG_WARN("unknown event code: %d", code); + break; + } +} + +#endif /*SUPPORT_OUTLINE_FONT*/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_layer.c b/src/draw/vg_lite/lv_draw_vg_lite_layer.c new file mode 100644 index 000000000..fcb908e07 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_layer.c @@ -0,0 +1,72 @@ +/** + * @file lv_draw_vg_lite_layer.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" +#include "lv_draw_vg_lite_type.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, + const lv_area_t * coords) +{ + lv_layer_t * layer = (lv_layer_t *)draw_dsc->src; + + /*It can happen that nothing was draw on a layer and therefore its buffer is not allocated. + *In this case just return. */ + if(layer->buf == NULL) + return; + + lv_image_dsc_t img_dsc; + lv_memzero(&img_dsc, sizeof(lv_image_dsc_t)); + img_dsc.header.w = lv_area_get_width(&layer->buf_area); + img_dsc.header.h = lv_area_get_height(&layer->buf_area); + img_dsc.header.cf = layer->color_format; + img_dsc.header.always_zero = 0; + img_dsc.data = layer->buf; + + lv_draw_image_dsc_t new_draw_dsc = *draw_dsc; + new_draw_dsc.src = &img_dsc; + + lv_draw_vg_lite_img(draw_unit, &new_draw_dsc, coords); + lv_cache_lock(); + lv_cache_invalidate_by_src(&img_dsc, LV_CACHE_SRC_TYPE_POINTER); + lv_cache_unlock(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_line.c b/src/draw/vg_lite/lv_draw_vg_lite_line.c new file mode 100644 index 000000000..6aee9cd75 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_line.c @@ -0,0 +1,211 @@ +/** + * @file lv_draw_vg_lite_line.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_math.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" + +/********************* + * DEFINES + *********************/ + +#define SQ(x) ((x) * (x)) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_line(lv_draw_unit_t * draw_unit, const lv_draw_line_dsc_t * dsc) +{ + if(dsc->opa <= LV_OPA_MIN) + return; + if(dsc->width == 0) + return; + + float p1_x = dsc->p1.x; + float p1_y = dsc->p1.y; + float p2_x = dsc->p2.x; + float p2_y = dsc->p2.y; + + if(p1_x == p2_x && p1_y == p2_y) + return; + + float half_w = dsc->width * 0.5f; + + lv_area_t rel_clip_area; + rel_clip_area.x1 = LV_MIN(p1_x, p2_x) - half_w; + rel_clip_area.x2 = LV_MAX(p1_x, p2_x) + half_w; + rel_clip_area.y1 = LV_MIN(p1_y, p2_y) - half_w; + rel_clip_area.y2 = LV_MAX(p1_y, p2_y) + half_w; + + if(!_lv_area_intersect(&rel_clip_area, &rel_clip_area, draw_unit->clip_area)) { + return; /*Fully clipped, nothing to do*/ + } + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + int32_t dash_width = dsc->dash_width; + int32_t dash_gap = dsc->dash_gap; + int32_t dash_l = dash_width + dash_gap; + + float dx = p2_x - p1_x; + float dy = p2_y - p1_y; + float inv_dl = math_fast_inv_sqrtf(SQ(dx) + SQ(dy)); + float w_dx = dsc->width * dy * inv_dl; + float w_dy = dsc->width * dx * inv_dl; + float w2_dx = w_dx / 2; + float w2_dy = w_dy / 2; + + int32_t ndash = 0; + if(dash_width && dash_l * inv_dl < 1.0f) { + ndash = (1.0f / inv_dl + dash_l - 1) / dash_l; + } + + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_FP32); + lv_vg_lite_path_set_quality(path, VG_LITE_MEDIUM); + lv_vg_lite_path_set_bonding_box_area(path, &rel_clip_area); + + /* head point */ + float head_start_x = p1_x + w2_dx; + float head_start_y = p1_y - w2_dy; + float head_end_x = p1_x - w2_dx; + float head_end_y = p1_y + w2_dy; + + /* tali point */ + float tali_start_x = p2_x - w2_dx; + float tali_start_y = p2_y + w2_dy; + float tali_end_x = p2_x + w2_dx; + float tali_end_y = p2_y - w2_dy; + + /* + head_start tali_end + *-----------------* + /| |\ + / | | \ + arc_c *( *p1 p2* )* arc_c + \ | | / + \| |/ + *-----------------* + head_end tali_start + */ + + /* move to start point */ + lv_vg_lite_path_move_to(path, head_start_x, head_start_y); + + /* draw line head */ + if(dsc->round_start) { + float arc_cx = p1_x - w2_dy; + float arc_cy = p1_y - w2_dx; + + /* start 90deg arc */ + lv_vg_lite_path_append_arc_right_angle(path, + head_start_x, head_start_y, + p1_x, p1_y, + arc_cx, arc_cy); + + /* end 90deg arc */ + lv_vg_lite_path_append_arc_right_angle(path, + arc_cx, arc_cy, + p1_x, p1_y, + head_end_x, head_end_y); + } + else { + lv_vg_lite_path_line_to(path, head_end_x, head_end_y); + } + + /* draw line body */ + lv_vg_lite_path_line_to(path, tali_start_x, tali_start_y); + + /* draw line tail */ + if(dsc->round_end) { + float arc_cx = p2_x + w2_dy; + float arc_cy = p2_y + w2_dx; + lv_vg_lite_path_append_arc_right_angle(path, + tali_start_x, tali_start_y, + p2_x, p2_y, + arc_cx, arc_cy); + lv_vg_lite_path_append_arc_right_angle(path, + arc_cx, arc_cy, + p2_x, p2_y, + tali_end_x, tali_end_y); + } + else { + lv_vg_lite_path_line_to(path, tali_end_x, tali_end_y); + } + + /* close draw line body */ + lv_vg_lite_path_line_to(path, head_start_x, head_start_y); + + for(int32_t i = 0; i < ndash; i++) { + float start_x = p1_x - w2_dx + dx * (i * dash_l + dash_width) * inv_dl; + float start_y = p1_y + w2_dy + dy * (i * dash_l + dash_width) * inv_dl; + + lv_vg_lite_path_move_to(path, start_x, start_y); + lv_vg_lite_path_line_to(path, + p1_x + w2_dx + dx * (i * dash_l + dash_width) * inv_dl, + p1_y - w2_dy + dy * (i * dash_l + dash_width) * inv_dl); + lv_vg_lite_path_line_to(path, + p1_x + w2_dx + dx * (i + 1) * dash_l * inv_dl, + p1_y - w2_dy + dy * (i + 1) * dash_l * inv_dl); + lv_vg_lite_path_line_to(path, + p1_x - w2_dx + dx * (i + 1) * dash_l * inv_dl, + p1_y + w2_dy + dy * (i + 1) * dash_l * inv_dl); + lv_vg_lite_path_line_to(path, start_x, start_y); + } + + lv_vg_lite_path_end(path); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &matrix, + VG_LITE_BLEND_SRC_OVER, + color)); + + lv_vg_lite_path_drop(u, path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c b/src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c new file mode 100644 index 000000000..89f9bd777 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c @@ -0,0 +1,94 @@ +/** + * @file lv_draw_vg_lite_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" +#include "lv_draw_vg_lite_type.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_mask_rect(lv_draw_unit_t * draw_unit, const lv_draw_mask_rect_dsc_t * dsc, + const lv_area_t * coords) +{ + LV_UNUSED(coords); + + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &dsc->area, draw_unit->clip_area)) { + return; + } + + lv_draw_sw_mask_radius_param_t param; + lv_draw_sw_mask_radius_init(¶m, &dsc->area, dsc->radius, false); + + void * masks[2] = {0}; + masks[0] = ¶m; + + uint32_t area_w = lv_area_get_width(&draw_area); + lv_opa_t * mask_buf = lv_malloc(area_w); + + int32_t y; + for(y = draw_area.y1; y <= draw_area.y2; y++) { + lv_memset(mask_buf, 0xff, area_w); + lv_draw_sw_mask_res_t res = lv_draw_sw_mask_apply(masks, mask_buf, draw_area.x1, y, area_w); + if(res == LV_DRAW_SW_MASK_RES_FULL_COVER) continue; + + lv_layer_t * target_layer = draw_unit->target_layer; + lv_color32_t * c32_buf = lv_draw_layer_go_to_xy(target_layer, draw_area.x1 - target_layer->buf_area.x1, + y - target_layer->buf_area.y1); + + if(res == LV_DRAW_SW_MASK_RES_TRANSP) { + uint32_t i; + for(i = 0; i < area_w; i++) { + c32_buf[i].alpha = 0x00; + } + } + else { + uint32_t i; + for(i = 0; i < area_w; i++) { + if(mask_buf[i] != LV_OPA_COVER) { + c32_buf[i].alpha = LV_OPA_MIX2(c32_buf[i].alpha, mask_buf[i]); + } + } + } + } + + lv_free(mask_buf); + lv_draw_sw_mask_free_param(¶m); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_triangle.c b/src/draw/vg_lite/lv_draw_vg_lite_triangle.c new file mode 100644 index 000000000..6dd90d7c4 --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_triangle.c @@ -0,0 +1,104 @@ +/** + * @file lv_draw_vg_lite_triangle.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" +#include "lv_vg_lite_path.h" +#include "lv_draw_vg_lite_type.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_triangle(lv_draw_unit_t * draw_unit, const lv_draw_triangle_dsc_t * dsc) +{ + if(dsc->bg_opa <= LV_OPA_MIN) return; + + lv_area_t tri_area; + tri_area.x1 = (int32_t)LV_MIN3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x); + tri_area.y1 = (int32_t)LV_MIN3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y); + tri_area.x2 = (int32_t)LV_MAX3(dsc->p[0].x, dsc->p[1].x, dsc->p[2].x); + tri_area.y2 = (int32_t)LV_MAX3(dsc->p[0].y, dsc->p[1].y, dsc->p[2].y); + + bool is_common; + lv_area_t clip_area; + is_common = _lv_area_intersect(&clip_area, &tri_area, draw_unit->clip_area); + if(!is_common) return; + + lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit; + + lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_FP32); + lv_vg_lite_path_set_bonding_box_area(path, &clip_area); + lv_vg_lite_path_move_to(path, dsc->p[0].x, dsc->p[0].y); + lv_vg_lite_path_line_to(path, dsc->p[1].x, dsc->p[1].y); + lv_vg_lite_path_line_to(path, dsc->p[2].x, dsc->p[2].y); + lv_vg_lite_path_close(path); + lv_vg_lite_path_end(path); + + vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path); + + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + LV_VG_LITE_ASSERT_PATH(vg_lite_path); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + lv_vg_lite_matrix_multiply(&matrix, &u->global_matrix); + + if(dsc->bg_grad.dir != LV_GRAD_DIR_NONE) { + lv_vg_lite_draw_linear_grad( + &u->target_buffer, + vg_lite_path, + &tri_area, + &dsc->bg_grad, + &matrix, + VG_LITE_FILL_EVEN_ODD, + VG_LITE_BLEND_SRC_OVER); + } + else { /* normal fill */ + vg_lite_color_t color = lv_vg_lite_color(dsc->bg_color, dsc->bg_opa, true); + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_lite_path, + VG_LITE_FILL_EVEN_ODD, + &matrix, + VG_LITE_BLEND_SRC_OVER, + color)); + } + + lv_vg_lite_path_drop(u, path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/src/draw/vg_lite/lv_draw_vg_lite_type.h b/src/draw/vg_lite/lv_draw_vg_lite_type.h new file mode 100644 index 000000000..7b594c2db --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_type.h @@ -0,0 +1,55 @@ +/** + * @file lv_draw_vg_lite_type.h + * + */ + +#ifndef LV_DRAW_VG_LITE_TYPE_H +#define LV_DRAW_VG_LITE_TYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_DRAW_VG_LITE + +#include "../lv_draw.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_draw_vg_lite_unit_t { + lv_draw_unit_t base_unit; + struct _lv_draw_task_t * task_act; + vg_lite_buffer_t target_buffer; + vg_lite_matrix_t global_matrix; + lv_ll_t path_free_ll; + int path_max_cnt; +} lv_draw_vg_lite_unit_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VG_LITE_DRAW_H*/ diff --git a/src/draw/vg_lite/lv_draw_vg_lite_vector.c b/src/draw/vg_lite/lv_draw_vg_lite_vector.c new file mode 100644 index 000000000..93138127a --- /dev/null +++ b/src/draw/vg_lite/lv_draw_vg_lite_vector.c @@ -0,0 +1,317 @@ +/** + * @file lv_draw_vg_lite_vector.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vg_lite.h" + +#if LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_path.h" +#include "lv_vg_lite_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc); +static void lv_matrix_to_vg(vg_lite_matrix_t * desy, const lv_matrix_t * src); +static void lv_path_to_vg(lv_vg_lite_path_t * dest, const lv_vector_path_t * src); +static vg_lite_blend_t lv_blend_to_vg(lv_vector_blend_t blend); +static vg_lite_fill_t lv_fill_to_vg(lv_vector_fill_t fill_rule); +static vg_lite_gradient_spreadmode_t lv_spread_to_vg(lv_vector_gradient_spread_t spread); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_vg_lite_vector(lv_draw_unit_t * draw_unit, const lv_draw_vector_task_dsc_t * dsc) +{ + if(dsc->task_list == NULL) + return; + + lv_layer_t * layer = dsc->base.layer; + if(layer->buf == NULL) + return; + + _lv_vector_for_each_destroy_tasks(dsc->task_list, task_draw_cb, draw_unit); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static vg_lite_color_t lv_color32_to_vg(lv_color32_t color, lv_opa_t opa) +{ + uint8_t a = LV_OPA_MIX2(color.alpha, opa); + if(a < LV_OPA_MAX) { + color.red = LV_UDIV255(color.red * opa); + color.green = LV_UDIV255(color.green * opa); + color.blue = LV_UDIV255(color.blue * opa); + } + return (uint32_t)a << 24 | (uint32_t)color.blue << 16 | (uint32_t)color.green << 8 | color.red; +} + +static void task_draw_cb(void * ctx, const lv_vector_path_t * path, const lv_vector_draw_dsc_t * dsc) +{ + lv_draw_vg_lite_unit_t * u = ctx; + LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer); + + /* clear area */ + if(!path) { + /* clear color needs to ignore fill_dsc.opa */ + vg_lite_color_t c = lv_color32_to_vg(dsc->fill_dsc.color, LV_OPA_COVER); + vg_lite_rectangle_t rect; + lv_vg_lite_rect(&rect, &dsc->scissor_area); + LV_VG_LITE_CHECK_ERROR(vg_lite_clear(&u->target_buffer, &rect, c)); + return; + } + + /* set scissor area */ + lv_vg_lite_set_scissor_area(&dsc->scissor_area); + + /* convert color */ + vg_lite_color_t vg_color = lv_color32_to_vg(dsc->fill_dsc.color, dsc->fill_dsc.opa); + + /* transform matrix */ + vg_lite_matrix_t matrix; + lv_matrix_to_vg(&matrix, &dsc->matrix); + + /* convert path */ + lv_vg_lite_path_t * lv_vg_path = lv_vg_lite_path_get(u, VG_LITE_FP32); + lv_path_to_vg(lv_vg_path, path); + vg_lite_path_t * vg_path = lv_vg_lite_path_get_path(lv_vg_path); + LV_VG_LITE_ASSERT_PATH(vg_path); + + /* convert blend mode and fill rule */ + vg_lite_blend_t blend = lv_blend_to_vg(dsc->blend_mode); + vg_lite_fill_t fill = lv_fill_to_vg(dsc->fill_dsc.fill_rule); + + /* get path bounds */ + float min_x, min_y, max_x, max_y; + lv_vg_lite_path_get_bonding_box(lv_vg_path, &min_x, &min_y, &max_x, &max_y); + + switch(dsc->fill_dsc.style) { + case LV_VECTOR_DRAW_STYLE_SOLID: { + /* normal draw shape */ + LV_VG_LITE_CHECK_ERROR(vg_lite_draw( + &u->target_buffer, + vg_path, + fill, + &matrix, + blend, + vg_color)); + } + break; + case LV_VECTOR_DRAW_STYLE_PATTERN: { + /* draw image */ + vg_lite_buffer_t image_buffer; + lv_image_decoder_dsc_t decoder_dsc; + if(lv_vg_lite_buffer_open_image(&image_buffer, &decoder_dsc, dsc->fill_dsc.img_dsc.src)) { + lv_matrix_t m = dsc->matrix; + lv_matrix_translate(&m, min_x, min_y); + lv_matrix_multiply(&m, &dsc->fill_dsc.matrix); + + vg_lite_matrix_t src_matrix; + lv_matrix_to_vg(&src_matrix, &m); + + vg_lite_matrix_t path_matrix; + vg_lite_identity(&path_matrix); + + vg_lite_color_t recolor = lv_vg_lite_color(dsc->fill_dsc.img_dsc.recolor, dsc->fill_dsc.img_dsc.recolor_opa, true); + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw_pattern( + &u->target_buffer, + vg_path, + fill, + &path_matrix, + &image_buffer, + &src_matrix, + blend, + VG_LITE_PATTERN_COLOR, + recolor, + vg_color, + VG_LITE_FILTER_BI_LINEAR)); + lv_image_decoder_close(&decoder_dsc); + } + } + break; + case LV_VECTOR_DRAW_STYLE_GRADIENT: { + /* draw gradient */ + lv_area_t grad_area; + lv_area_set(&grad_area, min_x, min_y, max_x, max_y); + lv_vector_gradient_style_t style = dsc->fill_dsc.gradient.style; + vg_lite_gradient_spreadmode_t spreadmode = lv_spread_to_vg(dsc->fill_dsc.gradient.spread); + LV_UNUSED(spreadmode); + + if(style == LV_VECTOR_GRADIENT_STYLE_LINEAR) { + lv_vg_lite_draw_linear_grad( + &u->target_buffer, + vg_path, + &grad_area, + &dsc->fill_dsc.gradient.grad, + &matrix, + fill, + blend); + } + else if(style == LV_VECTOR_GRADIENT_STYLE_RADIAL) { + if(vg_lite_query_feature(gcFEATURE_BIT_VG_RADIAL_GRADIENT)) { + /* TODO: radial gradient */ + } + else { + LV_LOG_WARN("radial gradient is not supported"); + } + } + } + break; + default: + LV_LOG_WARN("unknown style: %d", dsc->fill_dsc.style); + break; + } + + /* drop path */ + lv_vg_lite_path_drop(u, lv_vg_path); + + /* disable scissor */ + lv_vg_lite_disable_scissor(); +} + +static void lv_matrix_to_vg(vg_lite_matrix_t * dest, const lv_matrix_t * src) +{ + lv_memcpy(dest, src, sizeof(lv_matrix_t)); +} + +static vg_lite_quality_t lv_quality_to_vg(lv_vector_path_quality_t quality) +{ + switch(quality) { + case LV_VECTOR_PATH_QUALITY_LOW: + return VG_LITE_LOW; + case LV_VECTOR_PATH_QUALITY_MEDIUM: + return VG_LITE_MEDIUM; + case LV_VECTOR_PATH_QUALITY_HIGH: + return VG_LITE_HIGH; + default: + return VG_LITE_MEDIUM; + } +} + +static void lv_path_to_vg(lv_vg_lite_path_t * dest, const lv_vector_path_t * src) +{ + lv_vg_lite_path_set_quality(dest, lv_quality_to_vg(src->quality)); + + uint32_t pidx = 0; + for(uint32_t i = 0; i < src->ops.size; i++) { + lv_vector_path_op_t * op = LV_ARRAY_GET(&src->ops, i, uint8_t); + switch(*op) { + case LV_VECTOR_PATH_OP_MOVE_TO: { + lv_fpoint_t * pt = LV_ARRAY_GET(&src->points, pidx, lv_fpoint_t); + lv_vg_lite_path_move_to(dest, pt->x, pt->y); + pidx += 1; + } + break; + case LV_VECTOR_PATH_OP_LINE_TO: { + lv_fpoint_t * pt = LV_ARRAY_GET(&src->points, pidx, lv_fpoint_t); + lv_vg_lite_path_line_to(dest, pt->x, pt->y); + pidx += 1; + } + break; + case LV_VECTOR_PATH_OP_QUAD_TO: { + lv_fpoint_t * pt1 = LV_ARRAY_GET(&src->points, pidx, lv_fpoint_t); + lv_fpoint_t * pt2 = LV_ARRAY_GET(&src->points, pidx + 1, lv_fpoint_t); + lv_vg_lite_path_quad_to(dest, pt1->x, pt1->y, pt2->x, pt2->y); + pidx += 2; + } + break; + case LV_VECTOR_PATH_OP_CUBIC_TO: { + lv_fpoint_t * pt1 = LV_ARRAY_GET(&src->points, pidx, lv_fpoint_t); + lv_fpoint_t * pt2 = LV_ARRAY_GET(&src->points, pidx + 1, lv_fpoint_t); + lv_fpoint_t * pt3 = LV_ARRAY_GET(&src->points, pidx + 2, lv_fpoint_t); + lv_vg_lite_path_cubic_to(dest, pt1->x, pt1->y, pt2->x, pt2->y, pt3->x, pt3->y); + pidx += 3; + } + break; + case LV_VECTOR_PATH_OP_CLOSE: { + lv_vg_lite_path_close(dest); + } + break; + } + } + + lv_vg_lite_path_end(dest); + lv_vg_lite_path_update_bonding_box(dest); +} + +static vg_lite_blend_t lv_blend_to_vg(lv_vector_blend_t blend) +{ + switch(blend) { + case LV_VECTOR_BLEND_SRC_OVER: + return VG_LITE_BLEND_SRC_OVER; + case LV_VECTOR_BLEND_SCREEN: + return VG_LITE_BLEND_SCREEN; + case LV_VECTOR_BLEND_MULTIPLY: + return VG_LITE_BLEND_MULTIPLY; + case LV_VECTOR_BLEND_NONE: + return VG_LITE_BLEND_NONE; + case LV_VECTOR_BLEND_ADDITIVE: + return VG_LITE_BLEND_ADDITIVE; + case LV_VECTOR_BLEND_SRC_IN: + return VG_LITE_BLEND_SRC_IN; + case LV_VECTOR_BLEND_DST_OVER: + return VG_LITE_BLEND_DST_OVER; + case LV_VECTOR_BLEND_DST_IN: + return VG_LITE_BLEND_DST_IN; + case LV_VECTOR_BLEND_SUBTRACTIVE: + return VG_LITE_BLEND_SUBTRACT; + default: + return VG_LITE_BLEND_SRC_OVER; + } +} + +static vg_lite_fill_t lv_fill_to_vg(lv_vector_fill_t fill_rule) +{ + switch(fill_rule) { + case LV_VECTOR_FILL_NONZERO: + return VG_LITE_FILL_NON_ZERO; + case LV_VECTOR_FILL_EVENODD: + return VG_LITE_FILL_EVEN_ODD; + default: + return VG_LITE_FILL_NON_ZERO; + } +} + +static vg_lite_gradient_spreadmode_t lv_spread_to_vg(lv_vector_gradient_spread_t spread) +{ + switch(spread) { + case LV_VECTOR_GRADIENT_SPREAD_PAD: + return VG_LITE_GRADIENT_SPREAD_PAD; + case LV_VECTOR_GRADIENT_SPREAD_REPEAT: + return VG_LITE_GRADIENT_SPREAD_REPEAT; + case LV_VECTOR_GRADIENT_SPREAD_REFLECT: + return VG_LITE_GRADIENT_SPREAD_REFLECT; + default: + return VG_LITE_GRADIENT_SPREAD_FILL; + } +} + +#endif /*LV_USE_DRAW_VG_LITE && LV_USE_VECTOR_GRAPHIC*/ diff --git a/src/draw/vg_lite/lv_vg_lite_decoder.c b/src/draw/vg_lite/lv_vg_lite_decoder.c new file mode 100644 index 000000000..5d9b05223 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_decoder.c @@ -0,0 +1,550 @@ +/** + * @file lv_vg_lite_decoder.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_vg_lite_decoder.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +#pragma pack(1) + +typedef struct { + lv_color16_t c; + uint8_t alpha; +} lv_color16_alpha_t; + +#pragma pack() + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header); +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, + const lv_image_decoder_args_t * args); +static void decode_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static void image_try_self_pre_mul(lv_image_decoder_dsc_t * dsc); +static void image_color32_pre_mul(lv_color32_t * img_data, uint32_t px_size); +static void image_color16_pre_mul(lv_color16_alpha_t * img_data, uint32_t px_size); +static void image_copy(uint8_t * dest, const uint8_t * src, + size_t dest_stride, size_t src_stride, + uint32_t height); +static void image_invalidate_cache(void * buf, uint32_t stride, + uint32_t width, uint32_t height, + lv_color_format_t cf); +static void cache_invalidate_cb(lv_cache_entry_t * entry); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_vg_lite_decoder_init(void) +{ + lv_image_decoder_t * decoder = lv_image_decoder_create(); + lv_image_decoder_set_info_cb(decoder, decoder_info); + lv_image_decoder_set_open_cb(decoder, decoder_open); + lv_image_decoder_set_close_cb(decoder, decode_close); + decoder->cache_data_type = lv_cache_register_data_type(); +} + +void lv_vg_lite_decoder_deinit(void) +{ + lv_image_decoder_t * dec = NULL; + while((dec = lv_image_decoder_get_next(dec)) != NULL) { + if(dec->info_cb == decoder_info) { + lv_image_decoder_delete(dec); + break; + } + } +} + +lv_result_t lv_vg_lite_decoder_post_process(lv_image_decoder_dsc_t * dsc) +{ + lv_color_format_t color_format = dsc->header.cf; + lv_result_t res = LV_RESULT_OK; + + /* Only support this color format */ + switch(color_format) { + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + case LV_COLOR_FORMAT_RGB565A8: + case LV_COLOR_FORMAT_RGB888: + case LV_COLOR_FORMAT_RGB565: + break; + + default: + return LV_RESULT_OK; + } + + lv_cache_entry_t * entry = dsc->cache_entry; + if(!entry) { + LV_LOG_WARN("No detected cache entry, src_type: %d, src: %p", + dsc->src_type, dsc->src); + return LV_RESULT_INVALID; + } + + lv_cache_lock(); + + /* Check if the image is aligned */ + if(!(entry->process_state & LV_VG_LITE_IMAGE_FLAG_ALIGNED)) { + int32_t image_w = dsc->header.w; + int32_t image_h = dsc->header.h; + uint32_t width_byte = image_w * lv_color_format_get_size(color_format); + uint32_t stride = lv_draw_buf_width_to_stride(image_w, color_format); + + const uint8_t * ori_image = lv_cache_get_data(entry); + + /* Check stride alignment requirements */ + bool is_aligned = (stride == width_byte) + && (ori_image == lv_draw_buf_align((void *)ori_image, color_format)); + + if(!is_aligned) { + /* alloc new image */ + size_t size_bytes = stride * dsc->header.h; + uint8_t * new_image = lv_draw_buf_malloc(size_bytes, color_format); + if(!new_image) { + LV_LOG_ERROR("alloc %zu failed, cf = %d", size_bytes, color_format); + res = LV_RESULT_INVALID; + goto alloc_failed; + } + + /* Replace the image data pointer */ + entry->data = new_image; + dsc->img_data = new_image; + + /* Copy image data */ + image_copy(new_image, ori_image, stride, width_byte, image_h); + + /* invalidate D-Cache */ + image_invalidate_cache(new_image, stride, image_w, image_h, color_format); + + /* free memory for old image */ + lv_draw_buf_free((void *)ori_image); + } + else { + LV_LOG_INFO("no need to realign stride: %" LV_PRIu32, stride); + } + + entry->process_state |= LV_VG_LITE_IMAGE_FLAG_ALIGNED; + } + + /* Since image_try_self_pre_mul requires width alignment, + * premul alpha is placed after the image width alignment process. + */ + image_try_self_pre_mul(dsc); + +alloc_failed: + lv_cache_unlock(); + + return res; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) +{ + lv_cache_lock(); + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + const char * fn = dsc->src; + + lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, fn, LV_CACHE_SRC_TYPE_PATH); + if(cache) { + dsc->img_data = lv_cache_get_data(cache); + dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/ + lv_cache_unlock(); + return LV_RESULT_OK; + } + } + + if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) { + lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, dsc->src, LV_CACHE_SRC_TYPE_POINTER); + if(cache) { + dsc->img_data = lv_cache_get_data(cache); + dsc->cache_entry = cache; + lv_cache_unlock(); + return LV_RESULT_OK; + } + } + + lv_cache_unlock(); + return LV_RESULT_INVALID; +} + +static void image_color32_pre_mul(lv_color32_t * img_data, uint32_t px_size) +{ + while(px_size--) { + img_data->red = LV_UDIV255(img_data->red * img_data->alpha); + img_data->green = LV_UDIV255(img_data->green * img_data->alpha); + img_data->blue = LV_UDIV255(img_data->blue * img_data->alpha); + img_data++; + } +} + +static void image_color16_pre_mul(lv_color16_alpha_t * img_data, uint32_t px_size) +{ + while(px_size--) { + img_data->c.red = LV_UDIV255(img_data->c.red * img_data->alpha); + img_data->c.green = LV_UDIV255(img_data->c.green * img_data->alpha); + img_data->c.blue = LV_UDIV255(img_data->c.blue * img_data->alpha); + img_data++; + } +} + +static void image_try_self_pre_mul(lv_image_decoder_dsc_t * dsc) +{ + /* !!! WARNING !!! + * self-premultiplied images + * should be width-aligned and in modifiable RAM + */ + if(lv_vg_lite_support_blend_normal()) { + return; + } + + if(dsc->cache_entry->process_state & LV_VG_LITE_IMAGE_FLAG_PRE_MUL) { + return; + } + + lv_color_format_t cf = dsc->header.cf; + if(!lv_color_format_has_alpha(cf)) { + return; + } + + int32_t image_w = dsc->header.w; + int32_t image_h = dsc->header.h; + uint32_t stride = lv_draw_buf_width_to_stride(image_w, cf); + uint32_t aligned_w = lv_vg_lite_width_align(image_w); + size_t px_size = aligned_w * image_h; + + if(cf == LV_COLOR_FORMAT_ARGB8888) { + image_color32_pre_mul((lv_color32_t *)dsc->img_data, px_size); + } + else if(cf == LV_COLOR_FORMAT_RGB565A8) { + image_color16_pre_mul((lv_color16_alpha_t *)dsc->img_data, px_size); + } + else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) { + lv_color32_t * palette = (lv_color32_t *)dsc->img_data; + uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); + image_color32_pre_mul(palette, palette_size); + } + else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) { + /* do nothing */ + } + else { + LV_LOG_WARN("unsupported cf: %d", cf); + } + + image_invalidate_cache((void *)dsc->img_data, stride, image_w, image_h, cf); + + dsc->cache_entry->process_state |= LV_VG_LITE_IMAGE_FLAG_PRE_MUL; +} + +static void image_copy(uint8_t * dest, const uint8_t * src, + size_t dest_stride, size_t src_stride, + uint32_t height) +{ + for(uint32_t y = 0; y < height; y++) { + lv_memcpy(dest, src, src_stride); + src += src_stride; + dest += dest_stride; + } +} + +static void image_invalidate_cache(void * buf, uint32_t stride, + uint32_t width, uint32_t height, + lv_color_format_t cf) +{ + width = lv_vg_lite_width_align(width); + lv_area_t image_area; + lv_area_set(&image_area, 0, 0, width - 1, height - 1); + lv_draw_buf_invalidate_cache(buf, stride, cf, &image_area); +} + +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header) +{ + return lv_bin_decoder_info(decoder, src, header); +} + +static lv_result_t decoder_open_variable(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_color_format_t cf = dsc->header.cf; + int32_t width = dsc->header.w; + int32_t height = dsc->header.h; + + /* native stride */ + uint32_t width_byte; + width_byte = width * lv_color_format_get_bpp(cf); + width_byte = (width_byte + 7) >> 3; /*Round up*/ + + bool support_blend_normal = lv_vg_lite_support_blend_normal(); + + uint32_t start = lv_tick_get(); + + /*In case of uncompressed formats the image stored in the ROM/RAM. + *So simply give its pointer*/ + const uint8_t * image_data = ((lv_image_dsc_t *)dsc->src)->data; + + bool has_alpha = lv_color_format_has_alpha(cf); + bool is_indexed = LV_COLOR_FORMAT_IS_INDEXED(cf); + bool is_addr_aligned = (image_data == lv_draw_buf_align((void *)image_data, cf)) ? true : false; + + uint32_t stride = lv_draw_buf_width_to_stride(width, cf); + bool is_stride_aligned = (stride == width_byte) ? true : false; + + /* When the following conditions are met, + * there is no need to copy image resource preprocessing. + */ + if(is_addr_aligned + && is_stride_aligned + && !is_indexed + && (!has_alpha || (has_alpha && support_blend_normal))) { + + /*add cache*/ + lv_cache_lock(); + lv_cache_entry_t * cache = lv_cache_add(image_data, 0, decoder->cache_data_type, 1); + cache->process_state = LV_VG_LITE_IMAGE_FLAG_ALIGNED; + cache->weight = lv_tick_elaps(start); + cache->src_type = LV_CACHE_SRC_TYPE_POINTER; + cache->src = dsc->src; + + dsc->cache_entry = cache; + dsc->img_data = lv_cache_get_data(cache); + lv_cache_unlock(); + return LV_RESULT_OK; + } + + uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); + uint32_t palette_size_bytes = palette_size * sizeof(lv_color32_t); + + /* Since the palette and index image are next to each other, + * the palette size needs to be aligned to ensure that the image is aligned. + */ + uint32_t palette_size_bytes_aligned = LV_VG_LITE_ALIGN(palette_size_bytes, LV_VG_LITE_BUF_ALIGN); + + size_t image_size = height * stride + palette_size_bytes_aligned; + + void * image_buf = lv_draw_buf_malloc(image_size, cf); + if(image_buf == NULL) { + LV_LOG_ERROR("alloc %zu failed, cf = %d", image_size, cf); + return LV_RESULT_INVALID; + } + + const uint8_t * src = image_data; + uint8_t * dest = image_buf; + + /* copy palette */ + if(palette_size_bytes) { + lv_memcpy(dest, src, palette_size_bytes); + src += palette_size_bytes; + + /* move to align size */ + dest += palette_size_bytes_aligned; + } + + image_copy(dest, src, stride, width_byte, height); + + lv_cache_lock(); + lv_cache_entry_t * cache = lv_cache_add(image_buf, 0, decoder->cache_data_type, image_size); + + dsc->img_data = lv_cache_get_data(cache); + dsc->cache_entry = cache; + + /* premul alpha */ + image_try_self_pre_mul(dsc); + + /* invalidate D-Cache */ + image_invalidate_cache(image_buf, stride, width, height, cf); + + cache->process_state |= LV_VG_LITE_IMAGE_FLAG_ALLOCED | LV_VG_LITE_IMAGE_FLAG_ALIGNED; + cache->weight = lv_tick_elaps(start); + cache->src_type = LV_CACHE_SRC_TYPE_POINTER; + cache->src = dsc->src; + lv_cache_unlock(); + + LV_LOG_INFO("image %p (W%" LV_PRId32 " x H%" LV_PRId32 ", buffer: %p, cf: %d) decode finish %" LV_PRIu32 "ms", + image_data, width, height, image_buf, cf, cache->weight); + return LV_RESULT_OK; +} + +static lv_result_t decoder_open_file(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_color_format_t cf = dsc->header.cf; + int32_t width = dsc->header.w; + int32_t height = dsc->header.h; + const char * path = dsc->src; + + uint32_t start = lv_tick_get(); + + lv_fs_file_t file; + lv_fs_res_t res = lv_fs_open(&file, path, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + LV_LOG_ERROR("open %s failed", path); + return LV_RESULT_INVALID; + } + + /* skip image header bytes */ + res = lv_fs_seek(&file, sizeof(lv_image_header_t), LV_FS_SEEK_SET); + if(res != LV_FS_RES_OK) { + LV_LOG_ERROR("seek %s lv_image_header_t failed", path); + lv_fs_close(&file); + return LV_RESULT_INVALID; + } + + /* native stride */ + uint32_t width_byte; + width_byte = width * lv_color_format_get_bpp(cf); + width_byte = (width_byte + 7) >> 3; /*Round up*/ + + bool support_blend_normal = lv_vg_lite_support_blend_normal(); + + uint32_t stride = lv_draw_buf_width_to_stride(width, cf); + + uint32_t palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf); + uint32_t palette_size_bytes = palette_size * sizeof(lv_color32_t); + + /* Since the palette and index image are next to each other, + * the palette size needs to be aligned to ensure that the image is aligned. + */ + uint32_t palette_size_bytes_aligned = LV_VG_LITE_ALIGN(palette_size_bytes, LV_VG_LITE_BUF_ALIGN); + + size_t image_size = height * stride + palette_size_bytes_aligned; + + void * image_buf = lv_draw_buf_malloc(image_size, cf); + if(image_buf == NULL) { + LV_LOG_ERROR("alloc %zu failed, cf = %d", image_size, cf); + goto failed; + } + + uint8_t * dest = image_buf; + + /* copy palette */ + if(palette_size_bytes) { + uint32_t br; + /* read palette */ + res = lv_fs_read(&file, dest, palette_size_bytes, &br); + if(res != LV_FS_RES_OK || br != palette_size_bytes) { + LV_LOG_ERROR("read %s (palette: %" LV_PRIu32 ", br: %" LV_PRIu32 ") failed", + path, palette_size_bytes, br); + goto failed; + } + + if(!support_blend_normal) { + image_color32_pre_mul((lv_color32_t *)image_buf, palette_size); + } + + /* move to index image map */ + dest += palette_size_bytes_aligned; + } + + for(uint32_t y = 0; y < height; y++) { + uint32_t br; + res = lv_fs_read(&file, dest, width_byte, &br); + if(res != LV_FS_RES_OK || br != width_byte) { + LV_LOG_ERROR("read %s (y: %" LV_PRIu32 ", width_byte: %" LV_PRIu32 ", br: %" LV_PRIu32 ") failed", + path, y, width_byte, br); + goto failed; + } + + dest += stride; + } + + lv_fs_close(&file); + + lv_cache_lock(); + lv_cache_entry_t * cache = lv_cache_add(image_buf, 0, decoder->cache_data_type, image_size); + + dsc->cache_entry = cache; + dsc->img_data = lv_cache_get_data(cache); + + /* premul alpha */ + image_try_self_pre_mul(dsc); + + /* invalidate D-Cache */ + image_invalidate_cache(image_buf, stride, width, height, cf); + + cache->process_state |= LV_VG_LITE_IMAGE_FLAG_ALLOCED | LV_VG_LITE_IMAGE_FLAG_ALIGNED; + cache->weight = lv_tick_elaps(start); + cache->invalidate_cb = cache_invalidate_cb; + cache->src = lv_strdup(dsc->src); + cache->src_type = LV_CACHE_SRC_TYPE_PATH; + lv_cache_unlock(); + + LV_LOG_INFO("image %s (W%" LV_PRId32 " x H%" LV_PRId32 ", buffer: %p cf: %d) decode finish %" LV_PRIu32 "ms", + path, width, height, image_buf, cf, cache->weight); + return LV_RESULT_OK; + +failed: + lv_fs_close(&file); + + if(image_buf) { + LV_LOG_INFO("free image_buf: %p", image_buf); + lv_draw_buf_free(image_buf); + } + + return LV_RESULT_INVALID; +} + +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, + const lv_image_decoder_args_t * args) +{ + LV_UNUSED(args); /*Unused*/ + + /*Check the cache first*/ + if(try_cache(dsc) == LV_RESULT_OK) { + return LV_RESULT_OK; + } + + switch(dsc->src_type) { + case LV_IMAGE_SRC_VARIABLE: + return decoder_open_variable(decoder, dsc); + case LV_IMAGE_SRC_FILE: + return decoder_open_file(decoder, dsc); + } + + return LV_RESULT_INVALID; +} + +static void decode_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + lv_cache_lock(); + lv_cache_release(dsc->cache_entry); + lv_cache_unlock(); +} + +static void cache_invalidate_cb(lv_cache_entry_t * entry) +{ + if(entry->src_type == LV_CACHE_SRC_TYPE_PATH) lv_free((void *)entry->src); + lv_draw_buf_free((void *)entry->data); +} + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_vg_lite_decoder.h b/src/draw/vg_lite/lv_vg_lite_decoder.h new file mode 100644 index 000000000..fa7ec98d7 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_decoder.h @@ -0,0 +1,49 @@ +/** + * @file lv_vg_lite_decoder.h + * + */ + +#ifndef LV_VG_LITE_DECODER_H +#define LV_VG_LITE_DECODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../lv_image_decoder.h" + +#if LV_USE_DRAW_VG_LITE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_vg_lite_decoder_init(void); + +void lv_vg_lite_decoder_deinit(void); + +lv_result_t lv_vg_lite_decoder_post_process(lv_image_decoder_dsc_t * dsc); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VG_LITE_DECODER_H*/ diff --git a/src/draw/vg_lite/lv_vg_lite_math.c b/src/draw/vg_lite/lv_vg_lite_math.c new file mode 100644 index 000000000..08b180bf8 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_math.c @@ -0,0 +1,60 @@ +/** + * @file lv_vg_lite_math.h + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_vg_lite_math.h" + +#if LV_USE_DRAW_VG_LITE + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +float math_fast_inv_sqrtf(float number) +{ + int32_t i; + float x2, y; + const float threehalfs = 1.5f; + + x2 = number * 0.5f; + y = number; + i = *(int32_t *)&y; /* evil floating point bit level hacking */ + i = 0x5f3759df - (i >> 1); /* what the fuck? */ + y = *(float *)&i; + y = y * (threehalfs - (x2 * y * y)); /* 1st iteration */ + + return y; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_vg_lite_math.h b/src/draw/vg_lite/lv_vg_lite_math.h new file mode 100644 index 000000000..698f60685 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_math.h @@ -0,0 +1,75 @@ +/** + * @file lv_vg_lite_math.h + * + */ + +#ifndef LV_VG_LITE_MATH_H +#define LV_VG_LITE_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" + +#if LV_USE_DRAW_VG_LITE + +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +#define MATH_PI 3.14159265358979323846f +#define MATH_HALF_PI 1.57079632679489661923f +#define MATH_TWO_PI 6.28318530717958647692f +#define DEG_TO_RAD 0.017453292519943295769236907684886 +#define RAD_TO_DEG 57.295779513082320876798154814105 + +#define MATH_TANF(x) tanf(x) +#define MATH_SINF(x) sinf(x) +#define MATH_COSF(x) cosf(x) +#define MATH_ASINF(x) asinf(x) +#define MATH_FABSF(x) fabsf(x) +#define MATH_SQRTF(x) sqrtf(x) + +#define MATH_RADIANS(deg) ((deg) * DEG_TO_RAD) +#define MATD_DEGRESS(rad) ((rad) * RAD_TO_DEG) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +static inline bool math_zero(float a) +{ + return (MATH_FABSF(a) < FLT_EPSILON); +} + +static inline bool math_equal(float a, float b) +{ + return math_zero(a - b); +} + +float math_fast_inv_sqrtf(float number); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VG_LITE_MATH_H*/ diff --git a/src/draw/vg_lite/lv_vg_lite_path.c b/src/draw/vg_lite/lv_vg_lite_path.c new file mode 100644 index 000000000..1dec7860c --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_path.c @@ -0,0 +1,606 @@ +/** + * @file lv_vg_lite_path.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_vg_lite_path.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_draw_vg_lite_type.h" +#include "lv_vg_lite_math.h" +#include + +/********************* + * DEFINES + *********************/ + +#define PATH_KAPPA 0.552284f +#define PATH_MAX_CNT 32 + +/* Magic number from https://spencermortensen.com/articles/bezier-circle/ */ +#define PATH_ARC_MAGIC 0.55191502449351f + +#define SIGN(x) (math_zero(x) ? 0 : ((x) > 0 ? 1 : -1)) + +#define VLC_OP_ARG_LEN(OP, LEN) \ + case VLC_OP_##OP: \ + return (LEN) + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_vg_lite_path_t { + vg_lite_path_t base; + size_t mem_size; + uint8_t format_len; +}; + +typedef struct _lv_vg_lite_path_t * lv_vg_lite_path_ref_t; + +typedef struct { + float min_x; + float min_y; + float max_x; + float max_y; +} lv_vg_lite_path_bounds_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_vg_lite_path_init(struct _lv_draw_vg_lite_unit_t * unit) +{ + LV_ASSERT_NULL(unit); + _lv_ll_init(&unit->path_free_ll, sizeof(lv_vg_lite_path_ref_t)); +} + +void lv_vg_lite_path_deinit(struct _lv_draw_vg_lite_unit_t * unit) +{ + LV_ASSERT_NULL(unit); + + lv_ll_t * ll_p = &unit->path_free_ll; + lv_vg_lite_path_ref_t * path_ref; + + _LV_LL_READ(ll_p, path_ref) { + lv_vg_lite_path_destroy(*path_ref); + } + + _lv_ll_clear(ll_p); +} + +lv_vg_lite_path_t * lv_vg_lite_path_create(vg_lite_format_t data_format) +{ + lv_vg_lite_path_t * path = lv_malloc_zeroed(sizeof(lv_vg_lite_path_t)); + LV_ASSERT_MALLOC(path); + path->format_len = lv_vg_lite_path_format_len(data_format); + LV_ASSERT(vg_lite_init_path( + &path->base, + data_format, + VG_LITE_MEDIUM, + 0, + NULL, + 0, 0, 0, 0) + == VG_LITE_SUCCESS); + return path; +} + +void lv_vg_lite_path_destroy(lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(path); + if(path->base.path != NULL) { + lv_free(path->base.path); + path->base.path = NULL; + } + lv_free(path); +} + +lv_vg_lite_path_t * lv_vg_lite_path_get(struct _lv_draw_vg_lite_unit_t * unit, vg_lite_format_t data_format) +{ + LV_ASSERT_NULL(unit); + + unit->path_max_cnt++; + LV_ASSERT(unit->path_max_cnt < PATH_MAX_CNT); + + lv_ll_t * ll_p = &unit->path_free_ll; + + lv_vg_lite_path_ref_t * path_ref = _lv_ll_get_head(ll_p); + if(path_ref) { + lv_vg_lite_path_t * path = *path_ref; + lv_vg_lite_path_reset(path, data_format); + _lv_ll_remove(ll_p, path_ref); + lv_free(path_ref); + + return path; + } + + return lv_vg_lite_path_create(data_format); +} + +void lv_vg_lite_path_drop(struct _lv_draw_vg_lite_unit_t * unit, lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(unit); + LV_ASSERT_NULL(path); + + unit->path_max_cnt--; + LV_ASSERT(unit->path_max_cnt >= 0); + + lv_ll_t * ll_p = &unit->path_free_ll; + + uint32_t len = _lv_ll_get_len(ll_p); + if(len >= PATH_MAX_CNT) { + lv_vg_lite_path_ref_t * tail = _lv_ll_get_tail(ll_p); + lv_vg_lite_path_destroy(*tail); + _lv_ll_remove(ll_p, tail); + lv_free(tail); + } + + lv_vg_lite_path_ref_t * head = _lv_ll_ins_head(ll_p); + LV_ASSERT_MALLOC(head); + *head = path; +} + +void lv_vg_lite_path_reset(lv_vg_lite_path_t * path, vg_lite_format_t data_format) +{ + LV_ASSERT_NULL(path); + lv_memzero(path->base.path, path->mem_size); + path->base.path_length = 0; + path->base.format = data_format; + path->base.quality = VG_LITE_MEDIUM; + path->format_len = lv_vg_lite_path_format_len(data_format); +} + +vg_lite_path_t * lv_vg_lite_path_get_path(lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(path); + return &path->base; +} + +void lv_vg_lite_path_set_bonding_box(lv_vg_lite_path_t * path, + float min_x, float min_y, + float max_x, float max_y) +{ + LV_ASSERT_NULL(path); + path->base.bounding_box[0] = min_x; + path->base.bounding_box[1] = min_y; + path->base.bounding_box[2] = max_x; + path->base.bounding_box[3] = max_y; +} + +void lv_vg_lite_path_set_bonding_box_area(lv_vg_lite_path_t * path, const lv_area_t * area) +{ + LV_ASSERT_NULL(path); + LV_ASSERT_NULL(area); + lv_vg_lite_path_set_bonding_box(path, area->x1, area->y1, area->x2 + 1, area->y2 + 1); +} + +void lv_vg_lite_path_get_bonding_box(lv_vg_lite_path_t * path, + float * min_x, float * min_y, + float * max_x, float * max_y) +{ + LV_ASSERT_NULL(path); + if(min_x) *min_x = path->base.bounding_box[0]; + if(min_y) *min_y = path->base.bounding_box[1]; + if(max_x) *max_x = path->base.bounding_box[2]; + if(max_y) *max_y = path->base.bounding_box[3]; +} + +static void path_bounds_iter_cb(void * user_data, uint8_t op_code, const float * data, uint32_t len) +{ + if(len == 0) { + return; + } + + typedef struct { + float x; + float y; + } point_t; + + const int pt_len = sizeof(point_t) / sizeof(float); + + LV_ASSERT(len % pt_len == 0); + + const point_t * pt = (point_t *)data; + len /= pt_len; + + lv_vg_lite_path_bounds_t * bounds = user_data; + + for(uint32_t i = 0; i < len; i++) { + if(pt[i].x < bounds->min_x) bounds->min_x = pt[i].x; + if(pt[i].y < bounds->min_y) bounds->min_y = pt[i].y; + if(pt[i].x > bounds->max_x) bounds->max_x = pt[i].x; + if(pt[i].y > bounds->max_y) bounds->max_y = pt[i].y; + } +} + +bool lv_vg_lite_path_update_bonding_box(lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(path); + + if(!path->format_len) { + return false; + } + + lv_vg_lite_path_bounds_t bounds; + + /* init bounds */ + bounds.min_x = __FLT_MAX__; + bounds.min_y = __FLT_MAX__; + bounds.max_x = __FLT_MIN__; + bounds.max_y = __FLT_MIN__; + + /* calc bounds */ + lv_vg_lite_path_for_each_data(lv_vg_lite_path_get_path(path), path_bounds_iter_cb, &bounds); + + /* set bounds */ + lv_vg_lite_path_set_bonding_box(path, bounds.min_x, bounds.min_y, bounds.max_x, bounds.max_y); + + return true; +} + +void lv_vg_lite_path_set_quality(lv_vg_lite_path_t * path, vg_lite_quality_t quality) +{ + LV_ASSERT_NULL(path); + path->base.quality = quality; +} + +static void lv_vg_lite_path_append_data(lv_vg_lite_path_t * path, const void * data, size_t len) +{ + LV_ASSERT_NULL(path); + LV_ASSERT_NULL(data); + + if(path->base.path_length + len > path->mem_size) { + if(path->mem_size == 0) { + path->mem_size = len; + } + else { + path->mem_size *= 2; + } + path->base.path = lv_realloc(path->base.path, path->mem_size); + LV_ASSERT_MALLOC(path->base.path); + } + + lv_memcpy((uint8_t *)path->base.path + path->base.path_length, data, len); + path->base.path_length += len; +} + +static void lv_vg_lite_path_append_op(lv_vg_lite_path_t * path, uint32_t op) +{ + lv_vg_lite_path_append_data(path, &op, path->format_len); +} + +static void lv_vg_lite_path_append_point(lv_vg_lite_path_t * path, float x, float y) +{ + if(path->base.format == VG_LITE_FP32) { + lv_vg_lite_path_append_data(path, &x, sizeof(x)); + lv_vg_lite_path_append_data(path, &y, sizeof(y)); + return; + } + + int32_t ix = (int32_t)(x); + int32_t iy = (int32_t)(y); + lv_vg_lite_path_append_data(path, &ix, path->format_len); + lv_vg_lite_path_append_data(path, &iy, path->format_len); +} + +void lv_vg_lite_path_move_to(lv_vg_lite_path_t * path, + float x, float y) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_MOVE); + lv_vg_lite_path_append_point(path, x, y); +} + +void lv_vg_lite_path_line_to(lv_vg_lite_path_t * path, + float x, float y) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_LINE); + lv_vg_lite_path_append_point(path, x, y); +} + +void lv_vg_lite_path_quad_to(lv_vg_lite_path_t * path, + float cx, float cy, + float x, float y) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_QUAD); + lv_vg_lite_path_append_point(path, cx, cy); + lv_vg_lite_path_append_point(path, x, y); +} + +void lv_vg_lite_path_cubic_to(lv_vg_lite_path_t * path, + float cx1, float cy1, + float cx2, float cy2, + float x, float y) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_CUBIC); + lv_vg_lite_path_append_point(path, cx1, cy1); + lv_vg_lite_path_append_point(path, cx2, cy2); + lv_vg_lite_path_append_point(path, x, y); +} + +void lv_vg_lite_path_close(lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_CLOSE); +} + +void lv_vg_lite_path_end(lv_vg_lite_path_t * path) +{ + LV_ASSERT_NULL(path); + lv_vg_lite_path_append_op(path, VLC_OP_END); +} + +void lv_vg_lite_path_append_rect( + lv_vg_lite_path_t * path, + float x, float y, + float w, float h, + float rx, float ry) +{ + const float half_w = w * 0.5f; + const float half_h = h * 0.5f; + + /*clamping cornerRadius by minimum size*/ + if(rx > half_w) + rx = half_w; + if(ry > half_h) + ry = half_h; + + /*rectangle*/ + if(rx == 0 && ry == 0) { + lv_vg_lite_path_move_to(path, x, y); + lv_vg_lite_path_line_to(path, x + w, y); + lv_vg_lite_path_line_to(path, x + w, y + h); + lv_vg_lite_path_line_to(path, x, y + h); + lv_vg_lite_path_close(path); + return; + } + + /*circle*/ + if(math_equal(rx, half_w) && math_equal(ry, half_h)) { + return lv_vg_lite_path_append_circle(path, x + (w * 0.5f), y + (h * 0.5f), rx, ry); + } + + /*rounded rectangle*/ + float hrx = rx * 0.5f; + float hry = ry * 0.5f; + lv_vg_lite_path_move_to(path, x + rx, y); + lv_vg_lite_path_line_to(path, x + w - rx, y); + lv_vg_lite_path_cubic_to(path, x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); + lv_vg_lite_path_line_to(path, x + w, y + h - ry); + lv_vg_lite_path_cubic_to(path, x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); + lv_vg_lite_path_line_to(path, x + rx, y + h); + lv_vg_lite_path_cubic_to(path, x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); + lv_vg_lite_path_line_to(path, x, y + ry); + lv_vg_lite_path_cubic_to(path, x, y + ry - hry, x + rx - hrx, y, x + rx, y); + lv_vg_lite_path_close(path); +} + +void lv_vg_lite_path_append_circle( + lv_vg_lite_path_t * path, + float cx, float cy, + float rx, float ry) +{ + /* https://learn.microsoft.com/zh-cn/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers */ + float rx_kappa = rx * PATH_KAPPA; + float ry_kappa = ry * PATH_KAPPA; + + lv_vg_lite_path_move_to(path, cx, cy - ry); + lv_vg_lite_path_cubic_to(path, cx + rx_kappa, cy - ry, cx + rx, cy - ry_kappa, cx + rx, cy); + lv_vg_lite_path_cubic_to(path, cx + rx, cy + ry_kappa, cx + rx_kappa, cy + ry, cx, cy + ry); + lv_vg_lite_path_cubic_to(path, cx - rx_kappa, cy + ry, cx - rx, cy + ry_kappa, cx - rx, cy); + lv_vg_lite_path_cubic_to(path, cx - rx, cy - ry_kappa, cx - rx_kappa, cy - ry, cx, cy - ry); + lv_vg_lite_path_close(path); +} + +void lv_vg_lite_path_append_arc_right_angle(lv_vg_lite_path_t * path, + float start_x, float start_y, + float center_x, float center_y, + float end_x, float end_y) +{ + float dx1 = center_x - start_x; + float dy1 = center_y - start_y; + float dx2 = end_x - center_x; + float dy2 = end_y - center_y; + + float c = SIGN(dx1 * dy2 - dx2 * dy1) * PATH_ARC_MAGIC; + + lv_vg_lite_path_cubic_to(path, + start_x - c * dy1, start_y + c * dx1, + end_x - c * dy2, end_y + c * dx2, + end_x, end_y); +} + +void lv_vg_lite_path_append_arc(lv_vg_lite_path_t * path, + float cx, float cy, + float radius, + float start_angle, + float sweep, + bool pie) +{ + /* just circle */ + if(sweep >= 360.0f || sweep <= -360.0f) { + return lv_vg_lite_path_append_circle(path, cx, cy, radius, radius); + } + + start_angle = (start_angle * MATH_PI) / 180.0f; + sweep = sweep * MATH_PI / 180.0f; + + int n_curves = ceil(MATH_FABSF(sweep / MATH_HALF_PI)); + int sweep_sign = (sweep < 0 ? -1 : 1); + float fract = fmodf(sweep, MATH_HALF_PI); + fract = (math_zero(fract)) ? MATH_HALF_PI * sweep_sign : fract; + + /* Start from here */ + float start_x = radius * MATH_COSF(start_angle); + float start_y = radius * MATH_SINF(start_angle); + + if(pie) { + lv_vg_lite_path_move_to(path, cx, cy); + lv_vg_lite_path_line_to(path, start_x + cx, start_y + cy); + } + else { + lv_vg_lite_path_move_to(path, start_x + cx, start_y + cy); + } + + for(int i = 0; i < n_curves; ++i) { + float end_angle = start_angle + ((i != n_curves - 1) ? MATH_HALF_PI * sweep_sign : fract); + float end_x = radius * MATH_COSF(end_angle); + float end_y = radius * MATH_SINF(end_angle); + + /* variables needed to calculate bezier control points */ + + /** get bezier control points using article: + * (http://itc.ktu.lt/index.php/ITC/article/view/11812/6479) + */ + float ax = start_x; + float ay = start_y; + float bx = end_x; + float by = end_y; + float q1 = ax * ax + ay * ay; + float q2 = ax * bx + ay * by + q1; + float k2 = (4.0f / 3.0f) * ((MATH_SQRTF(2 * q1 * q2) - q2) / (ax * by - ay * bx)); + + /* Next start point is the current end point */ + start_x = end_x; + start_y = end_y; + + end_x += cx; + end_y += cy; + + float ctrl1_x = ax - k2 * ay + cx; + float ctrl1_y = ay + k2 * ax + cy; + float ctrl2_x = bx + k2 * by + cx; + float ctrl2_y = by - k2 * bx + cy; + + lv_vg_lite_path_cubic_to(path, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, end_x, end_y); + start_angle = end_angle; + } + + if(pie) { + lv_vg_lite_path_close(path); + } +} + +uint8_t lv_vg_lite_vlc_op_arg_len(uint8_t vlc_op) +{ + switch(vlc_op) { + VLC_OP_ARG_LEN(END, 0); + VLC_OP_ARG_LEN(CLOSE, 0); + VLC_OP_ARG_LEN(MOVE, 2); + VLC_OP_ARG_LEN(MOVE_REL, 2); + VLC_OP_ARG_LEN(LINE, 2); + VLC_OP_ARG_LEN(LINE_REL, 2); + VLC_OP_ARG_LEN(QUAD, 4); + VLC_OP_ARG_LEN(QUAD_REL, 4); + VLC_OP_ARG_LEN(CUBIC, 6); + VLC_OP_ARG_LEN(CUBIC_REL, 6); + VLC_OP_ARG_LEN(SCCWARC, 5); + VLC_OP_ARG_LEN(SCCWARC_REL, 5); + VLC_OP_ARG_LEN(SCWARC, 5); + VLC_OP_ARG_LEN(SCWARC_REL, 5); + VLC_OP_ARG_LEN(LCCWARC, 5); + VLC_OP_ARG_LEN(LCCWARC_REL, 5); + VLC_OP_ARG_LEN(LCWARC, 5); + VLC_OP_ARG_LEN(LCWARC_REL, 5); + default: + break; + } + + LV_LOG_ERROR("UNKNOW_VLC_OP: 0x%x", vlc_op); + LV_ASSERT(false); + return 0; +} + +uint8_t lv_vg_lite_path_format_len(vg_lite_format_t format) +{ + switch(format) { + case VG_LITE_S8: + return 1; + case VG_LITE_S16: + return 2; + case VG_LITE_S32: + return 4; + case VG_LITE_FP32: + return 4; + default: + break; + } + + LV_LOG_ERROR("UNKNOW_FORMAT: %d", format); + LV_ASSERT(false); + return 0; +} + +void lv_vg_lite_path_for_each_data(const vg_lite_path_t * path, lv_vg_lite_path_iter_cb_t cb, void * user_data) +{ + LV_ASSERT_NULL(path); + LV_ASSERT_NULL(cb); + + uint8_t fmt_len = lv_vg_lite_path_format_len(path->format); + uint8_t * cur = path->path; + uint8_t * end = cur + path->path_length; + float tmp_data[8]; + + while(cur < end) { + /* get op code */ + uint8_t op_code = VLC_GET_OP_CODE(cur); + + /* get arguments length */ + uint8_t arg_len = lv_vg_lite_vlc_op_arg_len(op_code); + + /* skip op code */ + cur += fmt_len; + + /* print arguments */ + for(uint8_t i = 0; i < arg_len; i++) { + switch(path->format) { + case VG_LITE_S8: + tmp_data[i] = *((int8_t *)cur); + break; + case VG_LITE_S16: + tmp_data[i] = *((int16_t *)cur); + break; + case VG_LITE_S32: + tmp_data[i] = *((int32_t *)cur); + break; + case VG_LITE_FP32: + tmp_data[i] = *((float *)cur); + break; + default: + LV_LOG_ERROR("UNKNOW_FORMAT(%d)", path->format); + LV_ASSERT(false); + break; + } + + cur += fmt_len; + } + + cb(user_data, op_code, tmp_data, arg_len); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_vg_lite_path.h b/src/draw/vg_lite/lv_vg_lite_path.h new file mode 100644 index 000000000..e151dc8ee --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_path.h @@ -0,0 +1,124 @@ +/** + * @file lv_vg_lite_path.h + * + */ + +#ifndef LV_VG_LITE_PATH_H +#define LV_VG_LITE_PATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "lv_vg_lite_utils.h" + +#if LV_USE_DRAW_VG_LITE + +/********************* + * DEFINES + *********************/ + +typedef struct _lv_vg_lite_path_t lv_vg_lite_path_t; +struct _lv_draw_vg_lite_unit_t; + +typedef void (*lv_vg_lite_path_iter_cb_t)(void * user_data, uint8_t op_code, const float * data, uint32_t len); + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_vg_lite_path_init(struct _lv_draw_vg_lite_unit_t * unit); + +void lv_vg_lite_path_deinit(struct _lv_draw_vg_lite_unit_t * unit); + +lv_vg_lite_path_t * lv_vg_lite_path_create(vg_lite_format_t data_format); + +void lv_vg_lite_path_destroy(lv_vg_lite_path_t * path); + +lv_vg_lite_path_t * lv_vg_lite_path_get(struct _lv_draw_vg_lite_unit_t * unit, vg_lite_format_t data_format); + +void lv_vg_lite_path_drop(struct _lv_draw_vg_lite_unit_t * unit, lv_vg_lite_path_t * path); + +void lv_vg_lite_path_reset(lv_vg_lite_path_t * path, vg_lite_format_t data_format); + +void lv_vg_lite_path_set_bonding_box_area(lv_vg_lite_path_t * path, const lv_area_t * area); + +void lv_vg_lite_path_set_bonding_box(lv_vg_lite_path_t * path, + float min_x, float min_y, + float max_x, float max_y); + +void lv_vg_lite_path_get_bonding_box(lv_vg_lite_path_t * path, + float * min_x, float * min_y, + float * max_x, float * max_y); + +bool lv_vg_lite_path_update_bonding_box(lv_vg_lite_path_t * path); + +void lv_vg_lite_path_set_quality(lv_vg_lite_path_t * path, vg_lite_quality_t quality); + +vg_lite_path_t * lv_vg_lite_path_get_path(lv_vg_lite_path_t * path); + +void lv_vg_lite_path_move_to(lv_vg_lite_path_t * path, + float x, float y); + +void lv_vg_lite_path_line_to(lv_vg_lite_path_t * path, + float x, float y); + +void lv_vg_lite_path_quad_to(lv_vg_lite_path_t * path, + float cx, float cy, + float x, float y); + +void lv_vg_lite_path_cubic_to(lv_vg_lite_path_t * path, + float cx1, float cy1, + float cx2, float cy2, + float x, float y); + +void lv_vg_lite_path_close(lv_vg_lite_path_t * path); + +void lv_vg_lite_path_end(lv_vg_lite_path_t * path); + +void lv_vg_lite_path_append_rect(lv_vg_lite_path_t * path, + float x, float y, + float w, float h, + float rx, float ry); + +void lv_vg_lite_path_append_circle(lv_vg_lite_path_t * path, + float cx, float cy, + float rx, float ry); + +void lv_vg_lite_path_append_arc_right_angle(lv_vg_lite_path_t * path, + float start_x, float start_y, + float center_x, float center_y, + float end_x, float end_y); + +void lv_vg_lite_path_append_arc(lv_vg_lite_path_t * path, + float cx, float cy, + float radius, + float start_angle, + float sweep, + bool pie); + +uint8_t lv_vg_lite_vlc_op_arg_len(uint8_t vlc_op); + +uint8_t lv_vg_lite_path_format_len(vg_lite_format_t format); + +void lv_vg_lite_path_for_each_data(const vg_lite_path_t * path, lv_vg_lite_path_iter_cb_t cb, void * user_data); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_VG_LITE_PATH_H*/ diff --git a/src/draw/vg_lite/lv_vg_lite_utils.c b/src/draw/vg_lite/lv_vg_lite_utils.c new file mode 100644 index 000000000..eb0679867 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_utils.c @@ -0,0 +1,1070 @@ +/** + * @file vg_lite_utils.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_vg_lite_utils.h" + +#if LV_USE_DRAW_VG_LITE + +#include "lv_vg_lite_decoder.h" +#include "lv_vg_lite_path.h" +#include + +/********************* + * DEFINES + *********************/ + +#define ENUM_TO_STRING(e) \ + case (e): \ + return #e + +#define VG_LITE_ENUM_TO_STRING(e) \ + case (VG_LITE_##e): \ + return #e + +#define VLC_OP_ENUM_TO_STRING(e) \ + case (VLC_OP_##e): \ + return #e + +#define FEATURE_ENUM_TO_STRING(e) \ + case (gcFEATURE_BIT_VG_##e): \ + return #e + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_vg_lite_dump_info(void) +{ + char name[64]; + uint32_t chip_id; + uint32_t chip_rev; + uint32_t cid; + vg_lite_get_product_info(name, &chip_id, &chip_rev); + vg_lite_get_register(0x30, &cid); + LV_LOG_USER("Product Info: %s" + " | Chip ID: 0x%" PRIx32 + " | Revision: 0x%" PRIx32 + " | CID: 0x%" PRIx32, + name, chip_id, chip_rev, cid); + + vg_lite_info_t info; + vg_lite_get_info(&info); + LV_LOG_USER("VGLite API version: 0x%" PRIx32, info.api_version); + LV_LOG_USER("VGLite API header version: 0x%" PRIx32, info.header_version); + LV_LOG_USER("VGLite release version: 0x%" PRIx32, info.release_version); + + for(int feature = 0; feature < gcFEATURE_COUNT; feature++) { + uint32_t ret = vg_lite_query_feature((vg_lite_feature_t)feature); + LV_UNUSED(ret); + LV_LOG_USER("Feature-%d: %s\t - %s", + feature, lv_vg_lite_feature_string((vg_lite_feature_t)feature), + ret ? "YES" : "NO"); + } + + uint32_t mem_avail; + vg_lite_mem_avail(&mem_avail); + LV_LOG_USER("Memory Avaliable: %" PRId32 " Bytes", mem_avail); +} + +const char * lv_vg_lite_error_string(vg_lite_error_t error) +{ + switch(error) { + VG_LITE_ENUM_TO_STRING(SUCCESS); + VG_LITE_ENUM_TO_STRING(INVALID_ARGUMENT); + VG_LITE_ENUM_TO_STRING(OUT_OF_MEMORY); + VG_LITE_ENUM_TO_STRING(NO_CONTEXT); + VG_LITE_ENUM_TO_STRING(TIMEOUT); + VG_LITE_ENUM_TO_STRING(OUT_OF_RESOURCES); + VG_LITE_ENUM_TO_STRING(GENERIC_IO); + VG_LITE_ENUM_TO_STRING(NOT_SUPPORT); + VG_LITE_ENUM_TO_STRING(ALREADY_EXISTS); + VG_LITE_ENUM_TO_STRING(NOT_ALIGNED); + VG_LITE_ENUM_TO_STRING(FLEXA_TIME_OUT); + VG_LITE_ENUM_TO_STRING(FLEXA_HANDSHAKE_FAIL); + default: + break; + } + return "UNKNOW_ERROR"; +} + +const char * lv_vg_lite_feature_string(vg_lite_feature_t feature) +{ + switch(feature) { + FEATURE_ENUM_TO_STRING(IM_INDEX_FORMAT); + FEATURE_ENUM_TO_STRING(SCISSOR); + FEATURE_ENUM_TO_STRING(BORDER_CULLING); + FEATURE_ENUM_TO_STRING(RGBA2_FORMAT); + FEATURE_ENUM_TO_STRING(QUALITY_8X); + FEATURE_ENUM_TO_STRING(IM_FASTCLAER); + FEATURE_ENUM_TO_STRING(RADIAL_GRADIENT); + FEATURE_ENUM_TO_STRING(GLOBAL_ALPHA); + FEATURE_ENUM_TO_STRING(RGBA8_ETC2_EAC); + FEATURE_ENUM_TO_STRING(COLOR_KEY); + FEATURE_ENUM_TO_STRING(DOUBLE_IMAGE); + FEATURE_ENUM_TO_STRING(YUV_OUTPUT); + FEATURE_ENUM_TO_STRING(FLEXA); + FEATURE_ENUM_TO_STRING(24BIT); + FEATURE_ENUM_TO_STRING(DITHER); + FEATURE_ENUM_TO_STRING(USE_DST); + FEATURE_ENUM_TO_STRING(PE_CLEAR); + FEATURE_ENUM_TO_STRING(IM_INPUT); + FEATURE_ENUM_TO_STRING(DEC_COMPRESS); + FEATURE_ENUM_TO_STRING(LINEAR_GRADIENT_EXT); + FEATURE_ENUM_TO_STRING(MASK); + FEATURE_ENUM_TO_STRING(MIRROR); + FEATURE_ENUM_TO_STRING(GAMMA); + FEATURE_ENUM_TO_STRING(NEW_BLEND_MODE); + FEATURE_ENUM_TO_STRING(STENCIL); + FEATURE_ENUM_TO_STRING(SRC_PREMULTIPLIED); /*! Valid only if FEATURE_ENUM_TO_STRING(HW_PREMULTIPLY is 0 */ + FEATURE_ENUM_TO_STRING(HW_PREMULTIPLY); /*! HW multiplier can accept either premultiplied or not */ + FEATURE_ENUM_TO_STRING(COLOR_TRANSFORMATION); + FEATURE_ENUM_TO_STRING(LVGL_SUPPORT); + FEATURE_ENUM_TO_STRING(INDEX_ENDIAN); + FEATURE_ENUM_TO_STRING(24BIT_PLANAR); + FEATURE_ENUM_TO_STRING(PIXEL_MATRIX); + FEATURE_ENUM_TO_STRING(NEW_IMAGE_INDEX); + FEATURE_ENUM_TO_STRING(PARALLEL_PATHS); + FEATURE_ENUM_TO_STRING(STRIPE_MODE); + FEATURE_ENUM_TO_STRING(IM_DEC_INPUT); + FEATURE_ENUM_TO_STRING(GAUSSIAN_BLUR); + FEATURE_ENUM_TO_STRING(RECTANGLE_TILED_OUT); + FEATURE_ENUM_TO_STRING(TESSELLATION_TILED_OUT); + FEATURE_ENUM_TO_STRING(IM_REPEAT_REFLECT); + FEATURE_ENUM_TO_STRING(YUY2_INPUT); + FEATURE_ENUM_TO_STRING(YUV_INPUT); + FEATURE_ENUM_TO_STRING(YUV_TILED_INPUT); + FEATURE_ENUM_TO_STRING(AYUV_INPUT); + FEATURE_ENUM_TO_STRING(16PIXELS_ALIGN); + default: + break; + } + return "UNKNOW_FEATURE"; +} + +const char * lv_vg_lite_buffer_format_string(vg_lite_buffer_format_t format) +{ + switch(format) { + VG_LITE_ENUM_TO_STRING(RGBA8888); + VG_LITE_ENUM_TO_STRING(BGRA8888); + VG_LITE_ENUM_TO_STRING(RGBX8888); + VG_LITE_ENUM_TO_STRING(BGRX8888); + VG_LITE_ENUM_TO_STRING(RGB565); + VG_LITE_ENUM_TO_STRING(BGR565); + VG_LITE_ENUM_TO_STRING(RGBA4444); + VG_LITE_ENUM_TO_STRING(BGRA4444); + VG_LITE_ENUM_TO_STRING(BGRA5551); + VG_LITE_ENUM_TO_STRING(A4); + VG_LITE_ENUM_TO_STRING(A8); + VG_LITE_ENUM_TO_STRING(L8); + VG_LITE_ENUM_TO_STRING(YUYV); + VG_LITE_ENUM_TO_STRING(YUY2); + VG_LITE_ENUM_TO_STRING(NV12); + VG_LITE_ENUM_TO_STRING(ANV12); + VG_LITE_ENUM_TO_STRING(AYUY2); + VG_LITE_ENUM_TO_STRING(YV12); + VG_LITE_ENUM_TO_STRING(YV24); + VG_LITE_ENUM_TO_STRING(YV16); + VG_LITE_ENUM_TO_STRING(NV16); + VG_LITE_ENUM_TO_STRING(YUY2_TILED); + VG_LITE_ENUM_TO_STRING(NV12_TILED); + VG_LITE_ENUM_TO_STRING(ANV12_TILED); + VG_LITE_ENUM_TO_STRING(AYUY2_TILED); + VG_LITE_ENUM_TO_STRING(INDEX_1); + VG_LITE_ENUM_TO_STRING(INDEX_2); + VG_LITE_ENUM_TO_STRING(INDEX_4); + VG_LITE_ENUM_TO_STRING(INDEX_8); + VG_LITE_ENUM_TO_STRING(RGBA2222); + VG_LITE_ENUM_TO_STRING(BGRA2222); + VG_LITE_ENUM_TO_STRING(ABGR2222); + VG_LITE_ENUM_TO_STRING(ARGB2222); + VG_LITE_ENUM_TO_STRING(ABGR4444); + VG_LITE_ENUM_TO_STRING(ARGB4444); + VG_LITE_ENUM_TO_STRING(ABGR8888); + VG_LITE_ENUM_TO_STRING(ARGB8888); + VG_LITE_ENUM_TO_STRING(ABGR1555); + VG_LITE_ENUM_TO_STRING(RGBA5551); + VG_LITE_ENUM_TO_STRING(ARGB1555); + VG_LITE_ENUM_TO_STRING(XBGR8888); + VG_LITE_ENUM_TO_STRING(XRGB8888); + VG_LITE_ENUM_TO_STRING(RGBA8888_ETC2_EAC); + VG_LITE_ENUM_TO_STRING(RGB888); + VG_LITE_ENUM_TO_STRING(BGR888); + VG_LITE_ENUM_TO_STRING(ABGR8565); + VG_LITE_ENUM_TO_STRING(BGRA5658); + VG_LITE_ENUM_TO_STRING(ARGB8565); + VG_LITE_ENUM_TO_STRING(RGBA5658); + default: + break; + } + return "UNKNOW_BUFFER_FORMAT"; +} + +const char * lv_vg_lite_filter_string(vg_lite_filter_t filter) +{ + switch(filter) { + VG_LITE_ENUM_TO_STRING(FILTER_POINT); + VG_LITE_ENUM_TO_STRING(FILTER_LINEAR); + VG_LITE_ENUM_TO_STRING(FILTER_BI_LINEAR); + default: + break; + } + return "UNKNOW_FILTER"; +} + +const char * lv_vg_lite_blend_string(vg_lite_blend_t blend) +{ + switch(blend) { + VG_LITE_ENUM_TO_STRING(BLEND_NONE); /*! S, i.e. no blending. */ + VG_LITE_ENUM_TO_STRING(BLEND_SRC_OVER); /*! S + (1 - Sa) * D */ + VG_LITE_ENUM_TO_STRING(BLEND_DST_OVER); /*! (1 - Da) * S + D */ + VG_LITE_ENUM_TO_STRING(BLEND_SRC_IN); /*! Da * S */ + VG_LITE_ENUM_TO_STRING(BLEND_DST_IN); /*! Sa * D */ + VG_LITE_ENUM_TO_STRING(BLEND_SCREEN); /*! S + D - S * D */ + VG_LITE_ENUM_TO_STRING(BLEND_MULTIPLY); /*! S * (1 - Da) + D * (1 - Sa) + S * D */ + VG_LITE_ENUM_TO_STRING(BLEND_ADDITIVE); /*! S + D */ + VG_LITE_ENUM_TO_STRING(BLEND_SUBTRACT); /*! D * (1 - S) */ + VG_LITE_ENUM_TO_STRING(BLEND_SUBTRACT_LVGL); /*! D - S */ + VG_LITE_ENUM_TO_STRING(BLEND_NORMAL_LVGL); /*! S * Sa + (1 - Sa) * D */ + VG_LITE_ENUM_TO_STRING(BLEND_ADDITIVE_LVGL); /*! (S + D) * Sa + D * (1 - Sa) */ + VG_LITE_ENUM_TO_STRING(BLEND_MULTIPLY_LVGL); /*! (S * D) * Sa + D * (1 - Sa) */ + default: + break; + } + return "UNKNOW_BLEND"; +} + +const char * lv_vg_lite_global_alpha_string(vg_lite_global_alpha_t global_alpha) +{ + switch(global_alpha) { + VG_LITE_ENUM_TO_STRING(NORMAL); + VG_LITE_ENUM_TO_STRING(GLOBAL); + VG_LITE_ENUM_TO_STRING(SCALED); + default: + break; + } + return "UNKNOW_GLOBAL_ALPHA"; +} + +const char * lv_vg_lite_fill_rule_string(vg_lite_fill_t fill_rule) +{ + switch(fill_rule) { + VG_LITE_ENUM_TO_STRING(FILL_NON_ZERO); + VG_LITE_ENUM_TO_STRING(FILL_EVEN_ODD); + default: + break; + } + return "UNKNOW_FILL"; +} + +const char * lv_vg_lite_image_mode_string(vg_lite_buffer_image_mode_t image_mode) +{ + switch(image_mode) { + VG_LITE_ENUM_TO_STRING(NORMAL_IMAGE_MODE); + VG_LITE_ENUM_TO_STRING(NONE_IMAGE_MODE); + VG_LITE_ENUM_TO_STRING(MULTIPLY_IMAGE_MODE); + default: + break; + } + return "UNKNOW_IMAGE_MODE"; +} + +const char * lv_vg_lite_vlc_op_string(uint8_t vlc_op) +{ + switch(vlc_op) { + VLC_OP_ENUM_TO_STRING(END); + VLC_OP_ENUM_TO_STRING(CLOSE); + VLC_OP_ENUM_TO_STRING(MOVE); + VLC_OP_ENUM_TO_STRING(MOVE_REL); + VLC_OP_ENUM_TO_STRING(LINE); + VLC_OP_ENUM_TO_STRING(LINE_REL); + VLC_OP_ENUM_TO_STRING(QUAD); + VLC_OP_ENUM_TO_STRING(QUAD_REL); + VLC_OP_ENUM_TO_STRING(CUBIC); + VLC_OP_ENUM_TO_STRING(CUBIC_REL); + + VLC_OP_ENUM_TO_STRING(SCCWARC); + VLC_OP_ENUM_TO_STRING(SCCWARC_REL); + VLC_OP_ENUM_TO_STRING(SCWARC); + VLC_OP_ENUM_TO_STRING(SCWARC_REL); + VLC_OP_ENUM_TO_STRING(LCCWARC); + VLC_OP_ENUM_TO_STRING(LCCWARC_REL); + VLC_OP_ENUM_TO_STRING(LCWARC); + VLC_OP_ENUM_TO_STRING(LCWARC_REL); + default: + break; + } + return "UNKNOW_VLC_OP"; +} + +static void path_data_print_cb(void * user_data, uint8_t op_code, const float * data, uint32_t len) +{ + LV_LOG("%s, ", lv_vg_lite_vlc_op_string(op_code)); + for(uint32_t i = 0; i < len; i++) { + LV_LOG("%0.2f, ", data[i]); + } + LV_LOG("\n"); +} + +void lv_vg_lite_path_dump_info(const vg_lite_path_t * path) +{ + LV_ASSERT(path != NULL); + LV_ASSERT(path->path != NULL); + uint8_t fmt_len = lv_vg_lite_path_format_len(path->format); + size_t len = path->path_length / fmt_len; + + LV_ASSERT(len > 0); + + LV_LOG_USER("address: %p", path->path); + LV_LOG_USER("length: %d", (int)len); + LV_LOG_USER("bonding box: (%0.2f, %0.2f) - (%0.2f, %0.2f)", + path->bounding_box[0], path->bounding_box[1], + path->bounding_box[2], path->bounding_box[3]); + LV_LOG_USER("format: %d", (int)path->format); + LV_LOG_USER("quality: %d", (int)path->quality); + + lv_vg_lite_path_for_each_data(path, path_data_print_cb, NULL); +} + +void lv_vg_lite_buffer_dump_info(const vg_lite_buffer_t * buffer) +{ + LV_LOG_USER("memory: %p", (buffer)->memory); + LV_LOG_USER("address: 0x%08x", (int)(buffer)->address); + LV_LOG_USER("size: W%d x H%d", (int)((buffer)->width), (int)((buffer)->height)); + LV_LOG_USER("stride: %d", (int)((buffer)->stride)); + LV_LOG_USER("format: %d (%s)", + (int)((buffer)->format), + lv_vg_lite_buffer_format_string((buffer)->format)); + LV_LOG_USER("tiled: %d", (int)((buffer)->tiled)); +} + +void lv_vg_lite_matrix_dump_info(const vg_lite_matrix_t * matrix) +{ + for(int i = 0; i < 3; i++) { + LV_LOG_USER("| %0.2f, %0.2f, %0.2f |", + (matrix)->m[i][0], (matrix)->m[i][1], (matrix)->m[i][2]); + } +} + +bool lv_vg_lite_is_dest_cf_supported(lv_color_format_t cf) +{ + switch(cf) { + case LV_COLOR_FORMAT_RGB565: + case LV_COLOR_FORMAT_RGB565A8: + case LV_COLOR_FORMAT_RGB888: + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + return true; + default: + break; + } + return false; +} + +bool lv_vg_lite_is_src_cf_supported(lv_color_format_t cf) +{ + switch(cf) { + case LV_COLOR_FORMAT_A4: + case LV_COLOR_FORMAT_A8: + case LV_COLOR_FORMAT_I1: + case LV_COLOR_FORMAT_I2: + case LV_COLOR_FORMAT_I4: + case LV_COLOR_FORMAT_I8: + case LV_COLOR_FORMAT_RGB565: + case LV_COLOR_FORMAT_RGB565A8: + case LV_COLOR_FORMAT_RGB888: + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + return true; + default: + break; + } + return false; +} + +vg_lite_buffer_format_t lv_vg_lite_vg_fmt(lv_color_format_t cf) +{ + switch(cf) { + case LV_COLOR_FORMAT_A4: + return VG_LITE_A4; + + case LV_COLOR_FORMAT_A8: + return VG_LITE_A8; + + case LV_COLOR_FORMAT_L8: + return VG_LITE_L8; + + case LV_COLOR_FORMAT_I1: + return VG_LITE_INDEX_1; + + case LV_COLOR_FORMAT_I2: + return VG_LITE_INDEX_2; + + case LV_COLOR_FORMAT_I4: + return VG_LITE_INDEX_4; + + case LV_COLOR_FORMAT_I8: + return VG_LITE_INDEX_8; + + case LV_COLOR_FORMAT_RGB565: + return VG_LITE_BGR565; + + case LV_COLOR_FORMAT_RGB565A8: + return VG_LITE_BGRA5658; + + case LV_COLOR_FORMAT_RGB888: + return VG_LITE_BGR888; + + case LV_COLOR_FORMAT_ARGB8888: + return VG_LITE_BGRA8888; + + case LV_COLOR_FORMAT_XRGB8888: + return VG_LITE_BGRX8888; + + default: + LV_LOG_ERROR("unsupport color format: %d", cf); + break; + } + + LV_ASSERT(false); +} + +void lv_vg_lite_buffer_format_bytes( + vg_lite_buffer_format_t format, + uint32_t * mul, + uint32_t * div, + uint32_t * bytes_align) +{ + /* Get the bpp information of a color format. */ + *mul = *div = 1; + *bytes_align = 4; + switch(format) { + case VG_LITE_L8: + case VG_LITE_A8: + case VG_LITE_RGBA8888_ETC2_EAC: + break; + case VG_LITE_A4: + *div = 2; + break; + case VG_LITE_ABGR1555: + case VG_LITE_ARGB1555: + case VG_LITE_BGRA5551: + case VG_LITE_RGBA5551: + case VG_LITE_RGBA4444: + case VG_LITE_BGRA4444: + case VG_LITE_ABGR4444: + case VG_LITE_ARGB4444: + case VG_LITE_RGB565: + case VG_LITE_BGR565: + case VG_LITE_YUYV: + case VG_LITE_YUY2: + case VG_LITE_YUY2_TILED: + /* AYUY2 buffer memory = YUY2 + alpha. */ + case VG_LITE_AYUY2: + case VG_LITE_AYUY2_TILED: + *mul = 2; + break; + case VG_LITE_RGBA8888: + case VG_LITE_BGRA8888: + case VG_LITE_ABGR8888: + case VG_LITE_ARGB8888: + case VG_LITE_RGBX8888: + case VG_LITE_BGRX8888: + case VG_LITE_XBGR8888: + case VG_LITE_XRGB8888: + *mul = 4; + break; + case VG_LITE_NV12: + case VG_LITE_NV12_TILED: + *mul = 1; + break; + case VG_LITE_ANV12: + case VG_LITE_ANV12_TILED: + *mul = 4; + break; + case VG_LITE_INDEX_1: + *div = 8; + *bytes_align = 8; + break; + case VG_LITE_INDEX_2: + *div = 4; + *bytes_align = 8; + break; + case VG_LITE_INDEX_4: + *div = 2; + *bytes_align = 8; + break; + case VG_LITE_INDEX_8: + *bytes_align = 1; + break; + case VG_LITE_RGBA2222: + case VG_LITE_BGRA2222: + case VG_LITE_ABGR2222: + case VG_LITE_ARGB2222: + *mul = 1; + break; + case VG_LITE_RGB888: + case VG_LITE_BGR888: + case VG_LITE_ABGR8565: + case VG_LITE_BGRA5658: + case VG_LITE_ARGB8565: + case VG_LITE_RGBA5658: + *mul = 3; + break; + default: + break; + } +} + +uint32_t lv_vg_lite_width_to_stride(uint32_t w, vg_lite_buffer_format_t color_format) +{ + w = lv_vg_lite_width_align(w); + + uint32_t mul, div, align; + lv_vg_lite_buffer_format_bytes(color_format, &mul, &div, &align); + return LV_VG_LITE_ALIGN((w * mul / div), align); +} + +uint32_t lv_vg_lite_width_align(uint32_t w) +{ + if(lv_vg_lite_16px_align()) { + w = LV_VG_LITE_ALIGN(w, 16); + } + + return w; +} + +bool lv_vg_lite_buffer_init( + vg_lite_buffer_t * buffer, + const void * ptr, + int32_t width, + int32_t height, + vg_lite_buffer_format_t format, + bool tiled) +{ + uint32_t mul; + uint32_t div; + uint32_t align; + LV_ASSERT_NULL(buffer); + LV_ASSERT_NULL(ptr); + + lv_memzero(buffer, sizeof(vg_lite_buffer_t)); + + buffer->format = format; + if(tiled || format == VG_LITE_RGBA8888_ETC2_EAC) { + buffer->tiled = VG_LITE_TILED; + } + else { + buffer->tiled = VG_LITE_LINEAR; + } + buffer->image_mode = VG_LITE_NORMAL_IMAGE_MODE; + buffer->transparency_mode = VG_LITE_IMAGE_OPAQUE; + buffer->width = width; + buffer->height = height; + lv_vg_lite_buffer_format_bytes(buffer->format, &mul, &div, &align); + buffer->stride = LV_VG_LITE_ALIGN((buffer->width * mul / div), align); + buffer->memory = (void *)ptr; + buffer->address = (uintptr_t)ptr; + + if(format == VG_LITE_NV12) { + buffer->yuv.swizzle = VG_LITE_SWIZZLE_UV; + buffer->yuv.uv_stride = buffer->stride; + buffer->yuv.alpha_stride = buffer->stride; + buffer->yuv.uv_height = buffer->height / 2; + buffer->yuv.uv_memory = (uint8_t *)ptr + (buffer->stride * buffer->height); + buffer->yuv.uv_planar = (uint32_t)(uintptr_t)buffer->yuv.uv_memory; + } + + return true; +} + +void lv_vg_lite_image_matrix(vg_lite_matrix_t * matrix, int32_t x, int32_t y, const lv_draw_image_dsc_t * dsc) +{ + LV_ASSERT_NULL(matrix); + LV_ASSERT_NULL(dsc); + + int32_t rotation = dsc->rotation; + int32_t scale_x = dsc->scale_x; + int32_t scale_y = dsc->scale_y; + + vg_lite_translate(x, y, matrix); + + if(rotation != 0 || scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) { + lv_point_t pivot = dsc->pivot; + vg_lite_translate(pivot.x, pivot.y, matrix); + + if(rotation != 0) { + vg_lite_rotate(rotation * 0.1f, matrix); + } + + if(scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) { + vg_lite_scale( + (vg_lite_float_t)scale_x / LV_SCALE_NONE, + (vg_lite_float_t)scale_y / LV_SCALE_NONE, + matrix); + } + + vg_lite_translate(-pivot.x, -pivot.y, matrix); + } +} + +bool lv_vg_lite_buffer_open_image(vg_lite_buffer_t * buffer, lv_image_decoder_dsc_t * decoder_dsc, const void * src) +{ + LV_ASSERT_NULL(buffer); + LV_ASSERT_NULL(decoder_dsc); + LV_ASSERT_NULL(src); + + lv_result_t res = lv_image_decoder_open(decoder_dsc, src, NULL); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to open image"); + return false; + } + + const uint8_t * img_data = _lv_image_decoder_get_data(decoder_dsc); + + if(!img_data) { + lv_image_decoder_close(decoder_dsc); + LV_LOG_ERROR("image data is NULL"); + return false; + } + + if(!lv_vg_lite_is_src_cf_supported(decoder_dsc->header.cf)) { + lv_image_decoder_close(decoder_dsc); + LV_LOG_ERROR("unsupport color format: %d", decoder_dsc->header.cf); + return false; + } + + res = lv_vg_lite_decoder_post_process(decoder_dsc); + if(res != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to post process image"); + return false; + } + + vg_lite_buffer_format_t fmt = lv_vg_lite_vg_fmt(decoder_dsc->header.cf); + + uint32_t palette_size = lv_vg_lite_get_palette_size(fmt); + uint32_t image_offset = 0; + if(palette_size) { + LV_VG_LITE_CHECK_ERROR(vg_lite_set_CLUT(palette_size, (uint32_t *)decoder_dsc->img_data)); + image_offset = LV_VG_LITE_ALIGN(palette_size * sizeof(uint32_t), LV_VG_LITE_BUF_ALIGN); + } + + uint32_t width = lv_vg_lite_width_align(decoder_dsc->header.w); + img_data += image_offset; + + LV_ASSERT(lv_vg_lite_buffer_init( + buffer, + img_data, + width, + decoder_dsc->header.h, + fmt, + false)); + + return true; +} + +void lv_vg_lite_rect(vg_lite_rectangle_t * rect, const lv_area_t * area) +{ + rect->x = area->x1; + rect->y = area->y1; + rect->width = lv_area_get_width(area); + rect->height = lv_area_get_height(area); +} + +uint32_t lv_vg_lite_get_palette_size(vg_lite_buffer_format_t format) +{ + uint32_t size = 0; + switch(format) { + case VG_LITE_INDEX_1: + size = 1 << 1; + break; + case VG_LITE_INDEX_2: + size = 1 << 2; + break; + case VG_LITE_INDEX_4: + size = 1 << 4; + break; + case VG_LITE_INDEX_8: + size = 1 << 8; + break; + default: + break; + } + return size; +} + +vg_lite_color_t lv_vg_lite_color(lv_color_t color, lv_opa_t opa, bool pre_mul) +{ + if(pre_mul && opa < LV_OPA_MAX) { + color.red = LV_UDIV255(color.red * opa); + color.green = LV_UDIV255(color.green * opa); + color.blue = LV_UDIV255(color.blue * opa); + } + return (uint32_t)opa << 24 | (uint32_t)color.blue << 16 | (uint32_t)color.green << 8 | color.red; +} + +vg_lite_blend_t lv_vg_lite_blend_mode(lv_blend_mode_t blend_mode) +{ + if(lv_vg_lite_support_blend_normal()) { + switch(blend_mode) { + case LV_BLEND_MODE_NORMAL: /**< Simply mix according to the opacity value*/ + return VG_LITE_BLEND_NORMAL_LVGL; + + case LV_BLEND_MODE_ADDITIVE: /**< Add the respective color channels*/ + return VG_LITE_BLEND_ADDITIVE_LVGL; + + case LV_BLEND_MODE_SUBTRACTIVE: /**< Subtract the foreground from the background*/ + return VG_LITE_BLEND_SUBTRACT_LVGL; + + case LV_BLEND_MODE_MULTIPLY: /**< Multiply the foreground and background*/ + return VG_LITE_BLEND_MULTIPLY_LVGL; + + default: + return VG_LITE_BLEND_NONE; + } + } + + switch(blend_mode) { + case LV_BLEND_MODE_NORMAL: /**< Simply mix according to the opacity value*/ + return VG_LITE_BLEND_SRC_OVER; + + case LV_BLEND_MODE_ADDITIVE: /**< Add the respective color channels*/ + return VG_LITE_BLEND_ADDITIVE; + + case LV_BLEND_MODE_SUBTRACTIVE: /**< Subtract the foreground from the background*/ + return VG_LITE_BLEND_SUBTRACT; + + case LV_BLEND_MODE_MULTIPLY: /**< Multiply the foreground and background*/ + return VG_LITE_BLEND_MULTIPLY; + + default: + return VG_LITE_BLEND_NONE; + } +} + +bool lv_vg_lite_buffer_check(const vg_lite_buffer_t * buffer, bool is_src) +{ + uint32_t mul; + uint32_t div; + uint32_t align; + uint32_t stride; + + if(!buffer) { + LV_LOG_ERROR("buffer is NULL"); + return false; + } + + if(buffer->width < 1) { + LV_LOG_ERROR("buffer width(%d) < 1", (int)buffer->width); + return false; + } + + if(buffer->height < 1) { + LV_LOG_ERROR("buffer height(%d) < 1", (int)buffer->height); + return false; + } + + if(buffer->stride < 1) { + LV_LOG_ERROR("buffer stride(%d) < 1", (int)buffer->stride); + return false; + } + + if(!(buffer->tiled == VG_LITE_LINEAR || buffer->tiled == VG_LITE_TILED)) { + LV_LOG_ERROR("buffer tiled(%d) is invalid", (int)buffer->tiled); + return false; + } + + if(buffer->memory == NULL) { + LV_LOG_ERROR("buffer memory is NULL"); + return false; + } + + if((uint32_t)(uintptr_t)buffer->memory != buffer->address) { + LV_LOG_ERROR("buffer memory(%p) != address(%p)", + buffer->memory, (void *)(uintptr_t)buffer->address); + return false; + } + + if(is_src && buffer->width != lv_vg_lite_width_align(buffer->width)) { + LV_LOG_ERROR("buffer width(%d) is not aligned", (int)buffer->width); + return false; + } + + if(!LV_VG_LITE_IS_ALIGNED(buffer->memory, LV_VG_LITE_BUF_ALIGN)) { + LV_LOG_ERROR("buffer address(%p) is not aligned to %d", buffer->memory, LV_VG_LITE_BUF_ALIGN); + return false; + } + + lv_vg_lite_buffer_format_bytes(buffer->format, &mul, &div, &align); + stride = LV_VG_LITE_ALIGN((buffer->width * mul / div), align); + + if(buffer->stride != stride) { + LV_LOG_ERROR("buffer stride(%d) != %d", (int)buffer->stride, (int)stride); + return false; + } + + if(!(buffer->image_mode == VG_LITE_NORMAL_IMAGE_MODE + || buffer->image_mode == VG_LITE_NONE_IMAGE_MODE + || buffer->image_mode == VG_LITE_MULTIPLY_IMAGE_MODE)) { + LV_LOG_ERROR("buffer image_mode(%d) is invalid", (int)buffer->image_mode); + return false; + } + + if(!(buffer->transparency_mode == VG_LITE_IMAGE_OPAQUE + || buffer->transparency_mode == VG_LITE_IMAGE_TRANSPARENT)) { + LV_LOG_ERROR("buffer transparency_mode(%d) is invalid", + (int)buffer->transparency_mode); + return false; + } + + return true; +} + +bool lv_vg_lite_path_check(const vg_lite_path_t * path) +{ + if(path == NULL) { + LV_LOG_ERROR("path is NULL"); + return false; + } + + if(path->path == NULL) { + LV_LOG_ERROR("path->path is NULL"); + return false; + } + + uint8_t fmt_len = lv_vg_lite_path_format_len(path->format); + if(!fmt_len) { + LV_LOG_ERROR("path format(%d) is invalid", (int)path->format); + return false; + } + + size_t len = path->path_length / fmt_len; + + if(len < 1) { + LV_LOG_ERROR("path length(%d) error", (int)path->path_length); + return false; + } + + uint8_t * cur = path->path; + uint8_t * end = cur + path->path_length; + + while(cur < end) { + /* get op code */ + uint8_t op_code = VLC_GET_OP_CODE(cur); + + /* get arguments length */ + uint8_t arg_len = lv_vg_lite_vlc_op_arg_len(op_code); + + /* get next op code */ + cur += (fmt_len * (1 + arg_len)) ; + } + + uint8_t end_op_code = VLC_GET_OP_CODE(end - fmt_len); + + if(end_op_code != VLC_OP_END) { + LV_LOG_ERROR("%d (%s) -> is NOT VLC_OP_END", end_op_code, lv_vg_lite_vlc_op_string(end_op_code)); + return false; + } + + return true; +} + +bool lv_vg_lite_support_blend_normal(void) +{ + return vg_lite_query_feature(gcFEATURE_BIT_VG_LVGL_SUPPORT); +} + +bool lv_vg_lite_16px_align(void) +{ + return vg_lite_query_feature(gcFEATURE_BIT_VG_16PIXELS_ALIGN); +} + +void lv_vg_lite_draw_linear_grad( + vg_lite_buffer_t * buffer, + vg_lite_path_t * path, + const lv_area_t * area, + const lv_grad_dsc_t * grad, + const vg_lite_matrix_t * matrix, + vg_lite_fill_t fill, + vg_lite_blend_t blend) +{ + LV_ASSERT_NULL(buffer); + LV_ASSERT_NULL(path); + LV_ASSERT_NULL(area); + LV_ASSERT_NULL(grad); + + LV_ASSERT(grad->dir != LV_GRAD_DIR_NONE); + + uint32_t colors[VLC_MAX_GRADIENT_STOPS]; + uint32_t stops[VLC_MAX_GRADIENT_STOPS]; + + /* Gradient setup */ + uint8_t cnt = grad->stops_count; + LV_ASSERT(cnt < VLC_MAX_GRADIENT_STOPS); + for(uint8_t i = 0; i < cnt; i++) { + stops[i] = grad->stops[i].frac; + const lv_color_t * c = &grad->stops[i].color; + lv_opa_t opa = grad->stops[i].opa; + + /* lvgl color -> gradient color */ + lv_color_t grad_color = lv_color_make(c->blue, c->green, c->red); + colors[i] = lv_vg_lite_color(grad_color, opa, true); + } + + vg_lite_linear_gradient_t gradient; + lv_memzero(&gradient, sizeof(gradient)); + + LV_VG_LITE_CHECK_ERROR(vg_lite_init_grad(&gradient)); + LV_VG_LITE_CHECK_ERROR(vg_lite_set_grad(&gradient, cnt, colors, stops)); + LV_VG_LITE_CHECK_ERROR(vg_lite_update_grad(&gradient)); + + vg_lite_matrix_t * grad_matrix = vg_lite_get_grad_matrix(&gradient); + vg_lite_identity(grad_matrix); + vg_lite_translate(area->x1, area->y1, grad_matrix); + + if(grad->dir == LV_GRAD_DIR_VER) { + vg_lite_scale(1, lv_area_get_height(area) / 256.0f, grad_matrix); + vg_lite_rotate(90, grad_matrix); + } + else { /*LV_GRAD_DIR_HOR*/ + vg_lite_scale(lv_area_get_width(area) / 256.0f, 1, grad_matrix); + } + + LV_VG_LITE_CHECK_ERROR(vg_lite_draw_grad( + buffer, + path, + fill, + (vg_lite_matrix_t *)matrix, + &gradient, + blend)); + + LV_VG_LITE_CHECK_ERROR(vg_lite_clear_grad(&gradient)); +} + +void lv_vg_lite_matrix_multiply(vg_lite_matrix_t * matrix, const vg_lite_matrix_t * mult) +{ + vg_lite_matrix_t temp; + int row, column; + + /* Process all rows. */ + for(row = 0; row < 3; row++) { + /* Process all columns. */ + for(column = 0; column < 3; column++) { + /* Compute matrix entry. */ + temp.m[row][column] = (matrix->m[row][0] * mult->m[0][column]) + + (matrix->m[row][1] * mult->m[1][column]) + + (matrix->m[row][2] * mult->m[2][column]); + } + } + + /* Copy temporary matrix into result. */ + lv_memcpy(matrix, &temp, sizeof(temp)); +} + +void lv_vg_lite_matrix_flip_y(vg_lite_matrix_t * matrix) +{ + matrix->m[1][1] = -matrix->m[1][1]; +} + +bool lv_vg_lite_matrix_inverse(vg_lite_matrix_t * result, const vg_lite_matrix_t * matrix) +{ + vg_lite_float_t det00, det01, det02; + vg_lite_float_t d; + bool is_affine; + + /* Test for identity matrix. */ + if(matrix == NULL) { + result->m[0][0] = 1.0f; + result->m[0][1] = 0.0f; + result->m[0][2] = 0.0f; + result->m[1][0] = 0.0f; + result->m[1][1] = 1.0f; + result->m[1][2] = 0.0f; + result->m[2][0] = 0.0f; + result->m[2][1] = 0.0f; + result->m[2][2] = 1.0f; + + /* Success. */ + return true; + } + + det00 = (matrix->m[1][1] * matrix->m[2][2]) - (matrix->m[2][1] * matrix->m[1][2]); + det01 = (matrix->m[2][0] * matrix->m[1][2]) - (matrix->m[1][0] * matrix->m[2][2]); + det02 = (matrix->m[1][0] * matrix->m[2][1]) - (matrix->m[2][0] * matrix->m[1][1]); + + /* Compute determinant. */ + d = (matrix->m[0][0] * det00) + (matrix->m[0][1] * det01) + (matrix->m[0][2] * det02); + + /* Return 0 if there is no inverse matrix. */ + if(d == 0.0f) + return false; + + /* Compute reciprocal. */ + d = 1.0f / d; + + /* Determine if the matrix is affine. */ + is_affine = (matrix->m[2][0] == 0.0f) && (matrix->m[2][1] == 0.0f) && (matrix->m[2][2] == 1.0f); + + result->m[0][0] = d * det00; + result->m[0][1] = d * ((matrix->m[2][1] * matrix->m[0][2]) - (matrix->m[0][1] * matrix->m[2][2])); + result->m[0][2] = d * ((matrix->m[0][1] * matrix->m[1][2]) - (matrix->m[1][1] * matrix->m[0][2])); + result->m[1][0] = d * det01; + result->m[1][1] = d * ((matrix->m[0][0] * matrix->m[2][2]) - (matrix->m[2][0] * matrix->m[0][2])); + result->m[1][2] = d * ((matrix->m[1][0] * matrix->m[0][2]) - (matrix->m[0][0] * matrix->m[1][2])); + result->m[2][0] = is_affine ? 0.0f : d * det02; + result->m[2][1] = is_affine ? 0.0f : d * ((matrix->m[2][0] * matrix->m[0][1]) - (matrix->m[0][0] * matrix->m[2][1])); + result->m[2][2] = is_affine ? 1.0f : d * ((matrix->m[0][0] * matrix->m[1][1]) - (matrix->m[1][0] * matrix->m[0][1])); + + /* Success. */ + return true; +} + +lv_point_precise_t lv_vg_lite_matrix_transform_point(const vg_lite_matrix_t * matrix, const lv_point_precise_t * point) +{ + lv_point_precise_t p; + p.x = point->x * matrix->m[0][0] + point->y * matrix->m[0][1] + matrix->m[0][2]; + p.y = point->x * matrix->m[1][0] + point->y * matrix->m[1][1] + matrix->m[1][2]; + return p; +} + +void lv_vg_lite_set_scissor_area(const lv_area_t * area) +{ + vg_lite_enable_scissor(); + vg_lite_set_scissor( + area->x1, + area->y1, + lv_area_get_width(area), + lv_area_get_height(area)); +} + +void lv_vg_lite_disable_scissor(void) +{ + vg_lite_disable_scissor(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ diff --git a/src/draw/vg_lite/lv_vg_lite_utils.h b/src/draw/vg_lite/lv_vg_lite_utils.h new file mode 100644 index 000000000..017aae131 --- /dev/null +++ b/src/draw/vg_lite/lv_vg_lite_utils.h @@ -0,0 +1,188 @@ +/** + * @file lv_vg_lite_utils.h + * + */ + +#ifndef LV_VG_LITE_UTILS_H +#define LV_VG_LITE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lvgl.h" + +#if LV_USE_DRAW_VG_LITE + +#include +#include + +/********************* + * DEFINES + *********************/ + +#define LV_VG_LITE_BUF_ALIGN 64 + +#define LV_VG_LITE_IS_ERROR(err) (err > 0) + +#define VLC_GET_OP_CODE(ptr) (*((uint8_t*)ptr)) + +#if LV_VG_LITE_USE_ASSERT +#define LV_VG_LITE_ASSERT(expr) LV_ASSERT(expr) +#else +#define LV_VG_LITE_ASSERT(expr) +#endif + +#define LV_VG_LITE_CHECK_ERROR(expr) \ + do { \ + vg_lite_error_t error = expr; \ + if (LV_VG_LITE_IS_ERROR(error)) { \ + LV_LOG_ERROR("Execute '" #expr "' error(%d): %s", \ + (int)error, lv_vg_lite_error_string(error)); \ + LV_VG_LITE_ASSERT(false); \ + } \ + } while (0) + +#define LV_VG_LITE_ASSERT_PATH(path) LV_VG_LITE_ASSERT(lv_vg_lite_path_check(path)) +#define LV_VG_LITE_ASSERT_SRC_BUFFER(buffer) LV_VG_LITE_ASSERT(lv_vg_lite_buffer_check(buffer, true)) +#define LV_VG_LITE_ASSERT_DEST_BUFFER(buffer) LV_VG_LITE_ASSERT(lv_vg_lite_buffer_check(buffer, false)) + +#define LV_VG_LITE_ALIGN(number, align_bytes) \ + (((number) + ((align_bytes)-1)) & ~((align_bytes)-1)) + +#define LV_VG_LITE_IS_ALIGNED(num, align) (((uintptr_t)(num) & ((align)-1)) == 0) + +#define LV_VG_LITE_IS_INDEX_FMT(fmt) \ + ((fmt) == VG_LITE_INDEX_1 \ + || (fmt) == VG_LITE_INDEX_2 \ + || (fmt) == VG_LITE_INDEX_4 \ + || (fmt) == VG_LITE_INDEX_8) + +/********************** + * TYPEDEFS + **********************/ + +typedef enum { + LV_VG_LITE_IMAGE_FLAG_ALLOCED = 1 << 0, + LV_VG_LITE_IMAGE_FLAG_ALIGNED = 1 << 1, + LV_VG_LITE_IMAGE_FLAG_PRE_MUL = 1 << 2, +} lv_vg_lite_image_flag_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/* Print info */ + +void lv_vg_lite_dump_info(void); + +const char * lv_vg_lite_error_string(vg_lite_error_t error); + +const char * lv_vg_lite_feature_string(vg_lite_feature_t feature); + +const char * lv_vg_lite_buffer_format_string(vg_lite_buffer_format_t format); + +const char * lv_vg_lite_filter_string(vg_lite_filter_t filter); + +const char * lv_vg_lite_blend_string(vg_lite_blend_t blend); + +const char * lv_vg_lite_global_alpha_string(vg_lite_global_alpha_t global_alpha); + +const char * lv_vg_lite_fill_rule_string(vg_lite_fill_t fill_rule); + +const char * lv_vg_lite_image_mode_string(vg_lite_buffer_image_mode_t image_mode); + +const char * lv_vg_lite_vlc_op_string(uint8_t vlc_op); + +void lv_vg_lite_path_dump_info(const vg_lite_path_t * path); + +void lv_vg_lite_buffer_dump_info(const vg_lite_buffer_t * buffer); + +void lv_vg_lite_matrix_dump_info(const vg_lite_matrix_t * matrix); + +bool lv_vg_lite_is_dest_cf_supported(lv_color_format_t cf); + +bool lv_vg_lite_is_src_cf_supported(lv_color_format_t cf); + +/* Converter */ + +vg_lite_buffer_format_t lv_vg_lite_vg_fmt(lv_color_format_t cf); + +void lv_vg_lite_buffer_format_bytes( + vg_lite_buffer_format_t format, + uint32_t * mul, + uint32_t * div, + uint32_t * bytes_align); + +uint32_t lv_vg_lite_width_to_stride(uint32_t w, vg_lite_buffer_format_t color_format); + +uint32_t lv_vg_lite_width_align(uint32_t w); + +bool lv_vg_lite_buffer_init( + vg_lite_buffer_t * buffer, + const void * ptr, + int32_t width, + int32_t height, + vg_lite_buffer_format_t format, + bool tiled); + +void lv_vg_lite_image_matrix(vg_lite_matrix_t * matrix, int32_t x, int32_t y, const lv_draw_image_dsc_t * dsc); + +bool lv_vg_lite_buffer_open_image(vg_lite_buffer_t * buffer, lv_image_decoder_dsc_t * decoder_dsc, const void * src); + +vg_lite_blend_t lv_vg_lite_blend_mode(lv_blend_mode_t blend_mode); + +uint32_t lv_vg_lite_get_palette_size(vg_lite_buffer_format_t format); + +vg_lite_color_t lv_vg_lite_color(lv_color_t color, lv_opa_t opa, bool pre_mul); + +void lv_vg_lite_rect(vg_lite_rectangle_t * rect, const lv_area_t * area); + +/* Param checker */ + +bool lv_vg_lite_buffer_check(const vg_lite_buffer_t * buffer, bool is_src); + +bool lv_vg_lite_path_check(const vg_lite_path_t * path); + +/* Wrapper */ + +bool lv_vg_lite_support_blend_normal(void); + +bool lv_vg_lite_16px_align(void); + +void lv_vg_lite_draw_linear_grad( + vg_lite_buffer_t * buffer, + vg_lite_path_t * path, + const lv_area_t * area, + const lv_grad_dsc_t * grad, + const vg_lite_matrix_t * matrix, + vg_lite_fill_t fill, + vg_lite_blend_t blend); + +void lv_vg_lite_matrix_multiply(vg_lite_matrix_t * matrix, const vg_lite_matrix_t * mult); + +void lv_vg_lite_matrix_flip_y(vg_lite_matrix_t * matrix); + +bool lv_vg_lite_matrix_inverse(vg_lite_matrix_t * result, const vg_lite_matrix_t * matrix); + +lv_point_precise_t lv_vg_lite_matrix_transform_point(const vg_lite_matrix_t * matrix, const lv_point_precise_t * point); + +void lv_vg_lite_set_scissor_area(const lv_area_t * area); + +void lv_vg_lite_disable_scissor(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*VG_LITE_UTILS_H*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index c9f8da3cf..13d363603 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -350,6 +350,35 @@ #endif #endif +/* Use VG-Lite GPU. */ +#ifndef LV_USE_DRAW_VG_LITE + #ifdef CONFIG_LV_USE_DRAW_VG_LITE + #define LV_USE_DRAW_VG_LITE CONFIG_LV_USE_DRAW_VG_LITE + #else + #define LV_USE_DRAW_VG_LITE 0 + #endif +#endif + +#if LV_USE_DRAW_VG_LITE +/* Enbale VG-Lite custom external 'gpu_init()' function */ +#ifndef LV_VG_LITE_USE_GPU_INIT + #ifdef CONFIG_LV_VG_LITE_USE_GPU_INIT + #define LV_VG_LITE_USE_GPU_INIT CONFIG_LV_VG_LITE_USE_GPU_INIT + #else + #define LV_VG_LITE_USE_GPU_INIT 0 + #endif +#endif + +/* Enable VG-Lite assert. */ +#ifndef LV_VG_LITE_USE_ASSERT + #ifdef CONFIG_LV_VG_LITE_USE_ASSERT + #define LV_VG_LITE_USE_ASSERT CONFIG_LV_VG_LITE_USE_ASSERT + #else + #define LV_VG_LITE_USE_ASSERT 0 + #endif +#endif +#endif + /*================= * OPERATING SYSTEM *=================*/ diff --git a/src/lv_init.c b/src/lv_init.c index 9914de011..bc9a4f35b 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -38,6 +38,9 @@ #if LV_USE_DRAW_SDL #include "draw/sdl/lv_draw_sdl.h" #endif +#if LV_USE_DRAW_VG_LITE + #include "draw/vg_lite/lv_draw_vg_lite.h" +#endif /********************* * DEFINES @@ -192,6 +195,10 @@ void lv_init(void) _lv_image_decoder_init(); lv_bin_decoder_init(); /*LVGL built-in binary image decoder*/ +#if LV_USE_DRAW_VG_LITE + lv_draw_vg_lite_init(); +#endif + _lv_cache_init(); _lv_cache_builtin_init(); lv_cache_lock(); @@ -357,6 +364,10 @@ void lv_deinit(void) lv_draw_vglite_deinit(); #endif +#if LV_USE_DRAW_VG_LITE + lv_draw_vg_lite_deinit(); +#endif + #if LV_USE_DRAW_SW lv_draw_sw_deinit(); #endif