diff --git a/Kconfig b/Kconfig index 0188e9d4cd537a80cbf3205fbbc29df396840ba1..fd7261e2559d8e17b95f96311f729bae4e2b8d34 100644 --- a/Kconfig +++ b/Kconfig @@ -422,6 +422,19 @@ menu "LVGL configuration" bool "Use Sunxi G2D platform" default n + config LV_USE_DRAW_GE2D + bool "Use ArtInChip GE2D GPU accelerator" + default n + + config LV_USE_MPP_DEC + bool "Use ArtInChip MPP hardware image decoder" + default n + + config LV_USE_AIC_BMP + bool "Use ArtInChip BMP decoder" + default y + depends on LV_USE_MPP_DEC + config LV_USE_VECTOR_GRAPHIC bool "Use Vector Graphic APIs" default n diff --git a/lv_conf_template.h b/lv_conf_template.h index 87754d7cc3762580f517ff0d91f464153435525b..95526f6a8b1dde9f5d576516305e8e6d7d8212ce 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -236,6 +236,15 @@ /* Use G2D GPU. */ #define LV_USE_DRAW_G2D 0 +/* Use ArtInChip GE2D GPU. */ +#define LV_USE_DRAW_GE2D 0 + +/* Use ArtInChip MPP hardware image decoder. */ +#define LV_USE_MPP_DEC 0 + +/* Use ArtInChip BMP decoder. */ +#define LV_USE_AIC_BMP 0 + /*======================= * FEATURE CONFIGURATION *=======================*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d.c b/src/draw/artinchip/ge2d/lv_draw_ge2d.c new file mode 100644 index 0000000000000000000000000000000000000000..0b4b6429b017dff8ca6e89629711f07c90c9bfaf --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2025 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include "lv_draw_ge2d.h" +#include "lv_draw_ge2d_utils.h" + +#if LV_USE_DRAW_GE2D + +#define DRAW_UNIT_ID_GE2D 10 + +static struct mpp_ge *ge2d_dev = NULL; + +static int32_t ge2d_evaluate(lv_draw_unit_t *draw_unit, lv_draw_task_t *task); + +static int32_t ge2d_dispatch(lv_draw_unit_t *draw_unit, lv_layer_t *layer); + +static int32_t ge2d_delete(lv_draw_unit_t * draw_unit); + +static void ge2d_execute_drawing(lv_draw_ge2d_unit_t *u); + +#if LV_USE_OS +static void ge2d_render_thread_cb(void *ptr); +#endif + +#define _draw_info LV_GLOBAL_DEFAULT()->draw_info + +static void lv_ge2d_init(void) +{ + ge2d_dev = mpp_ge_open(); + if (!ge2d_dev) { + LV_LOG_ERROR("open ge device fail"); + } +} + +static void lv_ge2d_deinit(void) +{ + if (ge2d_dev) { + mpp_ge_close(ge2d_dev); + ge2d_dev = NULL; + } +} + +struct mpp_ge *get_ge2d_device(void) +{ + return ge2d_dev; +} + +void lv_draw_ge2d_init(void) +{ + lv_draw_ge2d_unit_t *draw_ge2d_unit = lv_draw_create_unit(sizeof(lv_draw_ge2d_unit_t)); + draw_ge2d_unit->base_unit.evaluate_cb = ge2d_evaluate; + draw_ge2d_unit->base_unit.dispatch_cb = ge2d_dispatch; + draw_ge2d_unit->base_unit.delete_cb = LV_USE_OS ? ge2d_delete : NULL; + + lv_ge2d_init(); + +#if LV_USE_OS + lv_thread_init(&draw_ge2d_unit->thread, LV_THREAD_PRIO_HIGH, ge2d_render_thread_cb, + LV_DRAW_THREAD_STACKSIZE, draw_ge2d_unit); +#endif +} + +void lv_draw_ge2d_deinit(void) +{ + lv_ge2d_deinit(); +} + +void lv_draw_ge2d_rotate(const void *src_buf, void *dest_buf, int32_t src_width, int32_t src_height, + int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation, + lv_color_format_t cf) +{ + struct ge_bitblt blt = { 0 }; + enum mpp_pixel_format fmt = lv_fmt_to_mpp_fmt(cf); + + switch (rotation) { + case LV_DISPLAY_ROTATION_0: + blt.ctrl.flags = MPP_ROTATION_0; + break; + case LV_DISPLAY_ROTATION_90: + /* LV_DISP_ROT_90 means display rotate 90 degrees counterclockwise, + * so set degree to MPP_ROTATION_270 + */ + blt.ctrl.flags = MPP_ROTATION_270; + break; + case LV_DISPLAY_ROTATION_180: + blt.ctrl.flags = MPP_ROTATION_180; + break; + case LV_DISPLAY_ROTATION_270: + blt.ctrl.flags = MPP_ROTATION_90; + break; + default: + break; + } + + blt.src_buf.buf_type = MPP_PHY_ADDR; + blt.src_buf.phy_addr[0] = (uint32_t)(ulong)src_buf; + blt.src_buf.stride[0] = src_stride; + blt.src_buf.size.width = src_width; + blt.src_buf.size.height = src_height; + blt.src_buf.format = fmt; + + blt.dst_buf.buf_type = MPP_PHY_ADDR; + blt.dst_buf.phy_addr[0] = (uint32_t)(ulong)dest_buf; + blt.dst_buf.stride[0] = dest_stride; + blt.dst_buf.format = fmt; + if (rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) { + blt.dst_buf.size.width = src_height; + blt.dst_buf.size.height = src_width; + } else { + blt.dst_buf.size.width = src_width; + blt.dst_buf.size.height = src_height; + } + + int ret = mpp_ge_bitblt(ge2d_dev, &blt); + if (ret < 0) { + LV_LOG_ERROR("bitblt fail"); + return; + } + + ret = mpp_ge_emit(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("emit fail"); + return; + } + + ret = mpp_ge_sync(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("sync fail"); + return; + } + + return; +} + +#if defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) +static bool inline lv_draw_ge2d_buf_address_valid(lv_draw_buf_t *draw_buf) +{ + if ((uint32_t)(long)draw_buf->data < (uint32_t)0x40000000) { + return false; + } else { + return true; + } +} +#endif + +static bool ge2d_draw_img_supported(const lv_draw_image_dsc_t *draw_dsc) +{ + bool recolor = (draw_dsc->recolor_opa > LV_OPA_MIN); + bool scale = draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE; + + if (recolor) + return false; + + if (lv_image_src_get_type(draw_dsc->src) >= LV_IMAGE_SRC_SYMBOL) + return false; + +#if defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) + if (lv_image_src_get_type(draw_dsc->src) == LV_IMAGE_SRC_VARIABLE) { + if (!lv_draw_ge2d_buf_address_valid((lv_draw_buf_t *)draw_dsc->src)) { + return false; + } + } +#endif + + if (draw_dsc->header.w * draw_dsc->header.h < LV_GE2D_FILL_OPA_SIZE_LIMIT) { + if (lv_image_src_get_type(draw_dsc->src) == LV_IMAGE_SRC_FILE) { + if (!strcmp(lv_fs_get_ext(draw_dsc->src), "fake")) { + return true; + } + } + return false; + } + + if (lv_fmt_is_yuv(draw_dsc->header.cf)) { + if (draw_dsc->rotation % 900) + return false; + } + + if (draw_dsc->rotation % 900 && scale) + return false; + + // check scale ratio + if (draw_dsc->scale_x < LV_SCALE_NONE / 16 || draw_dsc->scale_y < LV_SCALE_NONE / 16 || + draw_dsc->scale_x > LV_SCALE_NONE * 16 || draw_dsc->scale_y > LV_SCALE_NONE * 16) { + return false; + } + + if (draw_dsc->header.cf < LV_COLOR_FORMAT_YUV_START) { + if (draw_dsc->header.stride % 8) { + LV_LOG_TRACE("stride:%d", draw_dsc->header.stride); + return false; + } + } + + if (draw_dsc->bitmap_mask_src) + return false; + + return true; +} + +static int32_t ge2d_evaluate(lv_draw_unit_t *u, lv_draw_task_t *t) +{ + LV_UNUSED(u); + + const lv_draw_dsc_base_t *draw_dsc_base = (lv_draw_dsc_base_t *)t->draw_dsc; + + if (!ge2d_dst_fmt_supported(draw_dsc_base->layer->color_format)) + return 0; + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: { + const lv_draw_fill_dsc_t * draw_dsc = (lv_draw_fill_dsc_t *) t->draw_dsc; + lv_draw_buf_t *dest_buf = draw_dsc->base.layer->draw_buf; + + if (!dest_buf) + return 0; + +#if defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) + if (!lv_draw_ge2d_buf_address_valid(dest_buf)) + return 0; +#endif + if ((draw_dsc->radius != 0) || (draw_dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE)) + return 0; + + if (t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_GE2D; + } + return 0; + } + + case LV_DRAW_TASK_TYPE_LAYER: { + const lv_draw_image_dsc_t *draw_dsc = (lv_draw_image_dsc_t *)t->draw_dsc; + lv_layer_t *layer= (lv_layer_t *)draw_dsc->src; + lv_draw_buf_t *dest_buf = draw_dsc->base.layer->draw_buf; + + if (!dest_buf) + return 0; + +#if defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) + if (!lv_draw_ge2d_buf_address_valid(dest_buf)) + return 0; +#endif + if (!ge2d_src_fmt_supported(layer->color_format)) + return 0; + + if (!ge2d_draw_img_supported(draw_dsc)) + return 0; + + if (t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_GE2D; + } + return 0; + } + + case LV_DRAW_TASK_TYPE_IMAGE: { + lv_draw_image_dsc_t *draw_dsc = (lv_draw_image_dsc_t *)t->draw_dsc; + lv_draw_buf_t *dest_buf = draw_dsc->base.layer->draw_buf; + + if (!dest_buf) + return 0; + +#if defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) + if (!lv_draw_ge2d_buf_address_valid(dest_buf)) { + return 0; + } +#endif + if (!ge2d_src_fmt_supported(draw_dsc->header.cf)) + return 0; + + if (!ge2d_draw_img_supported(draw_dsc)) + return 0; + + if (t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_GE2D; + } + + return 0; + } + default: + return 0; + } + + return 0; +} + +static inline void execute_drawing_unit(lv_draw_sw_unit_t *u) +{ + ge2d_execute_drawing(u); + + u->task_act->state = LV_DRAW_TASK_STATE_READY; + u->task_act = NULL; + + /*The draw unit is free now. Request a new dispatching as it can get a new task*/ + lv_draw_dispatch_request(); +} + +static int32_t ge2d_dispatch(lv_draw_unit_t *draw_unit, lv_layer_t *layer) +{ + LV_PROFILER_BEGIN; + lv_draw_ge2d_unit_t * draw_ge2d_unit = (lv_draw_ge2d_unit_t *) draw_unit; + + /*Return immediately if it's busy with draw task*/ + if (draw_ge2d_unit->task_act) { + LV_PROFILER_END; + return 0; + } + + lv_draw_task_t *t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_GE2D); + + if (t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_GE2D) { + LV_PROFILER_END; + return -1; + } + + void *buf = lv_draw_layer_alloc_buf(layer); + if (buf == NULL) { + LV_PROFILER_END; + return -1; + } + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_ge2d_unit->base_unit.target_layer = layer; + draw_ge2d_unit->base_unit.clip_area = &t->clip_area; + draw_ge2d_unit->task_act = t; + +#if LV_USE_OS + if(draw_ge2d_unit->inited) + lv_thread_sync_signal(&draw_ge2d_unit->sync); +#else + execute_drawing_unit(draw_ge2d_unit); +#endif + LV_PROFILER_END; + return 1; +} + +static int32_t ge2d_delete(lv_draw_unit_t *draw_unit) +{ +#if LV_USE_OS + lv_draw_ge2d_unit_t * draw_ge2d_unit = (lv_draw_ge2d_unit_t *) draw_unit; + + LV_LOG_INFO("ge2d_delete"); + draw_ge2d_unit->exit_status = true; + + if(draw_ge2d_unit->inited) + lv_thread_sync_signal(&draw_ge2d_unit->sync); + + return lv_thread_delete(&draw_ge2d_unit->thread); +#else + LV_UNUSED(draw_unit); + return 0; +#endif +} + +static inline void lv_flush_draw_area(lv_draw_unit_t *draw_unit, + const lv_area_t *draw_area, + lv_layer_t *layer) +{ + lv_area_t clipped_area; + if (!_lv_area_intersect(&clipped_area, draw_area, draw_unit->clip_area)) + return; + + lv_area_move(&clipped_area, -layer->buf_area.x1, -layer->buf_area.y1); + lv_draw_buf_flush_cache(layer->draw_buf, &clipped_area); +} + +static inline void lv_flush_clip_area(lv_draw_unit_t *draw_unit, lv_layer_t *layer) +{ + lv_area_t clipped_area; + lv_area_copy(&clipped_area, draw_unit->clip_area); + lv_area_move(&clipped_area, -layer->buf_area.x1, -layer->buf_area.y1); + lv_draw_buf_flush_cache(layer->draw_buf, &clipped_area); +} + +static inline void lv_flush_image_area(lv_draw_unit_t *draw_unit, + const lv_draw_image_dsc_t *draw_dsc, + const lv_area_t *draw_area, + lv_layer_t *layer) +{ + if (draw_dsc->tile) + lv_flush_clip_area(draw_unit, layer); + else + lv_flush_draw_area(draw_unit, draw_area, layer); +} + +static void ge2d_execute_drawing(lv_draw_ge2d_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 = draw_unit->target_layer; + + switch (t->type) { + case LV_DRAW_TASK_TYPE_FILL: + lv_flush_draw_area(draw_unit, &t->area, layer); + lv_draw_ge2d_fill(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_flush_image_area(draw_unit, t->draw_dsc, &t->area, layer); + lv_draw_ge2d_img(draw_unit, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_LAYER: + lv_flush_clip_area(draw_unit, layer); + lv_draw_ge2d_layer(draw_unit, t->draw_dsc, &t->area); + break; + default: + break; + } + +#if LV_USE_PARALLEL_DRAW_DEBUG + /*Layers manage it for themselves*/ + if (t->type != LV_DRAW_TASK_TYPE_LAYER) { + lv_area_t draw_area; + if (!_lv_area_intersect(&draw_area, &t->area, u->base_unit.clip_area)) + return; + + int32_t idx = 0; + lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head; + while (draw_unit_tmp != (lv_draw_unit_t *)u) { + draw_unit_tmp = draw_unit_tmp->next; + idx++; + } + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_palette_main(idx % _LV_PALETTE_LAST); + fill_dsc.opa = LV_OPA_10; + lv_draw_sw_fill((lv_draw_unit_t *)u, &fill_dsc, &draw_area); + + lv_draw_border_dsc_t border_dsc; + lv_draw_border_dsc_init(&border_dsc); + border_dsc.color = fill_dsc.color; + border_dsc.opa = LV_OPA_80; + border_dsc.width = 1; + lv_draw_sw_border((lv_draw_unit_t *)u, &border_dsc, &draw_area); + + lv_point_t txt_size; + lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE); + + lv_area_t txt_area; + txt_area.x1 = draw_area.x1; + txt_area.y1 = draw_area.y1; + txt_area.x2 = draw_area.x1 + txt_size.x - 1; + txt_area.y2 = draw_area.y1 + txt_size.y - 1; + + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_color_white(); + lv_draw_sw_fill((lv_draw_unit_t *)u, &fill_dsc, &txt_area); + + char buf[8]; + lv_snprintf(buf, sizeof(buf), "%d", idx); + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_color_make(0x00, 0x00, 0xff); + label_dsc.text = buf; + lv_draw_sw_label((lv_draw_unit_t *)u, &label_dsc, &txt_area); + } +#endif +} + +#if LV_USE_OS +static void ge2d_render_thread_cb(void *ptr) +{ + lv_draw_ge2d_unit_t *u = ptr; + + lv_thread_sync_init(&u->sync); + u->inited = true; + + while(1) { + while (u->task_act == NULL) { + if(u->exit_status) + break; + + lv_thread_sync_wait(&u->sync); + } + + if (u->exit_status) { + LV_LOG_INFO("Ready to exit ge2d draw thread."); + break; + } + execute_drawing_unit(u); + } + + u->inited = false; + lv_thread_sync_delete(&u->sync); + LV_LOG_INFO("Exit ge2d draw thread."); +} +#endif + +#endif /*LV_USE_DRAW_GE2D*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d.h b/src/draw/artinchip/ge2d/lv_draw_ge2d.h new file mode 100644 index 0000000000000000000000000000000000000000..c84cc0ba23373a776226df3a32eda4e89fb05b16 --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_DRAW_GE2D_H +#define LV_DRAW_GE2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" +#include "mpp_ge.h" + +#ifndef LV_GE2D_FILL_SIZE_LIMIT +#define LV_GE2D_FILL_SIZE_LIMIT 100 * 100 +#endif + +#ifndef LV_GE2D_FILL_OPA_SIZE_LIMIT +#define LV_GE2D_FILL_OPA_SIZE_LIMIT 100 * 100 +#endif + +#ifndef LV_GE2D_BLIT_SIZE_LIMIT +#define LV_GE2D_BLIT_SIZE_LIMIT 100 * 100 +#endif + +#ifndef LV_GE2D_BLIT_OPA_SIZE_LIMIT +#define LV_GE2D_BLIT_OPA_SIZE_LIMIT 40 * 40 +#endif + +typedef lv_draw_sw_unit_t lv_draw_ge2d_unit_t; + +struct mpp_ge *get_ge2d_device(void); + +void lv_draw_ge2d_init(void); + +void lv_draw_ge2d_deinit(void); + +void lv_draw_ge2d_rotate(const void *src_buf, void *dest_buf, int32_t src_width, int32_t src_height, + int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation, + lv_color_format_t cf); + +void lv_draw_ge2d_fill_with_blend(lv_draw_unit_t *draw_unit, const lv_draw_fill_dsc_t *dsc, + const lv_area_t *coords, int32_t alpha_en); + +void lv_draw_ge2d_fill(lv_draw_unit_t *draw_unit, const lv_draw_fill_dsc_t *dsc, + const lv_area_t *coords); + +void lv_draw_ge2d_img(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *dsc, + const lv_area_t *coords); + +void lv_draw_ge2d_layer(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, + const lv_area_t *coords); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_DRAW_GE2D_H*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d_fill.c b/src/draw/artinchip/ge2d/lv_draw_ge2d_fill.c new file mode 100644 index 0000000000000000000000000000000000000000..0f7c6bb0c990182bc683c873ff25535c275f337d --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d_fill.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include "lv_draw_ge2d.h" + +#if LV_USE_DRAW_GE2D +#include "lv_draw_ge2d_utils.h" + +void lv_draw_ge2d_fill_with_blend(lv_draw_unit_t *draw_unit, const lv_draw_fill_dsc_t *dsc, + const lv_area_t *coords, int32_t alpha_en) +{ + lv_area_t blend_area; + if (!_lv_area_intersect(&blend_area, coords, draw_unit->clip_area)) + return; + + lv_layer_t *layer = draw_unit->target_layer; + lv_draw_buf_t *draw_buf = layer->draw_buf; + lv_area_move(&blend_area, -layer->buf_area.x1, -layer->buf_area.y1); + + int32_t blend_width = lv_area_get_width(&blend_area); + int32_t blend_height = lv_area_get_height(&blend_area); + lv_coord_t dest_width = lv_area_get_width(&layer->buf_area); + lv_coord_t dest_height = lv_area_get_height(&layer->buf_area); + + struct ge_fillrect fill = { 0 }; + struct mpp_ge *ge2d_device = get_ge2d_device(); + + /* fill info */ + fill.type = GE_NO_GRADIENT; + + if (dsc->opa >= LV_OPA_MAX) { + fill.start_color = lv_color_to_u32(dsc->color); + } else { + fill.start_color = dsc->color.blue | (dsc->color.green << 8) + | (dsc->color.red << 16) | (dsc->opa << 24); + } + + fill.end_color = 0; + + /* dst buf */ + fill.dst_buf.buf_type = MPP_PHY_ADDR; + fill.dst_buf.phy_addr[0] = (int32_t)(long)draw_buf->data; + fill.dst_buf.stride[0] = draw_buf->header.stride; + fill.dst_buf.size.width = dest_width; + fill.dst_buf.size.height = dest_height; + fill.dst_buf.format = lv_fmt_to_mpp_fmt(draw_buf->header.cf); + fill.dst_buf.crop_en = 1; + fill.dst_buf.crop.x = blend_area.x1; + fill.dst_buf.crop.y = blend_area.y1; + fill.dst_buf.crop.width = blend_width; + fill.dst_buf.crop.height = blend_height; + + /* ctrl */ + if (alpha_en) + fill.ctrl.alpha_en = 1; + + fill.ctrl.src_alpha_mode = 0; + + int ret = mpp_ge_fillrect(ge2d_device, &fill); + if (ret < 0) { + LV_LOG_WARN("fillrect1 fail"); + return; + } + ret = mpp_ge_emit(ge2d_device); + if (ret < 0) { + LV_LOG_WARN("emit fail"); + return; + } + ret = mpp_ge_sync(ge2d_device); + if (ret < 0) { + LV_LOG_WARN("sync fail"); + return; + } +} + +void lv_draw_ge2d_fill(lv_draw_unit_t *draw_unit, const lv_draw_fill_dsc_t *dsc, + const lv_area_t *coords) +{ + int32_t alpha_en; + if (dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + if (dsc->opa < LV_OPA_MAX) + alpha_en = 1; + else + alpha_en = 0; + + lv_draw_ge2d_fill_with_blend(draw_unit, dsc, coords, alpha_en); +} + +#endif /*LV_USE_DRAW_GE2D*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d_img.c b/src/draw/artinchip/ge2d/lv_draw_ge2d_img.c new file mode 100644 index 0000000000000000000000000000000000000000..979254696da47f82fca27512786933bec8a8a3f7 --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d_img.c @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2025-2026 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include "lv_draw_ge2d.h" +#if LV_USE_DRAW_GE2D + +#include "lv_draw_ge2d_utils.h" +#include +#include + +//#define GE2D_IMAGE_DEBUG + +#define PI 3.141592653589f +#define SIN(x) (sinf((x)* PI / 180.0f)) +#define COS(x) (cosf((x)* PI / 180.0f)) + +typedef struct { + int32_t x_in; + int32_t y_in; + int32_t x_out; + int32_t y_out; + int32_t sinma; + int32_t cosma; + int32_t scale_x; + int32_t scale_y; + int32_t angle; + int32_t pivot_x_256; + int32_t pivot_y_256; + lv_point_t pivot; +} point_transform_t; + +static void ge2d_img_draw_core(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, + const lv_image_decoder_dsc_t *decoder_dsc, lv_draw_image_sup_t *sup, + const lv_area_t *img_coords, const lv_area_t *clipped_img_area); +extern void lv_draw_ge2d_scale(lv_draw_unit_t *draw_unit, const lv_draw_sw_blend_dsc_t *blend_dsc, const lv_draw_image_dsc_t *draw_dsc, + const lv_area_t *clipped_area, const lv_draw_buf_t *decoded); + +#define _draw_info LV_GLOBAL_DEFAULT()->draw_info + +void lv_draw_ge2d_layer(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, const lv_area_t *coords) +{ + lv_layer_t *layer_to_draw = (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_to_draw->draw_buf == NULL) return; + + lv_draw_image_dsc_t new_draw_dsc = *draw_dsc; + new_draw_dsc.src = layer_to_draw->draw_buf; + lv_draw_ge2d_img(draw_unit, &new_draw_dsc, coords); +#if LV_USE_LAYER_DEBUG || LV_USE_PARALLEL_DRAW_DEBUG + lv_area_t area_rot; + lv_area_copy(&area_rot, coords); + if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) { + int32_t w = lv_area_get_width(coords); + int32_t h = lv_area_get_height(coords); + + _lv_image_buf_get_transformed_area(&area_rot, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y, + &draw_dsc->pivot); + + area_rot.x1 += coords->x1; + area_rot.y1 += coords->y1; + area_rot.x2 += coords->x1; + area_rot.y2 += coords->y1; + } + lv_area_t draw_area; + if(!_lv_area_intersect(&draw_area, &area_rot, draw_unit->clip_area)) return; +#endif + +#if LV_USE_LAYER_DEBUG + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_color_hex(layer_to_draw->color_format == LV_COLOR_FORMAT_ARGB8888 ? 0xff0000 : 0x00ff00); + fill_dsc.opa = LV_OPA_20; + lv_draw_sw_fill(draw_unit, &fill_dsc, &area_rot); + + lv_draw_border_dsc_t border_dsc; + lv_draw_border_dsc_init(&border_dsc); + border_dsc.color = fill_dsc.color; + border_dsc.opa = LV_OPA_60; + border_dsc.width = 2; + lv_draw_sw_border(draw_unit, &border_dsc, &area_rot); + +#endif + +#if LV_USE_PARALLEL_DRAW_DEBUG + uint32_t idx = 0; + lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head; + while(draw_unit_tmp != draw_unit) { + draw_unit_tmp = draw_unit_tmp->next; + idx++; + } + + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_palette_main(idx % _LV_PALETTE_LAST); + fill_dsc.opa = LV_OPA_10; + lv_draw_sw_fill(draw_unit, &fill_dsc, &area_rot); + + lv_draw_border_dsc_t border_dsc; + lv_draw_border_dsc_init(&border_dsc); + border_dsc.color = lv_palette_main(idx % _LV_PALETTE_LAST); + border_dsc.opa = LV_OPA_100; + border_dsc.width = 2; + lv_draw_sw_border(draw_unit, &border_dsc, &area_rot); + + lv_point_t txt_size; + lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE); + + lv_area_t txt_area; + txt_area.x1 = draw_area.x1; + txt_area.x2 = draw_area.x1 + txt_size.x - 1; + txt_area.y2 = draw_area.y2; + txt_area.y1 = draw_area.y2 - txt_size.y + 1; + + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color = lv_color_black(); + lv_draw_sw_fill(draw_unit, &fill_dsc, &txt_area); + + char buf[8]; + lv_snprintf(buf, sizeof(buf), "%d", idx); + lv_draw_label_dsc_t label_dsc; + lv_draw_label_dsc_init(&label_dsc); + label_dsc.color = lv_color_make(0x00, 0x00, 0xff); + label_dsc.text = buf; + lv_draw_sw_label(draw_unit, &label_dsc, &txt_area); +#endif +} + +static inline void ge2d_sync(struct mpp_ge *ge2d_dev) +{ + int ret; + + ret = mpp_ge_emit(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("emit fail"); + return; + } + + ret = mpp_ge_sync(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("sync fail"); + return; + } + + return; +} + +void lv_draw_ge2d_img(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, + const lv_area_t *coords) +{ + + if (lv_image_src_get_type(draw_dsc->src) == LV_IMAGE_SRC_FILE) { + const char *ptr = lv_fs_get_ext(draw_dsc->src); + if (!strcmp(ptr, "fake")) { + int alpha_en; + unsigned int color; + + { + char *p = (char *)draw_dsc->src + 3; + char *n; + strtol(p, &n, 10); /* skip width */ + strtol(n + 1, &n, 10); /* skip height */ + alpha_en = (int)strtol(n + 1, &n, 10); + color = (unsigned int)strtoul(n + 1, NULL, 16); + } + lv_draw_fill_dsc_t fill_dsc; + lv_draw_fill_dsc_init(&fill_dsc); + fill_dsc.color.blue = color & 0xff; + fill_dsc.color.green = (color >> 8) & 0xff; + fill_dsc.color.red = (color >> 16) & 0xff; + fill_dsc.opa = (color >> 24) & 0xff; + + lv_area_t clipped_img_area; + if(!_lv_area_intersect(&clipped_img_area, coords, draw_unit->clip_area)) { + return; + } + + _lv_image_buf_get_transformed_area(&clipped_img_area, lv_area_get_width(coords), lv_area_get_height(coords), + draw_dsc->rotation, draw_dsc->scale_x, + draw_dsc->scale_y, &draw_dsc->pivot); + lv_area_move(&clipped_img_area, coords->x1, coords->y1); + lv_draw_ge2d_fill_with_blend(draw_unit, &fill_dsc, &clipped_img_area, alpha_en); + return; + } + } + + if(!draw_dsc->tile) { + _lv_draw_image_normal_helper(draw_unit, draw_dsc, coords, ge2d_img_draw_core); + } else { + _lv_draw_image_tiled_helper(draw_unit, draw_dsc, coords, ge2d_img_draw_core); + } +} + +static inline bool yuv_size_is_invalid(int32_t src_w, int32_t src_h, int32_t dst_w, int32_t dst_h) +{ + if (src_w < 8 || src_h < 8 || dst_w < 8 || dst_h < 8) { + return true; + } + + return false; +} + +static inline bool rgb_size_is_invalid(int32_t src_w, int32_t src_h, int32_t dst_w, int32_t dst_h) +{ + if (src_w < 4 || src_h < 4 || dst_w < 4 || dst_h < 4) { + return true; + } + + return false; +} + +void lv_draw_ge2d_blit(lv_draw_unit_t *draw_unit, const lv_draw_sw_blend_dsc_t *blend_dsc) +{ + if (blend_dsc->opa <= LV_OPA_MIN) + return; + + lv_area_t blend_area; + + int32_t src_w = lv_area_get_width(blend_dsc->blend_area); + int32_t src_h = lv_area_get_height(blend_dsc->blend_area); + + if (!_lv_area_intersect(&blend_area, blend_dsc->blend_area, draw_unit->clip_area)) { + LV_LOG_ERROR("_lv_area_intersect"); + return; + } + + lv_layer_t *layer = draw_unit->target_layer; + lv_color_format_t dst_color_format = layer->color_format; + uint32_t layer_stride_byte = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dst_color_format); + + int32_t dest_w = lv_area_get_width(&blend_area); + int32_t dest_h = lv_area_get_height(&blend_area); + int32_t dest_stride = layer_stride_byte; + lv_opa_t opa = blend_dsc->opa; + int32_t src_stride = blend_dsc->src_stride; + lv_color_format_t src_color_format = blend_dsc->src_color_format; + const uint8_t *src_buf = blend_dsc->src_buf; + const uint8_t *dst_buf = layer->draw_buf->data; + + int32_t src_x = blend_area.x1 - blend_dsc->src_area->x1; + int32_t src_y = blend_area.y1 - blend_dsc->src_area->y1; + int32_t dst_x = blend_area.x1 - layer->buf_area.x1; + int32_t dst_y = blend_area.y1 - layer->buf_area.y1; + + struct mpp_ge *ge2d_dev = get_ge2d_device(); + struct ge_bitblt blt = { 0 }; + + // set src buf + if (lv_fmt_is_yuv(src_color_format)) { + if (yuv_size_is_invalid(dest_w, dest_h, dest_w, dest_h)) { + LV_LOG_TRACE("invalid w:%d, h:%d", dest_w, dest_h); + return; + } + + struct mpp_buf *buf = (struct mpp_buf *)src_buf; + blt.src_buf.phy_addr[0] = buf->phy_addr[0]; + blt.src_buf.phy_addr[1] = buf->phy_addr[1]; + blt.src_buf.phy_addr[2] = buf->phy_addr[2]; + blt.src_buf.stride[0] = buf->stride[0]; + blt.src_buf.stride[1] = buf->stride[1]; + } else { + blt.src_buf.phy_addr[0] = (unsigned int)(ulong)src_buf; + blt.src_buf.stride[0] = src_stride; + } + + blt.src_buf.buf_type = MPP_PHY_ADDR; + blt.src_buf.size.width = src_w; + blt.src_buf.size.height = src_h; + blt.src_buf.format = lv_fmt_to_mpp_fmt(src_color_format);; + blt.src_buf.crop_en = 1; + blt.src_buf.crop.x = src_x; + blt.src_buf.crop.y = src_y; + blt.src_buf.crop.width = dest_w; + blt.src_buf.crop.height = dest_h; + +#ifdef GE2D_IMAGE_DEBUG + LV_LOG_INFO("blend_area.x1:%d", blend_area.x1); + LV_LOG_INFO("blend_area.y1:%d", blend_area.y1); + LV_LOG_INFO("blend_area.x2:%d", blend_area.x2); + LV_LOG_INFO("blend_area.y2:%d", blend_area.y2); + LV_LOG_INFO("src_area.x1:%d", blend_dsc->src_area->x1); + LV_LOG_INFO("src_area.y1:%d", blend_dsc->src_area->y1); + LV_LOG_INFO("src_area.x2:%d", blend_dsc->src_area->x2); + LV_LOG_INFO("src_area.y2:%d", blend_dsc->src_area->y2); + LV_LOG_INFO("src_x:%d", src_x); + LV_LOG_INFO("src_y:%d", src_x); + LV_LOG_INFO("src_w:%d", dest_w); + LV_LOG_INFO("src_h:%d", dest_h); + LV_LOG_INFO("dst_x:%d", dst_x); + LV_LOG_INFO("dst_y:%d", dst_y); + LV_LOG_INFO("dst_w:%d", dest_w); + LV_LOG_INFO("dst_h:%d", dest_h); + LV_LOG_INFO("dst_stride:%d", dest_stride); +#endif + + // set dst buf + blt.dst_buf.buf_type = MPP_PHY_ADDR; + blt.dst_buf.phy_addr[0] = (unsigned int)(ulong)dst_buf; + blt.dst_buf.stride[0] = dest_stride; + blt.dst_buf.size.width = layer->draw_buf->header.w; + blt.dst_buf.size.height = layer->draw_buf->header.h; + blt.dst_buf.format = lv_fmt_to_mpp_fmt(dst_color_format); + blt.dst_buf.crop_en = 1; + blt.dst_buf.crop.x = dst_x;; + blt.dst_buf.crop.y = dst_y; + blt.dst_buf.crop.width = dest_w; + blt.dst_buf.crop.height = dest_h; + + if(opa >= LV_OPA_MAX && src_color_format != LV_COLOR_FORMAT_ARGB8888) + blt.ctrl.alpha_en = 0; + else + blt.ctrl.alpha_en = 1; + + blt.ctrl.src_alpha_mode = 2; + blt.ctrl.src_global_alpha = opa; + +#ifdef AIC_GE_DITHER + if (dst_color_format == LV_COLOR_FORMAT_RGB565) { + blt.ctrl.dither_en = 1; + if (blt.ctrl.alpha_en || blt.src_buf.format == MPP_FMT_RGB_565) + blt.ctrl.dither_en = 0; + } +#endif + + int ret = mpp_ge_bitblt(ge2d_dev, &blt); + if (ret < 0) { + LV_LOG_ERROR("bitblt fail"); + return; + } + + ge2d_sync(ge2d_dev); +} + +static void transform_point_inverse(point_transform_t *t, int32_t xin, int32_t yin, int32_t *xout, + int32_t *yout, int32_t src_w, int32_t src_h) +{ + if(t->angle == 0 && t->scale_x == LV_SCALE_NONE && t->scale_y == LV_SCALE_NONE) { + *xout = xin * 256; + *yout = yin * 256; + return; + } + + xin -= t->pivot.x; + yin -= t->pivot.y; + + if (t->angle == 0) { + *xout = ((int32_t)(xin * 256 * 256 / t->scale_x)) + (t->pivot_x_256); + *yout = ((int32_t)(yin * 256 * 256 / t->scale_y)) + (t->pivot_y_256); + } else if (t->scale_x == LV_SCALE_NONE && t->scale_y == LV_SCALE_NONE) { + *xout = ((t->cosma * xin - t->sinma * yin) >> 2) + (t->pivot_x_256); + *yout = ((t->sinma * xin + t->cosma * yin) >> 2) + (t->pivot_y_256); + } else { + *xout = (((t->cosma * xin - t->sinma * yin) * 256 / t->scale_x) >> 2) + (t->pivot_x_256); + *yout = (((t->sinma * xin + t->cosma * yin) * 256 / t->scale_y) >> 2) + (t->pivot_y_256); + } + + *xout = LV_CLAMP(0, (*xout + 128) >> 8, src_w); + *yout = LV_CLAMP(0, (*yout + 128) >> 8, src_h); +} + +static void image_buf_get_inverse_transform(lv_area_t *in_area, lv_area_t *out_area, int32_t rotation, + uint16_t scale_x, uint16_t scale_y, const lv_point_t *pivot, + int32_t src_w, int32_t src_h) +{ + int32_t angle_low = 0; + int32_t angle_high = 0; + int32_t angle_rem = 0; + int32_t s1 = 0; + int32_t s2 = 0; + int32_t c1 = 0; + int32_t c2 = 0; + point_transform_t tr_dsc = { 0 }; + lv_point_t out_p[4] = { 0 }; + + tr_dsc.angle = -rotation; + tr_dsc.scale_x = scale_x; + tr_dsc.scale_y = scale_y; + tr_dsc.pivot.x = pivot->x; + tr_dsc.pivot.y = pivot->y; + + angle_low = tr_dsc.angle / 10; + angle_high = angle_low + 1; + angle_rem = tr_dsc.angle - (angle_low * 10); + + s1 = lv_trigo_sin(angle_low); + s2 = lv_trigo_sin(angle_high); + + c1 = lv_trigo_sin(angle_low + 90); + c2 = lv_trigo_sin(angle_high + 90); + + tr_dsc.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + tr_dsc.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + tr_dsc.sinma = tr_dsc.sinma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.cosma = tr_dsc.cosma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.pivot_x_256 = tr_dsc.pivot.x * 256; + tr_dsc.pivot_y_256 = tr_dsc.pivot.y * 256; + + transform_point_inverse(&tr_dsc, in_area->x1, in_area->y1, + &out_p[0].x, &out_p[0].y, src_w, src_h); + transform_point_inverse(&tr_dsc, in_area->x2 + 1, in_area->y1, + &out_p[1].x, &out_p[1].y, src_w, src_h); + transform_point_inverse(&tr_dsc, in_area->x1, in_area->y2 + 1, + &out_p[2].x, &out_p[2].y, src_w, src_h); + transform_point_inverse(&tr_dsc, in_area->x2 + 1, in_area->y2 + 1, + &out_p[3].x, &out_p[3].y, src_w, src_h); + + out_area->x1 = LV_MIN4(out_p[0].x, out_p[1].x, out_p[2].x, out_p[3].x); + out_area->x2 = LV_MAX4(out_p[0].x, out_p[1].x, out_p[2].x, out_p[3].x); + out_area->y1 = LV_MIN4(out_p[0].y, out_p[1].y, out_p[2].y, out_p[3].y); + out_area->y2 = LV_MAX4(out_p[0].y, out_p[1].y, out_p[2].y, out_p[3].y); +} + +void lv_draw_ge2d_transform(lv_draw_unit_t *draw_unit, const lv_draw_sw_blend_dsc_t *blend_dsc, + const lv_draw_image_dsc_t *draw_dsc, const lv_area_t *clipped_area, const lv_draw_buf_t *decoded) +{ + int32_t src_w = 0; + int32_t src_h = 0; + lv_area_t in_area = { 0 }; + lv_area_t out_area = { 0 }; + + lv_layer_t *layer = draw_unit->target_layer; + lv_color_format_t dst_color_format = layer->color_format; + uint32_t layer_stride_byte = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dst_color_format); + + lv_area_copy(&in_area, clipped_area); + lv_area_move(&in_area, -blend_dsc->src_area->x1, -blend_dsc->src_area->y1); + + src_w = lv_area_get_width(blend_dsc->src_area); + src_h = lv_area_get_height(blend_dsc->src_area); + + image_buf_get_inverse_transform(&in_area, &out_area, draw_dsc->rotation, + draw_dsc->scale_x, draw_dsc->scale_y, &draw_dsc->pivot, + src_w, src_h); + + src_w = lv_area_get_width(&out_area); + src_h = lv_area_get_height(&out_area); + + + int32_t dest_w = lv_area_get_width(clipped_area); + int32_t dest_h = lv_area_get_height(clipped_area); + int32_t dest_stride = layer_stride_byte; + lv_opa_t opa = blend_dsc->opa; + int32_t src_stride = blend_dsc->src_stride; + lv_color_format_t src_color_format = blend_dsc->src_color_format; + const uint8_t *src_buf = blend_dsc->src_buf; + const uint8_t *dst_buf = layer->draw_buf->data; + + int32_t src_x = out_area.x1; + int32_t src_y = out_area.y1; + int32_t dst_x = clipped_area->x1 - layer->buf_area.x1; + int32_t dst_y = clipped_area->y1 - layer->buf_area.y1; + + if (src_x + src_w > decoded->header.w) { + src_w = decoded->header.w - src_x; + } + + if (src_y + src_h > decoded->header.h) { + src_h = decoded->header.h - src_y; + } + + struct mpp_ge *ge2d_dev = get_ge2d_device(); + struct ge_bitblt blt = { 0 }; + int32_t angle = draw_dsc->rotation; + + // set src buf + if (lv_fmt_is_yuv(src_color_format)) { + if (yuv_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + LV_LOG_TRACE("invalid src_w:%d, src_h:%d, dst_w:%d, dst_h:%d", src_w, src_h, dest_w, dest_h); + return; + } + + struct mpp_buf *buf = (struct mpp_buf *)src_buf; + blt.src_buf.phy_addr[0] = buf->phy_addr[0]; + blt.src_buf.phy_addr[1] = buf->phy_addr[1]; + blt.src_buf.phy_addr[2] = buf->phy_addr[2]; + blt.src_buf.stride[0] = buf->stride[0]; + blt.src_buf.stride[1] = buf->stride[1]; + } else { + if (rgb_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + LV_LOG_TRACE("invalid src_w:%d, src_h:%d, dst_w:%d, dst_h:%d", src_w, src_h, dest_w, dest_h); + return; + } + blt.src_buf.phy_addr[0] = (unsigned int)(ulong)src_buf; + blt.src_buf.stride[0] = src_stride; + } + + blt.src_buf.buf_type = MPP_PHY_ADDR; + blt.src_buf.size.width = src_w + src_x; + blt.src_buf.size.height = src_h + src_y; + blt.src_buf.format = lv_fmt_to_mpp_fmt(src_color_format); + blt.src_buf.crop_en = 1; + blt.src_buf.crop.x = src_x; + blt.src_buf.crop.y = src_y; + blt.src_buf.crop.width = src_w; + blt.src_buf.crop.height = src_h; + + // set dst buf + blt.dst_buf.buf_type = MPP_PHY_ADDR; + blt.dst_buf.phy_addr[0] = (unsigned int)(ulong)dst_buf; + blt.dst_buf.stride[0] = dest_stride; + blt.dst_buf.size.width = layer->draw_buf->header.w; + blt.dst_buf.size.height = layer->draw_buf->header.h; + blt.dst_buf.format = lv_fmt_to_mpp_fmt(dst_color_format); + blt.dst_buf.crop_en = 1; + blt.dst_buf.crop.x = dst_x;; + blt.dst_buf.crop.y = dst_y; + blt.dst_buf.crop.width = dest_w; + blt.dst_buf.crop.height = dest_h; + + if(opa >= LV_OPA_MAX && src_color_format != LV_COLOR_FORMAT_ARGB8888) + blt.ctrl.alpha_en = 0; + else + blt.ctrl.alpha_en = 1; + +#ifdef GE2D_IMAGE_DEBUG + if (angle == 2700 || angle == 1800) { + LV_LOG_INFO("scale_x:%d, scale_y:%d", draw_dsc->scale_x, draw_dsc->scale_y); + LV_LOG_INFO("src_x:%d", src_x); + LV_LOG_INFO("src_y:%d", src_y); + LV_LOG_INFO("src_w:%d", src_w); + LV_LOG_INFO("src_h:%d", src_h); + LV_LOG_INFO("dst_x:%d", dst_x); + LV_LOG_INFO("dst_y:%d", dst_y); + LV_LOG_INFO("dst_w:%d", dest_w); + LV_LOG_INFO("dst_h:%d", dest_h); + } +#endif + + blt.ctrl.src_alpha_mode = 2; + blt.ctrl.src_global_alpha = opa; + + while(angle < 0) angle += 3600; + while(angle >= 3600) angle -= 3600; + blt.ctrl.flags = angle / 900; + + int ret = mpp_ge_bitblt(ge2d_dev, &blt); + if (ret < 0) { + LV_LOG_ERROR("bitblt fail"); + return; + } + + ge2d_sync(ge2d_dev); +} + +void lv_draw_ge2d_rotate_any_degree(lv_draw_unit_t *draw_unit, const lv_draw_sw_blend_dsc_t *blend_dsc, + const lv_draw_image_dsc_t *draw_dsc, const lv_area_t *clipped_area) +{ + int32_t src_w; + int32_t src_h; + lv_area_t in_area; + + lv_layer_t *layer = draw_unit->target_layer; + lv_color_format_t dst_color_format = layer->color_format; + uint32_t layer_stride_byte = lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dst_color_format); + + lv_area_copy(&in_area, clipped_area); + lv_area_move(&in_area, -blend_dsc->src_area->x1, -blend_dsc->src_area->y1); + + src_w = lv_area_get_width(blend_dsc->src_area); + src_h = lv_area_get_height(blend_dsc->src_area); + + int32_t dest_w = lv_area_get_width(clipped_area); + int32_t dest_h = lv_area_get_height(clipped_area); + int32_t dest_stride = layer_stride_byte; + lv_opa_t opa = blend_dsc->opa; + int32_t src_stride = blend_dsc->src_stride; + lv_color_format_t src_color_format = blend_dsc->src_color_format; + const uint8_t *src_buf = blend_dsc->src_buf; + const uint8_t *dst_buf = layer->draw_buf->data; + + int32_t dst_x = clipped_area->x1 - layer->buf_area.x1; + int32_t dst_y = clipped_area->y1 - layer->buf_area.y1; + + struct mpp_ge *ge2d_dev = get_ge2d_device(); + struct ge_rotation rot = { 0 }; + int32_t angle = draw_dsc->rotation; + + if (rgb_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + LV_LOG_TRACE("invalid src_w:%d, src_h:%d, dst_w:%d, dst_h:%d", src_w, src_h, dest_w, dest_h); + return; + } + + // set src buf + rot.src_buf.buf_type = MPP_PHY_ADDR; + rot.src_buf.phy_addr[0] = (unsigned int)(ulong)src_buf; + rot.src_buf.stride[0] = src_stride; + rot.src_buf.size.width = src_w; + rot.src_buf.size.height = src_h; + rot.src_buf.format = lv_fmt_to_mpp_fmt(src_color_format); + + // src center + rot.src_rot_center.x = draw_dsc->pivot.x; + rot.src_rot_center.y = draw_dsc->pivot.y; + + // set dst buf + rot.dst_buf.buf_type = MPP_PHY_ADDR; + rot.dst_buf.phy_addr[0] = (unsigned int)(ulong)dst_buf; + rot.dst_buf.stride[0] = dest_stride; + rot.dst_buf.size.width = layer->draw_buf->header.w; + rot.dst_buf.size.height = layer->draw_buf->header.h; + rot.dst_buf.format = lv_fmt_to_mpp_fmt(dst_color_format); + rot.dst_buf.crop_en = 1; + rot.dst_buf.crop.x = dst_x;; + rot.dst_buf.crop.y = dst_y; + rot.dst_buf.crop.width = dest_w; + rot.dst_buf.crop.height = dest_h; + + // dst center + rot.dst_rot_center.x = blend_dsc->src_area->x1 + draw_dsc->pivot.x - dst_x; + rot.dst_rot_center.y = blend_dsc->src_area->y1 + draw_dsc->pivot.y - dst_y; + + while(angle < 0) angle += 3600; + while(angle >= 3600) angle -= 3600; + + // angle + rot.angle_sin = SIN((float)angle / 10.0f) * 4096; + rot.angle_cos = COS((float)angle / 10.0f) * 4096; + + if(opa >= LV_OPA_MAX && src_color_format != LV_COLOR_FORMAT_ARGB8888) + rot.ctrl.alpha_en = 0; + else + rot.ctrl.alpha_en = 1; + + rot.ctrl.src_alpha_mode = 2; + rot.ctrl.src_global_alpha = opa; + + int ret = mpp_ge_rotate(ge2d_dev, &rot); + if (ret < 0) { + LV_LOG_ERROR("bitblt fail"); + return; + } + + ge2d_sync(ge2d_dev); +} + +static void ge2d_img_draw_core(lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, + const lv_image_decoder_dsc_t *decoder_dsc, lv_draw_image_sup_t *sup, + const lv_area_t *img_coords, const lv_area_t *clipped_img_area) +{ + lv_draw_sw_blend_dsc_t blend_dsc; + const lv_draw_buf_t *decoded = decoder_dsc->decoded; + const uint8_t * src_buf = decoded->data; + uint32_t img_stride = decoded->header.stride; + + if (draw_dsc->opa <= LV_OPA_MIN) + return; + + lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); + blend_dsc.opa = draw_dsc->opa; + blend_dsc.blend_mode = draw_dsc->blend_mode; + blend_dsc.src_stride = img_stride; + blend_dsc.src_buf = src_buf; + blend_dsc.color = draw_dsc->recolor; + blend_dsc.src_color_format = decoded->header.cf; + blend_dsc.src_area = img_coords; + blend_dsc.blend_area = img_coords; + + bool scaled = draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE ? true : false; + bool transformed = draw_dsc->rotation != 0 || scaled ? true : false; + bool fix_rotated = draw_dsc->rotation % 900 == 0; + + // mpp buffer already flushed cached, so only flush other type buffer + if (decoded->header.flags & LV_IMAGE_FLAGS_MODIFIABLE) { + lv_draw_buf_flush_cache(decoded, NULL); + } + + if (!transformed) { + lv_area_t clipped_coords; + if (!_lv_area_intersect(&clipped_coords, img_coords, draw_unit->clip_area)) + return; + + lv_draw_ge2d_blit(draw_unit, &blend_dsc); + } else if (fix_rotated || scaled) { + if ((!lv_fmt_is_yuv(decoded->header.cf)) && scaled && draw_dsc->rotation == 0) { + lv_draw_ge2d_scale(draw_unit, &blend_dsc, draw_dsc, clipped_img_area, decoded); + } else { + lv_draw_ge2d_transform(draw_unit, &blend_dsc, draw_dsc, clipped_img_area, decoded); + } + } else { + lv_draw_ge2d_rotate_any_degree(draw_unit, &blend_dsc, draw_dsc, clipped_img_area); + } +} + +#endif /*LV_USE_DRAW_GE2D*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d_img_scale.c b/src/draw/artinchip/ge2d/lv_draw_ge2d_img_scale.c new file mode 100644 index 0000000000000000000000000000000000000000..b04ad00b22c8e10dace01d11c63ae87b6f4a3444 --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d_img_scale.c @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2025-2026, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Cui Jiawei + */ + +#include "lv_draw_ge2d.h" +#if LV_USE_DRAW_GE2D + +#include "lv_draw_ge2d_utils.h" +#include + +#define GE2D_DEBUG_ENABLE 0 + +#if defined(AIC_CHIP_D12X) ||defined(AIC_CHIP_D13X) || defined(AIC_CHIP_G73X) +#define CONFIG_GE2D_SPLIT_OPT +#endif + +#if GE2D_DEBUG_ENABLE +#define GE2D_DEBUG_PRINTF(fmt, ...) LV_LOG_USER(fmt, ##__VA_ARGS__) +#else +#define GE2D_DEBUG_PRINTF(fmt, ...) \ + do { \ + } while (0) +#endif + +#define MAX_TIMES 256 + +static inline bool yuv_size_is_invalid(int32_t src_w, int32_t src_h, int32_t dst_w, int32_t dst_h) +{ + if (src_w < 8 || src_h < 8 || dst_w < 8 || dst_h < 8) { + return true; + } + return false; +} + +static inline bool rgb_size_is_invalid(int32_t src_w, int32_t src_h, int32_t dst_w, int32_t dst_h) +{ + if (src_w < 4 || src_h < 4 || dst_w < 4 || dst_h < 4) { + return true; + } + return false; +} + +static void swap_int32(int32_t *a, int32_t *b) +{ + int32_t temp = *a; + *a = *b; + *b = temp; +} + +static int ge2d_sync(struct mpp_ge *ge2d_dev) +{ + int ret; + + ret = mpp_ge_emit(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("emit fail"); + return ret; + } + + ret = mpp_ge_sync(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("sync fail"); + return ret; + } + + return ret; +} + +static void rotate_area(lv_area_t *area, int32_t pivot_x, int32_t pivot_y, int32_t rotation) +{ + if (rotation == 0) { + return; + } + + /* Calculate coordinates relative to rotation center */ + int32_t x1_rel = area->x1 - pivot_x + 1; + int32_t y1_rel = area->y1 - pivot_y + 1; + int32_t x2_rel = area->x2 - pivot_x + 1; + int32_t y2_rel = area->y2 - pivot_y + 1; + + int32_t new_x1, new_y1, new_x2, new_y2; + + /* Transform coordinates according to rotation angle */ + switch (rotation) { + case 900: /* 90 degrees clockwise */ + /* (x,y) -> (y, -x) */ + new_x1 = y1_rel; + new_y1 = -x2_rel; + new_x2 = y2_rel; + new_y2 = -x1_rel; + break; + + case 1800: /* 180 degrees */ + /* (x,y) -> (-x, -y) */ + new_x1 = -x2_rel; + new_y1 = -y2_rel; + new_x2 = -x1_rel; + new_y2 = -y1_rel; + break; + + case 2700: /* 270 degrees clockwise (or 90 degrees counterclockwise) */ + /* (x,y) -> (-y, x) */ + new_x1 = -y2_rel; + new_y1 = x1_rel; + new_x2 = -y1_rel; + new_y2 = x2_rel; + break; + + default: + /* Not a multiple of 90 degrees, do nothing */ + return; + } + + /* Convert back to absolute coordinates */ + area->x1 = new_x1 + pivot_x - 1; + area->y1 = new_y1 + pivot_y - 1; + area->x2 = new_x2 + pivot_x - 1; + area->y2 = new_y2 + pivot_y - 1; + + /* Ensure x1<=x2, y1<=y2 */ + if (area->x1 > area->x2) { + swap_int32(&area->x1, &area->x2); + } + if (area->y1 > area->y2) { + swap_int32(&area->y1, &area->y2); + } +} + +static void config_scale(struct ge_bitblt *blt) +{ + int channel_num; + int i; + int dx[2] = { 0 }; + int dy[2] = { 0 }; + int h_phase[2] = { 0 }; + int v_phase[2] = { 0 }; + int in_w_ch0 = blt->src_buf.crop.width; + int in_h_ch0 = blt->src_buf.crop.height; + + dx[0] = blt->scale_phase.dx_16[0]; + dy[0] = blt->scale_phase.dy_16[0]; + h_phase[0] = blt->scale_phase.h_phase_16[0]; + v_phase[0] = blt->scale_phase.v_phase_16[0]; + + switch (blt->src_buf.format) { + case MPP_FMT_ARGB_8888: + case MPP_FMT_ABGR_8888: + case MPP_FMT_RGBA_8888: + case MPP_FMT_BGRA_8888: + case MPP_FMT_XRGB_8888: + case MPP_FMT_XBGR_8888: + case MPP_FMT_RGBX_8888: + case MPP_FMT_BGRX_8888: + case MPP_FMT_RGB_888: + case MPP_FMT_BGR_888: + case MPP_FMT_ARGB_1555: + case MPP_FMT_ABGR_1555: + case MPP_FMT_RGBA_5551: + case MPP_FMT_BGRA_5551: + case MPP_FMT_RGB_565: + case MPP_FMT_BGR_565: + case MPP_FMT_ARGB_4444: + case MPP_FMT_ABGR_4444: + case MPP_FMT_RGBA_4444: + case MPP_FMT_BGRA_4444: + /* RGB format - single channel */ + channel_num = 1; + break; + + case MPP_FMT_YUV400: + /* YUV400 - single channel grayscale */ + channel_num = 1; + break; + + case MPP_FMT_YUV420P: + case MPP_FMT_NV12: + case MPP_FMT_NV21: + /* YUV420 - 2 channels */ + channel_num = 2; + dx[0] = dx[0] & (~1); /* Align to even number */ + dy[0] = dy[0] & (~1); + h_phase[0] = h_phase[0] & (~1); + v_phase[0] = v_phase[0] & (~1); + + blt->scale_phase.in_w_ch1 = in_w_ch0 >> 1; + blt->scale_phase.in_h_ch1 = in_h_ch0 >> 1; + + /* UV component (half of Y) */ + dx[1] = dx[0] >> 1; + dy[1] = dy[0] >> 1; + h_phase[1] = h_phase[0] >> 1; + v_phase[1] = v_phase[0] >> 1; + break; + + case MPP_FMT_YUV422P: + case MPP_FMT_NV16: + case MPP_FMT_NV61: + case MPP_FMT_YUYV: + case MPP_FMT_YVYU: + case MPP_FMT_UYVY: + case MPP_FMT_VYUY: + /* YUV422 - 2 channels */ + channel_num = 2; + dx[0] = dx[0] & (~1); /* Horizontal alignment */ + h_phase[0] = h_phase[0] & (~1); + + blt->scale_phase.in_w_ch1 = in_w_ch0 >> 1; + blt->scale_phase.in_h_ch1 = in_h_ch0; + + /* UV component (horizontal halved, vertical unchanged) */ + dx[1] = dx[0] >> 1; + dy[1] = dy[0]; + h_phase[1] = h_phase[0] >> 1; + v_phase[1] = v_phase[0]; + break; + + case MPP_FMT_YUV444P: + /* YUV444 - 2 channels */ + channel_num = 2; + dx[1] = dx[0]; + dy[1] = dy[0]; + h_phase[1] = h_phase[0]; + v_phase[1] = v_phase[0]; + blt->scale_phase.in_w_ch1 = in_w_ch0; + blt->scale_phase.in_h_ch1 = in_h_ch0; + break; + + default: + return; + } + + /* Set scaling parameters */ + blt->scale_phase.channel_num = channel_num; + blt->scale_phase.scaler_en = 1; + + for (i = 0; i < channel_num; i++) { + blt->scale_phase.dx_16[i] = dx[i]; + blt->scale_phase.dy_16[i] = dy[i]; + blt->scale_phase.h_phase_16[i] = h_phase[i]; + blt->scale_phase.v_phase_16[i] = v_phase[i]; + } +} + +static int calculate_crop_area(int32_t *src_start_x_16, int32_t *src_start_y_16, + int32_t *dest_crop_x, int32_t *dest_crop_y, + int32_t *dest_w, int32_t *dest_h, + int32_t dx_16, int32_t dy_16, + int32_t src_w, int32_t src_h, + int32_t *src_crop_x, int32_t *src_crop_y, + int32_t *needed_input_w, int32_t *needed_input_h) +{ + int32_t src_start_x = *src_start_x_16; + int32_t src_start_y = *src_start_y_16; + int32_t d_crop_x = *dest_crop_x; + int32_t d_crop_y = *dest_crop_y; + int32_t d_w = *dest_w; + int32_t d_h = *dest_h; + int max_times = 0; + int32_t need_w_fixed_16; + int32_t need_h_fixed_16; + + while (src_start_x < 0 && max_times < MAX_TIMES) { + src_start_x += dx_16; + d_crop_x += 1; + d_w -=1; + max_times++; + } + + max_times = 0; + while (src_start_y < 0 && max_times < MAX_TIMES) { + src_start_y += dy_16; + d_crop_y += 1; + d_h -=1; + max_times++; + } + + need_w_fixed_16 = src_start_x + (d_w - 1) * dx_16; + need_h_fixed_16 = src_start_y + (d_h - 1) * dy_16; + + max_times = 0; + while ((((need_w_fixed_16 + 0xffff) >> 16) > src_w - 1) && max_times < MAX_TIMES) { + need_w_fixed_16 -= dx_16; + d_w -= 1; + max_times++; + } + + max_times = 0; + while ((((need_h_fixed_16 + 0xffff) >> 16) > src_h - 1) && max_times < MAX_TIMES) { + need_h_fixed_16 -= dy_16; + d_h -= 1; + max_times++; + } + + /* Starting position (integer part) */ + *src_crop_x = src_start_x >> 16; + *src_crop_y = src_start_y >> 16; + + *needed_input_w = ((need_w_fixed_16 + 0xffff) >> 16) - *src_crop_x; + *needed_input_h = ((need_h_fixed_16 + 0xffff) >> 16) - *src_crop_y; + + *src_start_x_16 = src_start_x; + *src_start_y_16 = src_start_y; + + *dest_w = d_w; + *dest_h = d_h; + *dest_crop_x = d_crop_x; + *dest_crop_y = d_crop_y; + + /* Add starting position offset */ +#if defined(AIC_CHIP_D12X) + *needed_input_w += 1; + *needed_input_h += 1; +#else + *needed_input_w += 2; + *needed_input_h += 2; +#endif + + return 0; +} + +#ifdef CONFIG_GE2D_SPLIT_OPT +static void calculate_split_params(int32_t dx_16, int32_t dest_w, int32_t src_start_x_16, + int32_t src_w, int32_t src_crop_x, int32_t *dst_w_sec, + int32_t *src_w_sec, int32_t *phase_x_sec, int32_t *offset_x_sec, + int32_t *adjusted_dest_w, int32_t *needed_input_w) +{ + *dst_w_sec = 0; + *src_w_sec = 0; + *phase_x_sec = 0; + *offset_x_sec = 0; + *adjusted_dest_w = dest_w; + + if (dx_16 < 65536 && dx_16 > 65536 / 32 * 29 && dest_w >= 32) { + int begin_phase = src_start_x_16 & 0xFFFF; + int next_phase = begin_phase; + int blk_cnt = 0; + int out_num = 0; + int split_flag = 0; + int final_phase = (src_start_x_16 & 0xFFFF) + dx_16 * (dest_w - 1); + + for (int i = 0; i < dest_w; i++) { + next_phase += dx_16; + if ((((next_phase >> 16) - (begin_phase >> 16)) < 31) && next_phase < final_phase) { + blk_cnt++; + } else { + blk_cnt++; + begin_phase = next_phase; + if (out_num + 32 == dest_w) { + split_flag = 1; + break; + } + out_num += blk_cnt; + blk_cnt = 0; + } + } + + if (split_flag) { + *dst_w_sec = 16; + *adjusted_dest_w = dest_w - *dst_w_sec; + + *needed_input_w = ((src_start_x_16 & 0xffff) + (*adjusted_dest_w - 1) * dx_16 + 0xffff) >> 16; +#if defined(AIC_CHIP_D12X) + *needed_input_w += 1; +#else + *needed_input_w += 2; +#endif + *phase_x_sec = (src_start_x_16 + dx_16 * (*adjusted_dest_w)) & 0xffff; + *offset_x_sec = (src_start_x_16 + dx_16 * (*adjusted_dest_w)) >> 16; + *src_w_sec = (((*phase_x_sec + dx_16 * (*dst_w_sec - 1)) + 0xffff) >> 16); +#if defined(AIC_CHIP_D12X) + *src_w_sec += 1; +#else + *src_w_sec += 2; +#endif + } + } +} + +static int handle_secondary_block(struct mpp_ge *ge2d_dev, struct ge_bitblt *blt, + int32_t src_crop_x, int32_t src_crop_y, + int32_t needed_input_h, int32_t dest_w, int32_t dst_x, + int32_t dst_y, int32_t dest_h, int32_t dx_16, + int32_t dy_16, int32_t phase_x_sec, int32_t src_start_y_16, + int32_t dst_w_sec, int32_t src_w_sec, int32_t offset_x_sec, + lv_draw_unit_t *draw_unit, const lv_draw_image_dsc_t *draw_dsc, + const lv_draw_sw_blend_dsc_t *blend_dsc) +{ + int ret; + + blt->src_buf.crop.x = offset_x_sec; + blt->src_buf.crop.y = src_crop_y; + blt->src_buf.crop.width = src_w_sec; + blt->src_buf.crop.height = needed_input_h; + blt->dst_buf.crop.x = dst_x + dest_w; + blt->dst_buf.crop.y = dst_y; + blt->dst_buf.crop.width = dst_w_sec; + blt->dst_buf.crop.height = dest_h; + blt->scale_phase.dx_16[0] = dx_16; + blt->scale_phase.dy_16[0] = dy_16; + blt->scale_phase.h_phase_16[0] = phase_x_sec; + blt->scale_phase.v_phase_16[0] = src_start_y_16 & 0xFFFF; + + if (blt->src_buf.crop.width < 4 || blt->src_buf.crop.height < 4 || + blt->dst_buf.crop.width < 4 || blt->dst_buf.crop.height < 4) { + GE2D_DEBUG_PRINTF("Secondary block min size requirement not met. Src: %dx%d, Dst: %dx%d. Falling back to software.", + blt->src_buf.crop.width, blt->src_buf.crop.height, + blt->dst_buf.crop.width, blt->dst_buf.crop.height); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return -1; + } + config_scale(blt); + + ret = mpp_ge_bitblt(ge2d_dev, blt); + if (ret < 0) { + GE2D_DEBUG_PRINTF("ERROR: mpp_ge_bitblt for secondary block failed with ret=%d\n", ret); + return ret; + } + + return 0; +} +#endif +void lv_draw_ge2d_scale(lv_draw_unit_t *draw_unit, const lv_draw_sw_blend_dsc_t *blend_dsc, const lv_draw_image_dsc_t *draw_dsc, + const lv_area_t *clipped_area, const lv_draw_buf_t *decoded) +{ + int32_t src_w = 0; + int32_t src_h = 0; + int rotation = 0; + lv_layer_t *layer = NULL; + lv_color_format_t dst_color_format = 0; + uint32_t layer_stride_byte = 0; + lv_area_t dest_area = { 0 }; + int32_t dest_w = 0; + int32_t dest_h = 0; + int32_t scale_x_8 = 0; + int32_t scale_y_8 = 0; + int32_t inv_scale_x_16 = 0; + int32_t inv_scale_y_16 = 0; + int32_t src_start_x_16 = 0; + int32_t src_start_y_16 = 0; + int32_t dx_16 = 0; + int32_t dy_16 = 0; + lv_opa_t opa = 0; + int32_t src_stride = 0; + lv_color_format_t src_color_format = 0; + const uint8_t *src_buf = NULL; + const uint8_t *dst_buf = NULL; + int32_t dst_x = 0; + int32_t dst_y = 0; + struct mpp_ge *ge2d_dev = NULL; + struct ge_bitblt blt = { 0 }; + int32_t src_crop_x = 0; + int32_t src_crop_y = 0; + int32_t needed_input_w = 0; + int32_t needed_input_h = 0; + int ret = 0; + + /* This version does not support YUV format */ + /* To scale normally, fall back to software rendering */ + if (lv_fmt_is_yuv(blend_dsc->src_color_format)) { + GE2D_DEBUG_PRINTF("Source is YUV format\n"); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + src_w = lv_area_get_width(blend_dsc->src_area); + src_h = lv_area_get_height(blend_dsc->src_area); + rotation = draw_dsc->rotation % 3600; + if (rotation < 0) + rotation += 3600; + layer = draw_unit->target_layer; + dst_color_format = layer->color_format; + layer_stride_byte = layer->draw_buf->header.stride; + + lv_area_copy(&dest_area, clipped_area); + + /* Revert the target area to pre-rotation state for easier calculation */ + rotate_area(&dest_area, draw_dsc->pivot.x + blend_dsc->src_area->x1, + draw_dsc->pivot.y + blend_dsc->src_area->y1, -rotation); + + lv_area_move(&dest_area, -blend_dsc->src_area->x1, -blend_dsc->src_area->y1); + + dest_w = lv_area_get_width(&dest_area); + dest_h = lv_area_get_height(&dest_area); + + GE2D_DEBUG_PRINTF("Input Params:\n"); + GE2D_DEBUG_PRINTF(" src_w=%d, src_h=%d\n", (int)src_w, (int)src_h); + GE2D_DEBUG_PRINTF(" scale_x=%d, scale_y=%d\n", (int)draw_dsc->scale_x, (int)draw_dsc->scale_y); + GE2D_DEBUG_PRINTF(" pivot.x=%d, pivot.y=%d\n", (int)draw_dsc->pivot.x, (int)draw_dsc->pivot.y); + GE2D_DEBUG_PRINTF("Source Area: x1=%d, y1=%d, x2=%d, y2=%d\n", (int)blend_dsc->src_area->x1, + (int)blend_dsc->src_area->y1, (int)blend_dsc->src_area->x2, (int)blend_dsc->src_area->y2); + GE2D_DEBUG_PRINTF(" opa=%d\n", blend_dsc->opa); + GE2D_DEBUG_PRINTF("Dest Area: x1=%d, y1=%d, x2=%d, y2=%d\n", (int)dest_area.x1, (int)dest_area.y1, + (int)dest_area.x2, (int)dest_area.y2); + + /* Scaling ratio (8.8 fixed point) */ + scale_x_8 = draw_dsc->scale_x; + scale_y_8 = draw_dsc->scale_y; + + /* Inverse scaling ratio (16.16 fixed point) */ + inv_scale_x_16 = (256 * 256 * 256) / scale_x_8; + inv_scale_y_16 = (256 * 256 * 256) / scale_y_8; + + src_start_x_16 = + ((dest_area.x1 - draw_dsc->pivot.x) * inv_scale_x_16) + (draw_dsc->pivot.x << 16); + src_start_y_16 = + ((dest_area.y1 - draw_dsc->pivot.y) * inv_scale_y_16) + (draw_dsc->pivot.y << 16); + + GE2D_DEBUG_PRINTF("Scale Factors: scale_x_8=%d, scale_y_8=%d\n", (int)scale_x_8, (int)scale_y_8); + GE2D_DEBUG_PRINTF("Inverse Scale: inv_scale_x_16=%d, inv_scale_y_16=%d\n", (int)inv_scale_x_16, + (int)inv_scale_y_16); + GE2D_DEBUG_PRINTF("Source Start before: src_start_x_16=%d, src_start_y_16=%d\n", (int)src_start_x_16, + (int)src_start_y_16); + + /* Step size (16.16 fixed point) */ + dx_16 = inv_scale_x_16; + dy_16 = inv_scale_y_16; + + GE2D_DEBUG_PRINTF("Source Start after: src_start_x_16=%d, src_start_y_16=%d\n", (int)src_start_x_16, + (int)src_start_y_16); + GE2D_DEBUG_PRINTF("Source Start Integer: x=%d, y=%d\n", (int)src_start_x_16 >> 16, + (int)src_start_y_16 >> 16); + GE2D_DEBUG_PRINTF("Step Size: dx_16=%d, dy_16=%d\n", (int)dx_16, (int)dy_16); + + opa = blend_dsc->opa; + src_stride = blend_dsc->src_stride; + src_color_format = blend_dsc->src_color_format; + src_buf = blend_dsc->src_buf; + dst_buf = layer->draw_buf->data; + + dst_x = clipped_area->x1 - layer->buf_area.x1; + dst_y = clipped_area->y1 - layer->buf_area.y1; + + GE2D_DEBUG_PRINTF(" dest_area=%d, dest_area->y1=%d\n", (int)dest_area.x1, (int)dest_area.y1); + GE2D_DEBUG_PRINTF(" layer->buf_area.x1=%d, layer->buf_area.y1=%d\n", (int)layer->buf_area.x1, + (int)layer->buf_area.y1); + GE2D_DEBUG_PRINTF("Destination: dst_x=%d, dst_y=%d\n", (int)dst_x, (int)dst_y); + GE2D_DEBUG_PRINTF("Source Color Format: %d\n", (int)src_color_format); + GE2D_DEBUG_PRINTF("Destination Color Format: %d\n", (int)dst_color_format); + + /* Calculate crop area */ + calculate_crop_area(&src_start_x_16, &src_start_y_16, &dst_x, &dst_y, &dest_w, &dest_h, + dx_16, dy_16, decoded->header.w, decoded->header.h, &src_crop_x, &src_crop_y, + &needed_input_w, &needed_input_h); + + ge2d_dev = get_ge2d_device(); + if (!ge2d_dev) { + GE2D_DEBUG_PRINTF("ERROR: GE2D device is NULL\n"); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + if (lv_fmt_is_yuv(src_color_format)) { + GE2D_DEBUG_PRINTF("Source is YUV format\n"); + if (yuv_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + GE2D_DEBUG_PRINTF("ERROR: YUV size invalid: src_w=%d, src_h=%d, dst_w=%d, dst_h=%d\n", + (int)src_w, (int)src_h, (int)dest_w, (int)dest_h); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + struct mpp_buf *buf = (struct mpp_buf *)src_buf; + blt.src_buf.phy_addr[0] = buf->phy_addr[0]; + blt.src_buf.phy_addr[1] = buf->phy_addr[1]; + blt.src_buf.phy_addr[2] = buf->phy_addr[2]; + blt.src_buf.stride[0] = buf->stride[0]; + blt.src_buf.stride[1] = buf->stride[1]; + } else { + GE2D_DEBUG_PRINTF("Source is RGB format\n"); + if (rgb_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + GE2D_DEBUG_PRINTF("ERROR: RGB size invalid: src_w=%d, src_h=%d, dst_w=%d, dst_h=%d\n", + (int)src_w, (int)src_h, (int)dest_w, (int)dest_h); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + blt.src_buf.phy_addr[0] = (unsigned int)(ulong)src_buf; + blt.src_buf.stride[0] = src_stride; + } + + blt.scale_phase.scale_phase_en = 1; + blt.scale_phase.dx_16[0] = dx_16; + blt.scale_phase.dy_16[0] = dy_16; + blt.scale_phase.h_phase_16[0] = src_start_x_16 & 0xFFFF; + blt.scale_phase.v_phase_16[0] = src_start_y_16 & 0xFFFF; + + GE2D_DEBUG_PRINTF("Scale Phase Params:\n"); + GE2D_DEBUG_PRINTF(" dx_16[0]=%d\n", blt.scale_phase.dx_16[0]); + GE2D_DEBUG_PRINTF(" dy_16[0]=%d\n", blt.scale_phase.dy_16[0]); + GE2D_DEBUG_PRINTF(" h_phase_16[0]=%d\n", blt.scale_phase.h_phase_16[0]); + GE2D_DEBUG_PRINTF(" v_phase_16[0]=%d\n", blt.scale_phase.v_phase_16[0]); + + src_w = needed_input_w + src_crop_x; +#ifdef CONFIG_GE2D_SPLIT_OPT + int32_t dst_w_sec = 0; + int32_t src_w_sec = 0; + int32_t phase_x_sec = 0; + int32_t offset_x_sec = 0; + + calculate_split_params(dx_16, dest_w, src_start_x_16, decoded->header.w, src_crop_x, + &dst_w_sec, &src_w_sec, &phase_x_sec, &offset_x_sec, + &dest_w, &needed_input_w); + +#endif + + /* Ensure width and height are valid */ + if (needed_input_w < 4 || needed_input_h < 4) { + GE2D_DEBUG_PRINTF("ERROR: Invalid needed input size: w=%d, h=%d\n", (int)needed_input_w, + (int)needed_input_h); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + if (rgb_size_is_invalid(src_w, src_h, dest_w, dest_h)) { + GE2D_DEBUG_PRINTF("invalid src_w:%d, src_h:%d, dst_w:%d, dst_h:%d", (int)src_w,(int) src_h, (int)dest_w, (int)dest_h); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + blt.src_buf.buf_type = MPP_PHY_ADDR; + blt.src_buf.format = lv_fmt_to_mpp_fmt(src_color_format); + blt.src_buf.crop_en = 1; + blt.src_buf.size.width = needed_input_w + src_crop_x; + blt.src_buf.size.height = needed_input_h + src_crop_y; + blt.src_buf.crop.x = src_crop_x; + blt.src_buf.crop.y = src_crop_y; + blt.src_buf.crop.width = needed_input_w; + blt.src_buf.crop.height = needed_input_h; + + blt.dst_buf.buf_type = MPP_PHY_ADDR; + blt.dst_buf.phy_addr[0] = (unsigned int)(ulong)dst_buf; + blt.dst_buf.stride[0] = layer_stride_byte; + blt.dst_buf.size.width = layer->draw_buf->header.w; + blt.dst_buf.size.height = layer->draw_buf->header.h; + blt.dst_buf.format = lv_fmt_to_mpp_fmt(dst_color_format); + blt.dst_buf.crop_en = 1; + blt.dst_buf.crop.x = dst_x; + blt.dst_buf.crop.y = dst_y; + blt.dst_buf.crop.width = dest_w; + blt.dst_buf.crop.height = dest_h; + + if (rotation % 1800) + swap_int32((int32_t*)&blt.dst_buf.crop.width, (int32_t*)&blt.dst_buf.crop.height); + + /* Set alpha blending */ + if (opa >= LV_OPA_MAX && src_color_format != LV_COLOR_FORMAT_ARGB8888) { + blt.ctrl.alpha_en = 0; + GE2D_DEBUG_PRINTF("Alpha disabled (opa=%d)\n", (int)opa); + } else { + blt.ctrl.alpha_en = 1; + GE2D_DEBUG_PRINTF("Alpha enabled (opa=%d)\n", (int)opa); + } + + blt.ctrl.src_alpha_mode = 2; + blt.ctrl.src_global_alpha = opa; + blt.ctrl.flags = rotation / 900; + + GE2D_DEBUG_PRINTF( + "Control Params: alpha_en=%d, src_alpha_mode=%d, src_global_alpha=%d, flags=%d\n", + blt.ctrl.alpha_en, blt.ctrl.src_alpha_mode, blt.ctrl.src_global_alpha, blt.ctrl.flags); + + /* Check if within valid range */ + if (src_crop_x >= 0 && src_crop_y >= 0 && (src_crop_x + needed_input_w) <= decoded->header.w + 1 && + (src_crop_y + needed_input_h) <= decoded->header.h + 1) { + GE2D_DEBUG_PRINTF(" Coordinates are within source bounds - OK\n"); + } else { + LV_LOG_ERROR(" ERROR: Coordinates are outside source bounds!\n"); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + + GE2D_DEBUG_PRINTF("blt.src_buf.size.width=%d\n", blt.src_buf.size.width); + GE2D_DEBUG_PRINTF("blt.src_buf.size.height=%d\n", blt.src_buf.size.height); + GE2D_DEBUG_PRINTF("blt.src_buf.format=%d\n", blt.src_buf.format); + GE2D_DEBUG_PRINTF("blt.src_buf.crop_en=%d\n", blt.src_buf.crop_en); + GE2D_DEBUG_PRINTF("blt.src_buf.crop.x=%d\n", blt.src_buf.crop.x); + GE2D_DEBUG_PRINTF("blt.src_buf.crop.y=%d\n", blt.src_buf.crop.y); + GE2D_DEBUG_PRINTF("blt.src_buf.crop.width=%d\n", blt.src_buf.crop.width); + GE2D_DEBUG_PRINTF("blt.src_buf.crop.height=%d\n", blt.src_buf.crop.height); + + GE2D_DEBUG_PRINTF("blt.dst_buf.stride[0]=%d\n", blt.dst_buf.stride[0]); + GE2D_DEBUG_PRINTF("blt.dst_buf.size.width=%d\n", blt.dst_buf.size.width); + GE2D_DEBUG_PRINTF("blt.dst_buf.size.height=%d\n", blt.dst_buf.size.height); + GE2D_DEBUG_PRINTF("blt.dst_buf.crop.x=%d\n", blt.dst_buf.crop.x); + GE2D_DEBUG_PRINTF("blt.dst_buf.crop.y=%d\n", blt.dst_buf.crop.y); + GE2D_DEBUG_PRINTF("blt.dst_buf.crop.width=%d\n", blt.dst_buf.crop.width); + GE2D_DEBUG_PRINTF("blt.dst_buf.crop.height=%d\n", blt.dst_buf.crop.height); + + config_scale(&blt); + + ret = mpp_ge_bitblt(ge2d_dev, &blt); + + if (ret < 0) { + GE2D_DEBUG_PRINTF("ERROR: mpp_ge_bitblt failed with ret=%d\n", (int)ret); + lv_draw_sw_image(draw_unit, draw_dsc, blend_dsc->blend_area); + return; + } + +#ifdef CONFIG_GE2D_SPLIT_OPT + if (dst_w_sec) { + blt.src_buf.size.width = src_w; + handle_secondary_block(ge2d_dev, &blt, src_crop_x, src_crop_y, + needed_input_h, dest_w, dst_x, dst_y, dest_h, + dx_16, dy_16, phase_x_sec, src_start_y_16, + dst_w_sec, src_w_sec, offset_x_sec, + draw_unit, draw_dsc, blend_dsc); + } +#endif + + ret = ge2d_sync(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("ge2d_sync\n"); + return; + } + +} + +#endif /*LV_USE_DRAW_GE2D*/ diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.c b/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..cab7544b60457d88e4df8f27c0cf8908b063b37e --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include "lv_draw_ge2d_utils.h" +#include "lv_draw_ge2d.h" + +enum mpp_pixel_format lv_fmt_to_mpp_fmt(lv_color_format_t cf) +{ + enum mpp_pixel_format fmt = LV_COLOR_FORMAT_RGB565; + + switch(cf) { + case LV_COLOR_FORMAT_RGB565: + fmt = MPP_FMT_RGB_565; + break; + case LV_COLOR_FORMAT_RGB888: + fmt = MPP_FMT_RGB_888; + break; + case LV_COLOR_FORMAT_ARGB8888: + fmt = MPP_FMT_ARGB_8888; + break; + case LV_COLOR_FORMAT_XRGB8888: + fmt = MPP_FMT_XRGB_8888; + break; + case LV_COLOR_FORMAT_I420: + fmt = MPP_FMT_YUV420P; + break; + case LV_COLOR_FORMAT_I422: + fmt = MPP_FMT_YUV422P; + break; + case LV_COLOR_FORMAT_I444: + fmt = MPP_FMT_YUV444P; + break; + case LV_COLOR_FORMAT_I400: + fmt = MPP_FMT_YUV400; + break; + default: + LV_LOG_ERROR("unsupported format:%d", cf); + break; + } + + return fmt; +} diff --git a/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.h b/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f134d063271acbedebe6032e13853cd6ca9f1dab --- /dev/null +++ b/src/draw/artinchip/ge2d/lv_draw_ge2d_utils.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 ArtInChip Technology Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_DRAW_GE2D_UTILS_H +#define LV_DRAW_GE2D_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" +#include "mpp_types.h" + +static inline bool ge2d_dst_fmt_supported(lv_color_format_t cf) +{ + bool supported = false; + + switch(cf) { + case LV_COLOR_FORMAT_RGB565: + case LV_COLOR_FORMAT_RGB888: + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + supported = true; + break; + default: + break; + } + + return supported; +} + +static inline bool ge2d_src_fmt_supported(lv_color_format_t cf) +{ + bool supported = false; + + switch(cf) { + case LV_COLOR_FORMAT_RGB565: + case LV_COLOR_FORMAT_RGB888: + case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: + case LV_COLOR_FORMAT_I420: + case LV_COLOR_FORMAT_I422: + case LV_COLOR_FORMAT_I444: + case LV_COLOR_FORMAT_I400: + case LV_COLOR_FORMAT_RAW: + supported = true; + break; + default: + break; + } + + return supported; +} + +static inline bool lv_fmt_is_yuv(lv_color_format_t fmt) +{ + if (fmt >= LV_COLOR_FORMAT_YUV_START) + return true; + else + return false; +} + +enum mpp_pixel_format lv_fmt_to_mpp_fmt(lv_color_format_t cf); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_DRAW_GE2D_UTILS_H*/ diff --git a/src/draw/artinchip/mpp_dec/lv_aic_bmp.c b/src/draw/artinchip/mpp_dec/lv_aic_bmp.c new file mode 100644 index 0000000000000000000000000000000000000000..4a33d6c02d8528c50c7c07fc69283b1b746a13ca --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_bmp.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2024-2025, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include + +#include "aic_core.h" +#include "lv_aic_stream.h" +#include "lv_mpp_dec.h" +#include "lv_aic_bmp.h" + +#if LV_USE_AIC_BMP + +lv_result_t lv_check_bmp_header(lv_stream_t *stream) +{ + uint32_t read_num; + uint8_t buf[128]; + lv_fs_res_t fs_res; + lv_result_t res = LV_RESULT_OK; + + fs_res = lv_aic_stream_read(stream, buf, 2, &read_num); + if (fs_res != LV_FS_RES_OK || read_num != 2) { + res = LV_RESULT_INVALID; + goto read_err; + } + + if (buf[0] != 0x42 || buf[1] != 0x4d) { + res = LV_RESULT_INVALID; + goto read_err; + } + +read_err: + return res; +} + +lv_result_t lv_bmp_decoder_info_from_stream(lv_stream_t *stream, lv_image_header_t *header) +{ + uint32_t read_num = 0; + uint8_t buf[64] = { 0 }; + int32_t w = 0; + int32_t h = 0; + uint16_t bpp = 0; + + lv_fs_res_t res = lv_aic_stream_read(stream, buf, 54, &read_num); + if(res != LV_FS_RES_OK || read_num != 54) + return LV_RESULT_INVALID; + + lv_memcpy(&w, buf + 18, 4); + lv_memcpy(&h, buf + 22, 4); + lv_memcpy(&bpp, buf + 28, 2); + + header->w = w; + header->h = LV_ABS(h); + header->stride = ((bpp * w + 31) / 32) * 4; + header->stride = ALIGN_UP((int32_t)(header->stride), 8); + + switch(bpp) { + case 16: header->cf = LV_COLOR_FORMAT_RGB565; break; + case 24: header->cf = LV_COLOR_FORMAT_RGB888; break; + case 32: header->cf = LV_COLOR_FORMAT_ARGB8888; break; + default: return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_bmp_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file) +{ + lv_fs_res_t res; + lv_stream_t stream; + + if(is_file) + res = lv_aic_stream_open_file(&stream, src); + else + res = lv_aic_stream_open_buf(&stream, src, size); + + if(res != LV_FS_RES_OK) return LV_RESULT_INVALID; + + res = lv_bmp_decoder_info_from_stream(&stream, header); + lv_aic_stream_close(&stream); + return res; +} + +lv_result_t lv_bmp_dec_open(lv_image_decoder_t *decoder, lv_image_decoder_dsc_t *dsc) +{ + lv_result_t res = LV_RESULT_OK; + lv_stream_t stream; + uint8_t headers[64]; + uint32_t read_num = 0; + lv_fs_res_t fs_res; + int32_t width; + int32_t height; + int32_t abs_height; + uint16_t bpp; + int32_t stride; + int32_t stride_aligned; + int32_t image_size; + unsigned int align_addr; + uint8_t *buf = NULL; + int i; + + if (lv_aic_stream_open(&stream, dsc->src) != LV_FS_RES_OK) + return LV_RESULT_INVALID; + + mpp_decoder_data_t *mpp_data = (mpp_decoder_data_t *)lv_malloc_zeroed(sizeof(mpp_decoder_data_t)); + CHECK_PTR(mpp_data); + + fs_res = lv_aic_stream_read(&stream, headers, 54, &read_num); + if (fs_res != LV_FS_RES_OK || read_num != 54) { + LV_LOG_ERROR("lv_aic_stream_read failed"); + res = LV_RESULT_INVALID; + goto out; + } + + lv_memcpy(&width, headers + 18, 4); + lv_memcpy(&height, headers + 22, 4); + lv_memcpy(&bpp, headers + 28, 2); + + stride = ((bpp * width + 31) / 32) * 4; + stride_aligned = ALIGN_UP(stride, 8); + abs_height = LV_ABS(height); + image_size = ALIGN_UP(abs_height * stride_aligned, CACHE_LINE_SIZE); + + mpp_data->data[0] = (uint8_t *)mpp_dec_malloc_oom(image_size + CACHE_LINE_SIZE - 1); + if (!mpp_data->data[0]) { + res = LV_RESULT_INVALID; + goto out; + } else { + align_addr = (unsigned int)ALIGN_UP((ulong)mpp_data->data[0], CACHE_LINE_SIZE); + } + + mpp_data->decoded.header.w = width; + mpp_data->decoded.header.h = abs_height; + mpp_data->decoded.header.cf = dsc->header.cf; + mpp_data->decoded.header.magic = LV_IMAGE_HEADER_MAGIC; + mpp_data->decoded.header.stride = stride_aligned; + mpp_data->decoded.data =(uint8_t *)((ulong)align_addr); + mpp_data->decoded.data_size = image_size; + mpp_data->decoded.unaligned_data = mpp_data->data[0]; + + if (height < 0) { + buf = mpp_data->decoded.data; + if (stride == stride_aligned) { + lv_aic_stream_read(&stream, buf, image_size, &read_num); + } else { + for (i = 0; i < abs_height; i++) { + lv_aic_stream_read(&stream, buf, stride, &read_num); + buf += stride_aligned; + } + } + } else { + for (i = height - 1; i >= 0; i--) { + buf = mpp_data->decoded.data + i * stride_aligned; + lv_aic_stream_read(&stream, buf, stride, &read_num); + } + } + aicos_dcache_clean_invalid_range((ulong *)(ulong)align_addr, ALIGN_UP(image_size, CACHE_LINE_SIZE)); + + dsc->decoded = &mpp_data->decoded; + if (!dsc->args.no_cache && lv_image_cache_is_enabled()) { + lv_image_cache_data_t search_key; + search_key.src_type = dsc->src_type; + search_key.src = dsc->src; + search_key.slot.size = dsc->decoded->data_size; + mpp_data->cached = true; + lv_cache_entry_t *cache_entry = lv_image_decoder_add_to_cache(decoder, &search_key, dsc->decoded, mpp_data); + CHECK_PTR(cache_entry); + dsc->cache_entry = cache_entry; + } + +out: + if (res == LV_RESULT_INVALID) { + dsc->decoded = NULL; + if (mpp_data) { + lv_frame_buf_free(mpp_data); + lv_free(mpp_data); + } + } + + lv_aic_stream_close(&stream); + return res; +} + +#endif diff --git a/src/draw/artinchip/mpp_dec/lv_aic_bmp.h b/src/draw/artinchip/mpp_dec/lv_aic_bmp.h new file mode 100644 index 0000000000000000000000000000000000000000..1d9ded9cd18e612dff697385b11822a589c8431d --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_bmp.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024-2025, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_AIC_BMP_H +#define LV_AIC_BMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" +#include "lv_aic_stream.h" + +#if LV_USE_AIC_BMP + +lv_result_t lv_check_bmp_header(lv_stream_t *stream); + +lv_result_t lv_bmp_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file); + +lv_result_t lv_bmp_decoder_info_from_stream(lv_stream_t *stream, lv_image_header_t *header); + +lv_result_t lv_bmp_dec_open(lv_image_decoder_t *decoder, lv_image_decoder_dsc_t *dsc); + +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif //LV_AIC_BMP_H diff --git a/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.c b/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.c new file mode 100644 index 0000000000000000000000000000000000000000..8cb3aeb53e26f746cc29722e3fc0a543a57e4bac --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2025-2026, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include + +#include "aic_core.h" +#include "mpp_ge.h" +#include "lv_aic_stream.h" +#include "lv_mpp_dec.h" +#include "lv_aic_img_dsc.h" +#include "../ge2d/lv_draw_ge2d_utils.h" +#include "../ge2d/lv_draw_ge2d.h" + +#if LV_USE_DRAW_GE2D + +void lv_aic_dec_free(struct mpp_buf *mpp_buf) +{ + int i; + for (i = 0; i < 3; i++) { + if (mpp_buf->phy_addr[i]) { + mpp_dec_free_oom( (void*)(ulong)mpp_buf->phy_addr[i]); + mpp_buf->phy_addr[i] = 0; + } + } +} + +lv_result_t lv_aic_dec_alloc(struct mpp_buf *mpp_buf, int *size) +{ + int i; + for (i = 0; i < 3; i++) { + if (size[i]) { + unsigned int align_addr = (unsigned int)(ulong)mpp_dec_memalign_oom( size[i], CACHE_LINE_SIZE); + if (align_addr == 0) { + LV_LOG_ERROR("lv_aic_dec_alloc failed"); + goto alloc_error; + } + + mpp_buf->phy_addr[i] = align_addr; + aicos_dcache_clean_invalid_range((ulong *)(ulong)align_addr, size[i]); + } + } + + return LV_RESULT_OK; +alloc_error: + lv_aic_dec_free(mpp_buf); + return LV_RESULT_INVALID; +} + +static lv_result_t lv_mpp_dec_frame(struct mpp_buf *buf, const char *src, uint32_t size, bool is_file) +{ + lv_stream_t stream; + char *ptr; + struct mpp_packet packet; + lv_image_header_t header; + struct mpp_frame dec_frame = { 0 }; + struct mpp_frame frame = { 0 }; + lv_fs_res_t fs_res = LV_FS_RES_FS_ERR; + lv_result_t res = LV_RESULT_OK; + uint32_t file_len = 0; + enum mpp_codec_type type = MPP_CODEC_VIDEO_DECODER_PNG; + struct decode_config config = { 0 }; + int buf_size[3] = { 0 }; + struct mpp_decoder *dec = NULL; + struct frame_allocator *allocator = NULL; + bool has_frame = false; + uint32_t read_size = 0; + bool is_aicp = false; + + if (is_file) { + // get image info + ptr = strrchr(src, '.'); + CHECK_PTR(ptr); + + if (!strncmp(ptr, ".png", 4)) { + res = lv_png_decoder_info(src, &header, 0, true); + type = MPP_CODEC_VIDEO_DECODER_PNG; + } else if (image_suffix_is_jpg(ptr)) { + res = lv_jpeg_decoder_info(src, &header, 0, true, false); + type = MPP_CODEC_VIDEO_DECODER_MJPEG; +#ifdef AIC_MPP_AICP_DEC_ENABLE + } else if (image_suffix_is_aicp(ptr)) { + res = lv_jpeg_decoder_info(src, &header, 0, true, true); + type = MPP_CODEC_VIDEO_DECODER_AICP; +#endif + } else { + LV_LOG_ERROR("unsupported format:%s", src); + goto out; + } + } else { + bool is_aicp_data = false; +#ifdef AIC_MPP_AICP_DEC_ENABLE + // Check for AICP header + if (size >= 4 && memcmp(src, "AICP", 4) == 0) { + is_aicp_data = true; + } +#endif + res = lv_jpeg_decoder_info(src, &header, size, false, is_aicp_data); + if (is_aicp_data) { + type = MPP_CODEC_VIDEO_DECODER_AICP; + } else { + type = MPP_CODEC_VIDEO_DECODER_MJPEG; + } + + if (res != LV_RESULT_OK) { + res = lv_png_decoder_info(src, &header, size, false); + type = MPP_CODEC_VIDEO_DECODER_PNG; + } + } + CHECK_RET(res, LV_RESULT_OK); + + // open image file + if (is_file) + fs_res = lv_aic_stream_open_file(&stream, src); + else + fs_res = lv_aic_stream_open_buf(&stream, src, size); + + CHECK_RET(fs_res, LV_FS_RES_OK); + + lv_aic_stream_get_size(&stream, &file_len); + if (!file_len) { + LV_LOG_ERROR("No image data"); + goto out; + } + + dec = mpp_decoder_create(type); + CHECK_PTR(dec); + + config.bitstream_buffer_size = ALIGN_UP(file_len, 256); + config.extra_frame_num = 0; + config.packet_count = 1; + + config.pix_fmt = lv_fmt_to_mpp_fmt(header.cf); + dec_frame.buf.size.width = header.w; + dec_frame.buf.size.height = header.h; + dec_frame.buf.format = config.pix_fmt; + dec_frame.buf.buf_type = MPP_PHY_ADDR; + + int size_shift = 0; +#if defined(MPP_JPEG_DEC_OUT_SIZE_LIMIT_ENABLE) + if (type == MPP_CODEC_VIDEO_DECODER_MJPEG) { + size_shift = header.reserved_2; + } +#endif + + if (type == MPP_CODEC_VIDEO_DECODER_AICP) + is_aicp = true; + + lv_set_frame_buf_size(&dec_frame, buf_size, 0, is_aicp); + if (size_shift > 0) { + struct mpp_scale_ratio scale; + scale.hor_scale = size_shift; + scale.ver_scale = size_shift; + mpp_decoder_control(dec, MPP_DEC_INIT_CMD_SET_SCALE, &scale); + } + + CHECK_RET(lv_aic_dec_alloc(&dec_frame.buf, buf_size), LV_RESULT_OK); + + // allocator will be released in decoder + allocator = lv_open_allocator(&dec_frame); + CHECK_PTR(allocator); + + mpp_decoder_control(dec, MPP_DEC_INIT_CMD_SET_EXT_FRAME_ALLOCATOR, (void*)allocator); + CHECK_RET(mpp_decoder_init(dec, &config), 0); + memset(&packet, 0, sizeof(struct mpp_packet)); + mpp_decoder_get_packet(dec, &packet, file_len); + + lv_aic_stream_read(&stream, packet.data, file_len, &read_size); + packet.size = file_len; + packet.flag = PACKET_FLAG_EOS; + + mpp_decoder_put_packet(dec, &packet); + CHECK_RET(mpp_decoder_decode(dec), 0); + + mpp_decoder_get_frame(dec, &frame); + mpp_decoder_put_frame(dec, &frame); + + memcpy(buf, &frame.buf, sizeof(struct mpp_buf)); + has_frame = true; + +out: + if (fs_res == LV_FS_RES_OK) + lv_aic_stream_close(&stream); + + if (dec) + mpp_decoder_destory(dec); + + if (!has_frame) { + lv_aic_dec_free(&dec_frame.buf); + } + + if (has_frame) + return LV_RESULT_OK; + else + return LV_RESULT_INVALID; +} + +static inline bool mpp_fmt_is_yuv(enum mpp_pixel_format fmt) +{ + if (fmt >= MPP_FMT_YUV420P) + return true; + else + return false; +} + +static int mpp_buf_yuv_to_rgb(struct mpp_buf *buf) +{ + int ret; + enum mpp_pixel_format out_fmt = MPP_FMT_ARGB_8888; + struct ge_bitblt blt = { 0 }; + struct mpp_ge *ge2d_dev = get_ge2d_device(); + int out_stride = 0; + int out_width; + int out_height; + unsigned int out_addr; + int out_size; + + if (buf->crop_en) { + out_width = buf->crop.width; + out_height = buf->crop.height; + } else { + out_width = buf->size.width; + out_height = buf->size.height; + } + + if (out_width < 4 || out_height < 4) { + LV_LOG_ERROR("Unsupported width: %d height:%d", out_width, out_height); + return -1; + } + + /* src buf */ + memcpy(&blt.src_buf, buf, sizeof(struct mpp_buf)); + +#if LV_COLOR_DEPTH == 16 + out_fmt = MPP_FMT_RGB_565; + out_stride = ALIGN_UP(out_width * 2, 8); +#elif LV_COLOR_DEPTH == 24 + out_fmt = MPP_FMT_RGB_888; + out_stride = ALIGN_UP(out_width * 3, 8); +#else + out_fmt = MPP_FMT_ARGB_8888; + out_stride = ALIGN_UP(out_width * 4, 8); +#endif + + out_size = out_stride * out_height; + out_addr = (unsigned int)(ulong)mpp_dec_memalign_oom( out_size, CACHE_LINE_SIZE); + if (!out_addr) { + LV_LOG_ERROR("alloc out buf failed"); + return -1; + } + + aicos_dcache_clean_invalid_range((ulong *)(ulong)out_addr, out_size); + + /* dst buf */ + blt.dst_buf.buf_type = MPP_PHY_ADDR; + blt.dst_buf.phy_addr[0] = out_addr; + blt.dst_buf.stride[0] = out_stride; + blt.dst_buf.format = out_fmt; + blt.dst_buf.size.width = out_width; + blt.dst_buf.size.height = out_height; + + ret = mpp_ge_bitblt(ge2d_dev, &blt); + if (ret < 0) { + LV_LOG_ERROR("bitblt fail\n"); + goto failed; + } + ret = mpp_ge_emit(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("emit fail\n"); + goto failed; + } + ret = mpp_ge_sync(ge2d_dev); + if (ret < 0) { + LV_LOG_ERROR("sync fail\n"); + goto failed; + } + + /* free source buffer */ + lv_aic_dec_free(&blt.src_buf); + + /* get new buffer */ + memcpy(buf, &blt.dst_buf, sizeof(struct mpp_buf)); + + return 0; + +failed: + mpp_dec_free_oom( (void *)(ulong)out_addr); + return -1; +} + +static lv_image_dsc_t *lv_aic_img_dsc_create_base(const char *src, uint32_t size, bool is_file) +{ + lv_image_dsc_t *img_dsc; + struct mpp_buf buf = { 0 }; + lv_result_t res = LV_RESULT_OK; + + if (is_file) + res = lv_mpp_dec_frame(&buf, src, 0, true); + else + res = lv_mpp_dec_frame(&buf, src, size, false); + + if (res != LV_RESULT_OK) { + LV_LOG_ERROR("lv decoder failed"); + return NULL; + } + + img_dsc = lv_malloc(sizeof(lv_image_dsc_t)); + if(!img_dsc) { + LV_LOG_ERROR("malloc lv_image_dsc_t failed"); + goto out; + } + + memset(img_dsc, 0, sizeof(lv_image_dsc_t)); + if (mpp_fmt_is_yuv(buf.format)) { + if (mpp_buf_yuv_to_rgb(&buf) != 0) { + LV_LOG_ERROR("mpp_buf_yuv_to_rgb failed"); + goto out; + } + } + + if (buf.crop_en) { + img_dsc->header.w = buf.crop.width; + img_dsc->header.h = buf.crop.height; + } else { + img_dsc->header.w = buf.size.width; + img_dsc->header.h = buf.size.height; + } + + img_dsc->header.cf = mpp_fmt_to_lv_fmt(buf.format); + img_dsc->header.magic = LV_IMAGE_HEADER_MAGIC; + img_dsc->header.stride = buf.stride[0]; + img_dsc->data_size = img_dsc->header.stride * img_dsc->header.h; + img_dsc->data = (uint8_t *)(ulong)buf.phy_addr[0]; + + return img_dsc; +out: + if (img_dsc) + lv_free(img_dsc); + + lv_aic_dec_free(&buf); + + return NULL; +} + +lv_image_dsc_t *lv_aic_img_dsc_create(const char *file_path) +{ + return lv_aic_img_dsc_create_base(file_path, 0, true); +} + +lv_image_dsc_t *lv_aic_img_dsc_create_from_buf(const char *buf, uint32_t size) +{ + return lv_aic_img_dsc_create_base(buf, size, false); +} + +void lv_aic_img_dsc_destory(lv_image_dsc_t *img_dsc) +{ + if (img_dsc) { + if (img_dsc->data) { + uint8_t *data = (uint8_t *)img_dsc->data; + mpp_dec_free_oom( data); + } + lv_free(img_dsc); + } +} + +#endif diff --git a/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.h b/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.h new file mode 100644 index 0000000000000000000000000000000000000000..4bd265fba65c6ad5799de587d7ad82d652c52175 --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_img_dsc.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025-2026, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_AIC_IMG_DSC_H +#define LV_AIC_IMG_DSC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" + +lv_image_dsc_t *lv_aic_img_dsc_create(const char *file_path); + +lv_image_dsc_t *lv_aic_img_dsc_create_from_buf(const char *buf, uint32_t size); + +void lv_aic_img_dsc_destory(lv_image_dsc_t *img); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif //LV_USE_AIC_IMG_DSC diff --git a/src/draw/artinchip/mpp_dec/lv_aic_stream.c b/src/draw/artinchip/mpp_dec/lv_aic_stream.c new file mode 100644 index 0000000000000000000000000000000000000000..52111d0000bfb56203c81b832d7adee0f5ef3c37 --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_stream.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024-2025, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include +#include +#include + +#include "lv_aic_stream.h" + +lv_fs_res_t lv_aic_stream_open(lv_stream_t *stream, const void *src) +{ + lv_fs_res_t res = LV_FS_RES_OK; + + stream->src = ((lv_img_dsc_t *)src)->data; + stream->cur_index = 0; + if (lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) { + stream->is_file = 1; + res = lv_fs_open(&stream->f, src, LV_FS_MODE_RD); + } else { + stream->is_file = 0; + stream->data_size = ((lv_img_dsc_t *)src)->data_size; + } + + return res; +} + +lv_fs_res_t lv_aic_stream_open_file(lv_stream_t *stream, const char *path) +{ + lv_fs_res_t res = LV_FS_RES_OK; + + stream->src = NULL; + stream->cur_index = 0; + stream->is_file = 1; + res = lv_fs_open(&stream->f, path, LV_FS_MODE_RD); + + return res; +} + +lv_fs_res_t lv_aic_stream_open_buf(lv_stream_t *stream, const char *buf, uint32_t size) +{ + lv_fs_res_t res = LV_FS_RES_OK; + + stream->src = (uint8_t *)buf; + stream->cur_index = 0; + stream->is_file = 0; + stream->data_size = (int)size; + + return res; +} + +lv_fs_res_t lv_aic_stream_close(lv_stream_t *stream) +{ + if (stream->is_file) + lv_fs_close(&stream->f); + + return LV_FS_RES_OK; +} + +lv_fs_res_t lv_aic_stream_seek(lv_stream_t *stream, uint32_t pos, lv_fs_whence_t whence) +{ + lv_fs_res_t res = LV_FS_RES_OK; + + if (stream->is_file) { + res = lv_fs_seek(&stream->f, pos, whence); + } else { + // only support SEEK_CUR + stream->cur_index += pos; + } + + return res; +} + +lv_fs_res_t lv_aic_stream_read(lv_stream_t *stream, void *buf, uint32_t btr, uint32_t *br) +{ + lv_fs_res_t res = LV_FS_RES_OK; + + if (stream->is_file) { + res = lv_fs_read(&stream->f, buf, btr, br); + } else { + if (stream->cur_index + btr <= stream->data_size) { + memcpy(buf, &stream->src[stream->cur_index], btr); + stream->cur_index += btr; + *br = btr; + } else { + LV_LOG_WARN("data_size:%d, cur_index:%d", stream->data_size, stream->cur_index); + } + } + + return res; +} + +void lv_aic_stream_reset(lv_stream_t *stream) +{ + stream->cur_index = 0; + return; +} + +lv_result_t lv_aic_stream_get_size(lv_stream_t *stream, uint32_t *file_size) +{ + lv_result_t res = LV_RESULT_OK; + + if (stream->is_file) { + lv_fs_seek(&stream->f, 0, SEEK_END); + lv_fs_tell(&stream->f, file_size); + lv_fs_seek(&stream->f, 0, SEEK_SET); + } else { + *file_size = stream->data_size; + } + + return res; +} diff --git a/src/draw/artinchip/mpp_dec/lv_aic_stream.h b/src/draw/artinchip/mpp_dec/lv_aic_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..3326187fe4b402e6aab1fbc2acb42dadc7e9c40d --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_aic_stream.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024-2025, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_AIC_STREAM_H +#define LV_AIC_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" + +typedef struct +{ + lv_fs_file_t f; + int cur_index; + const uint8_t *src; + int is_file; + int data_size; +} lv_stream_t; + +static inline uint64_t stream_to_u64(uint8_t *ptr) +{ + return ((uint64_t)ptr[0] << 56) | ((uint64_t)ptr[1] << 48) | + ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[3] << 32) | + ((uint64_t)ptr[4] << 24) | ((uint64_t)ptr[5] << 16) | + ((uint64_t)ptr[6] << 8) | ((uint64_t)ptr[7]); +} + +static inline unsigned int stream_to_u32(uint8_t *ptr) +{ + return ((unsigned int)ptr[0] << 24) | + ((unsigned int)ptr[1] << 16) | + ((unsigned int)ptr[2] << 8) | + (unsigned int)ptr[3]; +} + +static inline unsigned short stream_to_u16(uint8_t *ptr) +{ + return ((unsigned int)ptr[0] << 8) | (unsigned int)ptr[1]; +} + +lv_fs_res_t lv_aic_stream_open(lv_stream_t *stream, const void *src); + +lv_fs_res_t lv_aic_stream_open_file(lv_stream_t *stream, const char *path); + +lv_fs_res_t lv_aic_stream_open_buf(lv_stream_t *stream, const char *buf, uint32_t size); + +lv_fs_res_t lv_aic_stream_close(lv_stream_t *stream); + +lv_fs_res_t lv_aic_stream_seek(lv_stream_t *stream, uint32_t pos, lv_fs_whence_t whence); + +lv_fs_res_t lv_aic_stream_read(lv_stream_t *stream, void *buf, uint32_t btr, uint32_t *br); + +void lv_aic_stream_reset(lv_stream_t *stream); + +lv_result_t lv_aic_stream_get_size(lv_stream_t *stream, uint32_t *file_size); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif //LV_AIC_STREAM_H diff --git a/src/draw/artinchip/mpp_dec/lv_mpp_dec.c b/src/draw/artinchip/mpp_dec/lv_mpp_dec.c new file mode 100644 index 0000000000000000000000000000000000000000..7863257cd1e90df4b5954100ff1fa2d14ac7d85f --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_mpp_dec.c @@ -0,0 +1,975 @@ +/* + * Copyright (C) 2024-2026, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#include +#include +#include +#include + +#include "lvgl.h" +#include "aic_core.h" +#include "mpp_mem.h" +#include "mpp_ge.h" +#include "lv_mpp_dec.h" +#include "lv_aic_stream.h" +#include "lv_aic_bmp.h" +#include "../ge2d/lv_draw_ge2d_utils.h" + +#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache) + +#define PNG_HEADER_SIZE (8 + 12 + 13) //png signature + IHDR chuck +#define PNGSIG 0x89504e470d0a1a0aull +#define MNGSIG 0x8a4d4e470d0a1a0aull +#define JPEG_SOI 0xFFD8 +#define JPEG_SOF 0xFFC0 +#define AICP_SOF 0xFFC1 +#define ALIGN_16B(x) (((x) + (15)) & ~(15)) + +/* [orig][flag][returned ptr...] + * flag: NULL = system heap (kmm_free), non-NULL = LVGL heap (buf_free_cb) + */ +#define AIOCS_OOM_SLOTS 2 + +static inline bool aicos_oom_is_lvgl(void) { + return lv_is_initialized() && + LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers.buf_malloc_cb != NULL && + img_cache_p != NULL; +} + +void *mpp_dec_malloc_oom(size_t size) +{ + size_t alloc_size = size + AIOCS_OOM_SLOTS * sizeof(void *); + void *orig; + void *lvgl_flag = NULL; + + if(aicos_oom_is_lvgl()) { + lv_draw_buf_handlers_t *h = &LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers; + while(1) { + orig = h->buf_malloc_cb(alloc_size, LV_COLOR_FORMAT_UNKNOWN); + if(orig) { lvgl_flag = (void *)1; break; } + if(!lv_cache_evict_one(img_cache_p, NULL)) return NULL; + } + } else { + orig = kmm_malloc(alloc_size); + if(!orig) return NULL; + } + + void *ptr = (void *)((uint8_t *)orig + AIOCS_OOM_SLOTS * sizeof(void *)); + ((void **)ptr)[-1] = orig; + ((void **)ptr)[-2] = lvgl_flag; + return ptr; +} + +void *mpp_dec_memalign_oom(size_t size, size_t align) +{ + size_t alloc_size = size + align - 1 + AIOCS_OOM_SLOTS * sizeof(void *); + void *orig; + void *lvgl_flag = NULL; + + if(aicos_oom_is_lvgl()) { + lv_draw_buf_handlers_t *h = &LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers; + while(1) { + orig = h->buf_malloc_cb(alloc_size, LV_COLOR_FORMAT_UNKNOWN); + if(orig) { lvgl_flag = (void *)1; break; } + if(!lv_cache_evict_one(img_cache_p, NULL)) return NULL; + } + } else { + orig = kmm_malloc(alloc_size); + if(!orig) return NULL; + } + + void *aligned = (void *)ALIGN_UP((ulong)orig + AIOCS_OOM_SLOTS * sizeof(void *), align); + ((void **)aligned)[-1] = orig; + ((void **)aligned)[-2] = lvgl_flag; + return aligned; +} + +void mpp_dec_free_oom(void *ptr) +{ + if(!ptr) return; + + void *orig = ((void **)ptr)[-1]; + + if(((void **)ptr)[-2]) { + lv_draw_buf_handlers_t *h = &LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers; + h->buf_free_cb(orig); + } else { + kmm_free(orig); + } +} + +lv_color_format_t mpp_fmt_to_lv_fmt(enum mpp_pixel_format cf) +{ + lv_color_format_t fmt = LV_COLOR_FORMAT_ARGB8888; + + switch(cf) { + case MPP_FMT_RGB_565: + fmt = LV_COLOR_FORMAT_RGB565; + break; + case MPP_FMT_RGB_888: + fmt = LV_COLOR_FORMAT_RGB888; + break; + case MPP_FMT_ARGB_8888: + fmt = LV_COLOR_FORMAT_ARGB8888; + break; + case MPP_FMT_XRGB_8888: + fmt = LV_COLOR_FORMAT_XRGB8888; + break; + case MPP_FMT_YUV420P: + fmt = LV_COLOR_FORMAT_I420; + break; + case MPP_FMT_YUV422P: + fmt = LV_COLOR_FORMAT_I422; + break; + case MPP_FMT_YUV444P: + fmt = LV_COLOR_FORMAT_I444; + break; + case MPP_FMT_YUV400: + fmt = LV_COLOR_FORMAT_I400; + break; + default: + LV_LOG_ERROR("unsupported format:%d", cf); + break; + } + + return fmt; +} + +static int mpp_get_rgb_stride(int width, enum mpp_pixel_format fmt) +{ + int stride; + + switch(fmt) { + case MPP_FMT_RGB_565: + stride = ALIGN_16B(width) * 2; + break; + case MPP_FMT_RGB_888: + stride = ALIGN_16B(width) * 3; + break; + case MPP_FMT_ARGB_8888: + stride = ALIGN_16B(width) * 4; + break; + case MPP_FMT_XRGB_8888: + stride = ALIGN_16B(width) * 4; + break; + default: + stride = ALIGN_16B(width) * 4; + LV_LOG_ERROR("unsupported format:%d", fmt); + break; + } + return stride; +} + +static int get_jpeg_format(uint8_t *buf, enum mpp_pixel_format *pix_fmt) +{ + int i; + uint32_t h_count_flag; + uint32_t v_count_flag; + uint8_t h_count[3] = { 0 }; + uint8_t v_count[3] = { 0 }; + uint8_t nb_components = *buf++; + + for (i = 0; i < nb_components; i++) { + uint8_t h_v_cnt; + + // skip component id + buf++; + h_v_cnt = *buf++; + h_count[i] = h_v_cnt >> 4; + v_count[i] = h_v_cnt & 0xf; + + // skip quant_index + buf++; + } + + h_count_flag = h_count[2] | (h_count[1] << 4) | (h_count[0] << 8); + v_count_flag = v_count[2] | (v_count[1] << 4) | (v_count[0] << 8); + + if (h_count_flag == 0x211 && v_count_flag == 0x211) { + *pix_fmt = MPP_FMT_YUV420P; + } else if (h_count_flag == 0x211 && v_count_flag == 0x111) { + *pix_fmt = MPP_FMT_YUV422P; + } else if (h_count_flag == 0x111 && v_count_flag == 0x111) { + *pix_fmt = MPP_FMT_YUV444P; + } else if (h_count_flag == 0x111 && v_count_flag == 0x222) { + *pix_fmt = MPP_FMT_YUV444P; + } else if (h_count[1] == 0 && v_count[1] == 0 && h_count[2] == 0 && + v_count[2] == 0) { + *pix_fmt = MPP_FMT_YUV400; + } else { + LV_LOG_ERROR("Not support format! h_count: %d %d %d, v_count: %d %d %d", + h_count[0], h_count[1], h_count[2], + v_count[0], v_count[1], v_count[2]); + return -1; + } + +#ifndef AIC_VE_DRV_V10 +#if LV_COLOR_DEPTH == 16 + *pix_fmt = MPP_FMT_RGB_565; +#else + *pix_fmt = MPP_FMT_RGB_888; +#endif +#endif + +#ifdef AIC_VE_DRV_V31 + if (nb_components == 4) + *pix_fmt = MPP_FMT_ARGB_8888; +#else + if (nb_components == 4) { + LV_LOG_ERROR("Unsupported nb_components: %d", nb_components); + return -1; + } +#endif + return 0; +} + +static inline lv_result_t check_jpeg_soi(lv_stream_t *stream) +{ + uint32_t read_num; + uint8_t buf[128]; + lv_fs_res_t fs_res; + lv_result_t res = LV_RESULT_OK; + + // read JPEG SOI + fs_res = lv_aic_stream_read(stream, buf, 2, &read_num); + if (fs_res != LV_FS_RES_OK || read_num != 2) { + res = LV_RESULT_INVALID; + goto read_err; + } + + // check SOI + if (buf[0] != 0xff || buf[1] != 0xd8) { + res = LV_RESULT_INVALID; + goto read_err; + } + + // check SOI + if (stream_to_u16(buf) != JPEG_SOI) { + res = LV_RESULT_INVALID; + goto read_err; + } + +read_err: + return res; +} + +static lv_result_t jpeg_get_img_size(lv_stream_t *stream, int *w, int *h, enum mpp_pixel_format *pix_fmt, bool is_aicp) +{ + uint32_t read_num; + uint8_t buf[128]; + lv_fs_res_t fs_res; + lv_result_t res = LV_RESULT_OK; + + if (check_jpeg_soi(stream) != LV_RESULT_OK) { + res = LV_RESULT_INVALID; + LV_LOG_ERROR("check jpeg soi failed"); + goto read_err; + } + + // find SOF + while (1) { + int size; + uint16_t marker; + fs_res = lv_aic_stream_read(stream, buf, 4, &read_num); + if (fs_res != LV_FS_RES_OK || read_num != 4) { + res = LV_RESULT_INVALID; + LV_LOG_ERROR("get chunk header failed"); + goto read_err; + } + + marker = stream_to_u16(buf); + if (marker == JPEG_SOF || (is_aicp && marker == AICP_SOF)) { + fs_res = lv_aic_stream_read(stream, buf, 15, &read_num); + if (fs_res != LV_FS_RES_OK) { + res = LV_RESULT_INVALID; + LV_LOG_ERROR("read chunk data failed"); + goto read_err; + } + + *h = stream_to_u16(buf + 1); + *w = stream_to_u16(buf + 3); + + if (get_jpeg_format(buf + 5, pix_fmt) < 0) { + res = LV_RESULT_INVALID; + LV_LOG_ERROR("get format failed"); + goto read_err; + } + break; + } else { + size = stream_to_u16(buf + 2); + fs_res = lv_aic_stream_seek(stream, size - 2, SEEK_CUR); + if (fs_res != LV_FS_RES_OK) { + res = LV_RESULT_INVALID; + LV_LOG_ERROR("read chunk data failed"); + goto read_err; + } + } + } +read_err: + return res; +} + +static lv_result_t jpeg_decoder_info_from_stream(lv_stream_t *stream, lv_image_header_t *header, bool is_aicp) +{ + int width; + int height; + enum mpp_pixel_format format = MPP_FMT_ARGB_8888; + +#ifdef AIC_MPP_AICP_DEC_ENABLE + if(is_aicp) { + uint8_t aicp_header[4]; + uint32_t read_num; + lv_fs_res_t res = lv_aic_stream_read(stream, aicp_header, 4, &read_num); + if(res != LV_FS_RES_OK || read_num != 4 || memcmp(aicp_header, "AICP", 4) != 0) + return LV_RESULT_INVALID; + } +#endif + + if(jpeg_get_img_size(stream, &width, &height, &format, is_aicp) != LV_RESULT_OK) + return LV_RESULT_INVALID; + +#if defined(MPP_JPEG_DEC_OUT_SIZE_LIMIT_ENABLE) + if(!is_aicp) { + int size_shift = jpeg_size_limit(width, height); + width = width >> size_shift; + height = height >> size_shift; + header->reserved_2 = size_shift; + } +#endif + + header->w = width; + header->h = height; + header->cf = mpp_fmt_to_lv_fmt(format); + + if(!lv_fmt_is_yuv(header->cf)) + header->stride = mpp_get_rgb_stride(width, format); + + return LV_RESULT_OK; +} + +lv_result_t lv_jpeg_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file, bool is_aicp) +{ + lv_fs_res_t res; + lv_stream_t stream; + + if(is_file) + res = lv_aic_stream_open_file(&stream, src); + else + res = lv_aic_stream_open_buf(&stream, src, size); + + if(res != LV_FS_RES_OK) return LV_RESULT_INVALID; + + res = jpeg_decoder_info_from_stream(&stream, header, is_aicp); + lv_aic_stream_close(&stream); + return res; +} + +static inline lv_result_t check_png_sig(lv_stream_t *stream) +{ + uint32_t read_num; + unsigned char buf[64]; + uint64_t sig; + + lv_aic_stream_read(stream, buf, 8, &read_num); + sig = stream_to_u64(buf); + if (sig != PNGSIG && sig != MNGSIG) { + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +static lv_result_t png_decoder_info_from_stream(lv_stream_t *stream, lv_image_header_t *header) +{ + uint32_t read_num; + uint8_t buf[64]; + int color_type; + lv_fs_res_t res; + + res = lv_aic_stream_read(stream, buf, PNG_HEADER_SIZE, &read_num); + if(res != LV_FS_RES_OK || read_num != PNG_HEADER_SIZE) + return LV_RESULT_INVALID; + + uint64_t sig = stream_to_u64(buf); + if(sig != PNGSIG && sig != MNGSIG) + return LV_RESULT_INVALID; + + header->w = stream_to_u32(buf + 8 + 8); + header->h = stream_to_u32(buf + 8 + 8 + 4); + + color_type = buf[8 + 8 + 8 + 1]; + if(color_type == 2) { + header->cf = mpp_fmt_to_lv_fmt(MPP_FMT_RGB_888); + header->stride = mpp_get_rgb_stride(header->w, MPP_FMT_RGB_888); + } else { + header->cf = mpp_fmt_to_lv_fmt(MPP_FMT_ARGB_8888); + header->stride = mpp_get_rgb_stride(header->w, MPP_FMT_ARGB_8888); + } + + return LV_RESULT_OK; +} + +lv_result_t lv_png_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file) +{ + lv_fs_res_t res; + lv_stream_t stream; + + if(is_file) + res = lv_aic_stream_open_file(&stream, src); + else + res = lv_aic_stream_open_buf(&stream, src, size); + + if(res != LV_FS_RES_OK) return LV_RESULT_INVALID; + + res = png_decoder_info_from_stream(&stream, header); + lv_aic_stream_close(&stream); + return res; +} + +static lv_res_t fake_decoder_info(const void *src, lv_image_header_t *header) +{ + int width; + int height; + int alpha_en; + unsigned int color; + + { + char *p = (char *)src + 3; + char *n; + width = (int)strtol(p, &n, 10); + height = (int)strtol(n + 1, &n, 10); + alpha_en = (int)strtol(n + 1, &n, 10); + color = (unsigned int)strtoul(n + 1, NULL, 16); + } + header->w = width; + header->h = height; + LV_UNUSED(alpha_en); + LV_UNUSED(color); + header->stride = mpp_get_rgb_stride(header->w, MPP_FMT_ARGB_8888); + header->cf = LV_COLOR_FORMAT_RAW; + return LV_RES_OK; +} + +static lv_result_t lv_mpp_dec_info(lv_image_decoder_t *decoder, lv_image_decoder_dsc_t *dsc, lv_image_header_t *header) +{ + const void *src = dsc->src; + char* ptr = NULL; + lv_result_t res = LV_RESULT_INVALID; + + LV_UNUSED(decoder); + + if (lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) { + ptr = strrchr(src, '.'); + if (!ptr) return LV_RESULT_INVALID; + + /* dsc->file is already opened by image_decoder_get_info(), + * wrap it in a stream directly. */ + lv_stream_t stream; + lv_memcpy(&stream.f, &dsc->file, sizeof(lv_fs_file_t)); + stream.is_file = 1; + stream.cur_index = 0; + + if (!strncmp(ptr, ".png", 4)) { + res = png_decoder_info_from_stream(&stream, header); + } else if (image_suffix_is_jpg(ptr)) { + res = jpeg_decoder_info_from_stream(&stream, header, false); +#ifdef AIC_MPP_AICP_DEC_ENABLE + } else if (image_suffix_is_aicp(ptr)) { + res = jpeg_decoder_info_from_stream(&stream, header, true); +#endif + } else if (!strncmp(ptr, ".fake", 5)) { + res = fake_decoder_info(src, header); + } +#if LV_USE_AIC_BMP + else if (!strncmp(ptr, ".bmp", 4) || !strncmp(ptr, ".BMP", 4)) { + res = lv_bmp_decoder_info_from_stream(&stream, header); + } +#endif + + return res; + } else if (lv_image_src_get_type(src) == LV_IMAGE_SRC_VARIABLE) { + lv_color_format_t cf = ((lv_img_dsc_t *)src)->header.cf; + char *data = (char *)((lv_img_dsc_t *)src)->data; + uint32_t data_size = ((lv_img_dsc_t *)src)->data_size; + + if (cf == LV_COLOR_FORMAT_RAW || cf == LV_COLOR_FORMAT_RAW) { + bool is_aicp_data = false; +#ifdef AIC_MPP_AICP_DEC_ENABLE + // Check for AICP header + if (data_size >= 4 && memcmp(data, "AICP", 4) == 0) { + is_aicp_data = true; + } +#endif + res = lv_jpeg_decoder_info(data, header, data_size, false, is_aicp_data); + + if (res != LV_RESULT_OK) { + res = lv_png_decoder_info(data, header, data_size, false); +#if LV_USE_AIC_BMP + if (res != LV_RESULT_OK) { + res = lv_bmp_decoder_info(data, header, data_size, false); + } +#endif + } + } + } + + return res; +} + +struct ext_frame_allocator { + struct frame_allocator base; + struct mpp_frame* frame; +}; + +static int alloc_frame_buffer(struct frame_allocator *p, struct mpp_frame* frame, + int width, int height, enum mpp_pixel_format format) +{ + struct ext_frame_allocator* impl = (struct ext_frame_allocator*)p; + + memcpy(frame, impl->frame, sizeof(struct mpp_frame)); + return 0; +} + +static int free_frame_buffer(struct frame_allocator *p, struct mpp_frame *frame) +{ + return 0; +} + +static int close_allocator(struct frame_allocator *p) +{ + struct ext_frame_allocator* impl = (struct ext_frame_allocator*)p; + + free(impl); + + return 0; +} + +static struct alloc_ops def_ops = { + .alloc_frame_buffer = alloc_frame_buffer, + .free_frame_buffer = free_frame_buffer, + .close_allocator = close_allocator, +}; + +struct frame_allocator* lv_open_allocator(struct mpp_frame* frame) +{ + struct ext_frame_allocator* impl = (struct ext_frame_allocator*)malloc(sizeof(struct ext_frame_allocator)); + + if(impl == NULL) { + return NULL; + } + + memset(impl, 0, sizeof(struct ext_frame_allocator)); + + impl->frame = frame; + impl->base.ops = &def_ops; + return &impl->base; +} + +void lv_frame_buf_free(mpp_decoder_data_t *mpp_data) +{ + int i; + for (i = 0; i < 3; i++) { + if (mpp_data->data[i]) { + mpp_dec_free_oom((void*)mpp_data->data[i]); + mpp_data->data[i] = NULL; + } + } +} + +lv_result_t lv_frame_buf_alloc(mpp_decoder_data_t *mpp_data, struct mpp_buf *alloc_buf, + int *size, uint32_t cf) +{ + int i; + int data_size = 0; + for (i = 0; i < 3; i++) { + if (size[i]) { + mpp_data->data[i] = (uint8_t *)mpp_dec_malloc_oom(size[i] + CACHE_LINE_SIZE - 1); + if (!mpp_data->data[i]) { + goto alloc_error; + } else { + unsigned int align_addr = (unsigned int)ALIGN_UP((ulong)mpp_data->data[i], CACHE_LINE_SIZE); + alloc_buf->phy_addr[i] = align_addr; + aicos_dcache_clean_invalid_range((ulong *)(ulong)align_addr, ALIGN_UP(size[i], CACHE_LINE_SIZE)); + data_size += size[i]; + } + } + } + + mpp_data->decoded.header.w = alloc_buf->size.width; + mpp_data->decoded.header.h = alloc_buf->size.height; + mpp_data->decoded.header.cf = cf; + mpp_data->decoded.header.magic = LV_IMAGE_HEADER_MAGIC; + + if (lv_fmt_is_yuv(cf)) { + mpp_data->decoded.data =(uint8_t *)(&mpp_data->dec_buf); + mpp_data->decoded.data_size = data_size; + mpp_data->decoded.unaligned_data = (uint8_t *)(&mpp_data->dec_buf); + } else { + mpp_data->decoded.header.stride = alloc_buf->stride[0]; + mpp_data->decoded.data =(uint8_t *)((ulong)alloc_buf->phy_addr[0]); + mpp_data->decoded.data_size = size[0]; + mpp_data->decoded.unaligned_data = mpp_data->data[0]; + } + + /* + * FIXME: MPP alloc/free uses mpp_dec_malloc_oom/mpp_dec_free_oom, which + * adds AIOCS_OOM_SLOTS overhead. The pointers stored in unaligned_data + * and data do NOT match what default_handlers->buf_free_cb expects. + * + * Currently handlers->buf_free_cb is never called on MPP buffers because: + * - flags does not include LV_IMAGE_FLAGS_ALLOCATED → lv_draw_buf_destroy skips free + * - flags does not include LV_IMAGE_FLAGS_MODIFIABLE → flush_cache is skipped + * - MPP close path uses mpp_dec_data_release → lv_frame_buf_free → mpp_dec_free_oom + * + * handlers is set here only to pass the LV_ASSERT in lv_image_decoder.c:173. + * A proper long-term fix would be to register MPP-specific handlers with + * no-op or MPP-aware free/invalidate/flush callbacks. + */ + mpp_data->decoded.handlers = lv_draw_buf_get_handlers(); + + return LV_RESULT_OK; +alloc_error: + lv_frame_buf_free(mpp_data); + return LV_RESULT_INVALID; +} + +void lv_set_frame_buf_size(struct mpp_frame *frame, int *buf_size, int size_shift, bool is_aicp) +{ + int height_align; + int width = frame->buf.size.width; + int height = frame->buf.size.height; + + if (size_shift > 0) { + height_align = ALIGN_16B(ALIGN_16B(height) >> size_shift); + frame->buf.size.width = width >> size_shift;; + frame->buf.size.height = height >> size_shift; + } else { + height_align = ALIGN_16B(height); + } + + switch (frame->buf.format) { + case MPP_FMT_YUV420P: + if (size_shift > 0) + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift); + else + frame->buf.stride[0] = ALIGN_16B(width); + + frame->buf.stride[1] = frame->buf.stride[0] >> 1; + frame->buf.stride[2] = frame->buf.stride[0] >> 1; + buf_size[0] = frame->buf.stride[0] * height_align; + buf_size[1] = frame->buf.stride[1] * (height_align >> 1); + buf_size[2] = frame->buf.stride[2] * (height_align >> 1); + break; + case MPP_FMT_YUV422P: + if (size_shift > 0) + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift); + else + frame->buf.stride[0] = ALIGN_16B(width); + + frame->buf.stride[1] = frame->buf.stride[0] >> 1; + frame->buf.stride[2] = frame->buf.stride[0] >> 1; + buf_size[0] = frame->buf.stride[0] * height_align; + buf_size[1] = frame->buf.stride[1] * height_align; + buf_size[2] = frame->buf.stride[2] * height_align; + break; + case MPP_FMT_YUV444P: + if (size_shift > 0) + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift); + else + frame->buf.stride[0] = ALIGN_16B(width); + + frame->buf.stride[1] = frame->buf.stride[0]; + frame->buf.stride[2] = frame->buf.stride[0]; + buf_size[0] = frame->buf.stride[0] * height_align; + buf_size[1] = frame->buf.stride[1] * height_align; + buf_size[2] = frame->buf.stride[2] * height_align; + break; + case MPP_FMT_RGB_565: + if (size_shift > 0) + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift) * 2; + else + frame->buf.stride[0] = ALIGN_16B(width) * 2; + + buf_size[0] = frame->buf.stride[0] * height_align; + break; + case MPP_FMT_RGB_888: + if (size_shift > 0) + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift) * 3; + else + frame->buf.stride[0] = ALIGN_16B(width) * 3; + + buf_size[0] = frame->buf.stride[0] * height_align; + break; + case MPP_FMT_ARGB_8888: + if (size_shift > 0) { + frame->buf.stride[0] = ALIGN_16B(ALIGN_16B(width) >> size_shift) * 4; + buf_size[0] = frame->buf.stride[0] * height_align; + } else { + if (is_aicp) { + // Use 8-byte alignment for AICP format as requested + frame->buf.stride[0] = ALIGN_UP(width, 8) * 4; + buf_size[0] = frame->buf.stride[0] * ALIGN_UP(height, 8); + } else { + frame->buf.stride[0] = ALIGN_16B(width * 4); + buf_size[0] = ALIGN_UP(frame->buf.stride[0] * height, CACHE_LINE_SIZE); + } + } + break; + default: + LV_LOG_ERROR("unsupported format:%d", frame->buf.format); + break; + } +} + +static enum mpp_codec_type detect_codec_type(lv_stream_t *stream, lv_image_decoder_dsc_t *dsc) +{ + enum mpp_codec_type type = MPP_CODEC_VIDEO_DECODER_PNG; + char* ptr = NULL; + + if (lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_FILE) { + ptr = strrchr(dsc->src, '.'); + if (ptr && image_suffix_is_jpg(ptr)) { + type = MPP_CODEC_VIDEO_DECODER_MJPEG; +#ifdef AIC_MPP_AICP_DEC_ENABLE + } else if (ptr && image_suffix_is_aicp(ptr)) { + type = MPP_CODEC_VIDEO_DECODER_AICP; +#endif + } + } else if (lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE) { +#ifdef AIC_MPP_AICP_DEC_ENABLE + uint8_t aicp_header[4]; + uint32_t read_num; + lv_result_t res; + + // Check for AICP header first + res = lv_aic_stream_read(stream, aicp_header, 4, &read_num); + if (res == LV_FS_RES_OK && read_num == 4 && memcmp(aicp_header, "AICP", 4) == 0) { + type = MPP_CODEC_VIDEO_DECODER_AICP; + } else { + lv_aic_stream_reset(stream); +#endif + if (check_jpeg_soi(stream) == LV_RESULT_OK) { + type = MPP_CODEC_VIDEO_DECODER_MJPEG; + } else { + lv_aic_stream_reset(stream); + } +#ifdef AIC_MPP_AICP_DEC_ENABLE + } +#endif + lv_aic_stream_reset(stream); + } + + return type; +} + +static lv_result_t lv_mpp_dec_open(lv_image_decoder_t *decoder, lv_image_decoder_dsc_t *dsc) +{ + lv_result_t res = LV_RESULT_OK; + uint32_t file_len = 0; + lv_stream_t stream; + struct mpp_packet packet; + int width = 0; + int height = 0; + enum mpp_codec_type type; + struct decode_config config = { 0 }; + int buf_size[3] = { 0 }; + struct mpp_decoder *dec = NULL; + struct frame_allocator *allocator = NULL; + int size_shift = 0; + bool is_aicp = false; + +#if LV_USE_AIC_BMP + if (lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_FILE) { + const char *ext = lv_fs_get_ext(dsc->src); + if (lv_strcmp(ext, "bmp") == 0 || lv_strcmp(ext, "BMP") == 0) { + return lv_bmp_dec_open(decoder, dsc); + } + } else if (lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE) { + if (lv_aic_stream_open(&stream, dsc->src) != LV_FS_RES_OK) + return LV_RESULT_INVALID; + + if (lv_check_bmp_header(&stream) == LV_RESULT_OK) { + lv_aic_stream_close(&stream); + return lv_bmp_dec_open(decoder, dsc); + } + + lv_aic_stream_close(&stream); + } +#endif + + if (lv_aic_stream_open(&stream, dsc->src) != LV_FS_RES_OK) + return LV_RESULT_INVALID; + + mpp_decoder_data_t *mpp_data = (mpp_decoder_data_t *)lv_malloc_zeroed(sizeof(mpp_decoder_data_t)); + CHECK_PTR(mpp_data); + + width = dsc->header.w; + height = dsc->header.h; + config.pix_fmt = lv_fmt_to_mpp_fmt(dsc->header.cf); + + // Detect codec type + type = detect_codec_type(&stream, dsc); + + lv_aic_stream_get_size(&stream, &file_len); + dec = mpp_decoder_create(type); + CHECK_PTR(dec); + + config.bitstream_buffer_size = ALIGN_UP(file_len, 256); + config.extra_frame_num = 0; + config.packet_count = 1; + + struct mpp_frame dec_frame; + memset(&dec_frame, 0, sizeof(struct mpp_frame)); + dec_frame.buf.size.width = width; + dec_frame.buf.size.height = height; + dec_frame.buf.format = config.pix_fmt; + dec_frame.buf.buf_type = MPP_PHY_ADDR; + +#if defined(MPP_JPEG_DEC_OUT_SIZE_LIMIT_ENABLE) + if (type == MPP_CODEC_VIDEO_DECODER_MJPEG) + size_shift = dsc->header.reserved_2; +#endif + + if (type == MPP_CODEC_VIDEO_DECODER_AICP) + is_aicp = true; + + lv_set_frame_buf_size(&dec_frame, buf_size, 0, is_aicp); + + if (size_shift > 0) { + struct mpp_scale_ratio scale; + scale.hor_scale = size_shift; + scale.ver_scale = size_shift; + mpp_decoder_control(dec, MPP_DEC_INIT_CMD_SET_SCALE, &scale); + } + + CHECK_RET(lv_frame_buf_alloc(mpp_data, &dec_frame.buf, buf_size, dsc->header.cf), LV_RESULT_OK); + + // allocator will be released in decoder + allocator = lv_open_allocator(&dec_frame); + CHECK_PTR(allocator); + + mpp_decoder_control(dec, MPP_DEC_INIT_CMD_SET_EXT_FRAME_ALLOCATOR, (void*)allocator); + CHECK_RET(mpp_decoder_init(dec, &config), 0); + memset(&packet, 0, sizeof(struct mpp_packet)); + mpp_decoder_get_packet(dec, &packet, file_len); + + uint32_t read_size = 0; + + lv_aic_stream_read(&stream, packet.data, file_len, &read_size); + packet.size = file_len; + packet.flag = PACKET_FLAG_EOS; + + mpp_decoder_put_packet(dec, &packet); + CHECK_RET(mpp_decoder_decode(dec), 0); + + struct mpp_frame frame; + memset(&frame, 0, sizeof(struct mpp_frame)); + mpp_decoder_get_frame(dec, &frame); + + memcpy(&mpp_data->dec_buf, &frame.buf, sizeof(struct mpp_buf)); + mpp_decoder_put_frame(dec, &frame); + dsc->decoded = &mpp_data->decoded; + + if (!dsc->args.no_cache && lv_image_cache_is_enabled()) { + lv_image_cache_data_t search_key; + search_key.src_type = dsc->src_type; + search_key.src = dsc->src; + search_key.slot.size = dsc->decoded->data_size; + mpp_data->cached = true; + lv_cache_entry_t *cache_entry = lv_image_decoder_add_to_cache(decoder, &search_key, dsc->decoded, mpp_data); + CHECK_PTR(cache_entry); + dsc->cache_entry = cache_entry; + } + +out: + if (dec) + mpp_decoder_destory(dec); + + if (res == LV_RESULT_INVALID) { + dsc->decoded = NULL; + if (mpp_data) { + lv_frame_buf_free(mpp_data); + lv_free(mpp_data); + } + } + + lv_aic_stream_close(&stream); + return res; +} + +static void mpp_dec_data_release(mpp_decoder_data_t *decoder_data) +{ + if (decoder_data) { + lv_frame_buf_free(decoder_data); + lv_free(decoder_data); + } +} + +static void lv_mpp_dec_close(lv_image_decoder_t *decoder, lv_image_decoder_dsc_t *dsc) +{ + mpp_decoder_data_t *mpp_data = (mpp_decoder_data_t *)dsc->decoded; + LV_UNUSED(decoder); + + if (!mpp_data->cached) { + mpp_dec_data_release(mpp_data); + } + + return; +} + +static void mpp_dec_cache_free_cb(void *node, void *user_data) +{ + lv_image_cache_data_t *entry = (lv_image_cache_data_t *)node; + LV_UNUSED(user_data); + + /* MPP-decoded entries have user_data pointing to mpp_decoder_data_t */ + if(entry->user_data) { + mpp_decoder_data_t *mpp_data = (mpp_decoder_data_t *)entry->user_data; + + mpp_dec_data_release(mpp_data); + + if(entry->src_type == LV_IMAGE_SRC_FILE) + lv_free((void *)entry->src); + return; + } + + /* Default cleanup for non-MPP entries */ + lv_draw_buf_t *decoded = (lv_draw_buf_t *)entry->decoded; + if(decoded && lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_ALLOCATED)) { + lv_draw_buf_destroy(decoded); + } + + if(entry->src_type == LV_IMAGE_SRC_FILE) + lv_free((void *)entry->src); +} + +void lv_mpp_dec_init(void) +{ + lv_image_decoder_t *dec = lv_image_decoder_create(); + + lv_image_decoder_set_info_cb(dec, lv_mpp_dec_info); + lv_image_decoder_set_open_cb(dec, lv_mpp_dec_open); + lv_image_decoder_set_close_cb(dec, lv_mpp_dec_close); + + /* Replace image cache free callback to handle MPP-decoded entries */ + if(lv_image_cache_is_enabled()) { + lv_cache_set_free_cb(img_cache_p, mpp_dec_cache_free_cb, NULL); + } +} + +void lv_mpp_dec_deinit(void) +{ + lv_image_decoder_t * dec = NULL; + while ((dec = lv_image_decoder_get_next(dec)) != NULL) { + if (dec->info_cb == lv_mpp_dec_info) { + lv_image_decoder_delete(dec); + break; + } + } +} + diff --git a/src/draw/artinchip/mpp_dec/lv_mpp_dec.h b/src/draw/artinchip/mpp_dec/lv_mpp_dec.h new file mode 100644 index 0000000000000000000000000000000000000000..542f646b7f95d56a40f2829de04c6595c14e9d8d --- /dev/null +++ b/src/draw/artinchip/mpp_dec/lv_mpp_dec.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024-2026, ArtInChip Technology Co., Ltd + * + * SPDX-License-Identifier: Apache-2.0 + * + * Authors: Ning Fang + */ + +#ifndef LV_MPP_DEC_H +#define LV_MPP_DEC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lvgl.h" +#include "mpp_decoder.h" +#include "frame_allocator.h" + +#ifndef MPP_JPEG_DEC_OUT_SIZE_LIMIT_ENABLE +#define MPP_JPEG_DEC_OUT_SIZE_LIMIT_ENABLE +#endif + +#ifndef MPP_JPEG_DEC_MAX_OUT_WIDTH +#define MPP_JPEG_DEC_MAX_OUT_WIDTH 2048 +#endif + +#ifndef MPP_JPEG_DEC_MAX_OUT_HEIGHT +#define MPP_JPEG_DEC_MAX_OUT_HEIGHT 2048 +#endif + +#define CHECK_RET(func, ret) do {\ + if (func != ret) {\ + LV_LOG_ERROR("Invalid status");\ + res = LV_RESULT_INVALID;\ + goto out;\ + }\ + } while (0) + +#define CHECK_PTR(ptr) do {\ + if (ptr == NULL) {\ + LV_LOG_ERROR("ptr == NULL");\ + res = LV_RESULT_INVALID;\ + goto out;\ + }\ + } while (0) + +typedef struct { + lv_draw_buf_t decoded; + struct mpp_buf dec_buf; + uint8_t *data[3]; + bool cached; +} mpp_decoder_data_t; + +static inline int jpeg_width_limit(int width) +{ + int r = 0; + + while(width > MPP_JPEG_DEC_MAX_OUT_WIDTH) { + width = width >> 1; + r++; + + if (r == 3) { + break; + } + } + return r; +} + +static inline int jpeg_height_limit(int height) +{ + int r = 0; + + while(height > MPP_JPEG_DEC_MAX_OUT_HEIGHT) { + height = height >> 1; + r++; + + if (r == 3) { + break; + } + } + return r; +} + +static inline bool image_suffix_is_jpg(char *ptr) +{ + return ((!strncmp(ptr, ".jpg", 4)) || (!strncmp(ptr, ".jpeg", 5)) + || (!strncmp(ptr, ".JPG", 4)) || (!strncmp(ptr, ".JPEG", 5))); +} + +#ifdef AIC_MPP_AICP_DEC_ENABLE +static inline bool image_suffix_is_aicp(char *ptr) +{ + return (!strncmp(ptr, ".aicp", 5)); +} +#endif + +static inline int jpeg_size_limit(int w, int h) +{ + return LV_MAX(jpeg_width_limit(w), jpeg_height_limit(h)); +} + +lv_color_format_t mpp_fmt_to_lv_fmt(enum mpp_pixel_format cf); + +lv_result_t lv_png_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file); + +lv_result_t lv_jpeg_decoder_info(const char *src, lv_image_header_t *header, uint32_t size, bool is_file, bool is_aicp); + +lv_result_t lv_frame_buf_alloc(mpp_decoder_data_t *mpp_data, struct mpp_buf *alloc_buf, + int *size, uint32_t cf); + +void lv_frame_buf_free(mpp_decoder_data_t *mpp_data); + +void lv_set_frame_buf_size(struct mpp_frame *frame, int *buf_size, int size_shift, bool is_aicp); + +struct frame_allocator* lv_open_allocator(struct mpp_frame* frame); + +void *mpp_dec_malloc_oom(size_t size); +void mpp_dec_free_oom(void *ptr); +void *mpp_dec_memalign_oom(size_t size, size_t align); + +void lv_mpp_dec_init(void); + +void lv_mpp_dec_deinit(void); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif //LV_MPP_DEC_H diff --git a/src/lv_api_map_v8.h b/src/lv_api_map_v8.h index ff8fc998fb330aa767c759636d3aed1683e53a2f..41600a8787825ffc467a0b8cf95f9cc0b711f117 100644 --- a/src/lv_api_map_v8.h +++ b/src/lv_api_map_v8.h @@ -52,6 +52,7 @@ typedef lv_display_rotation_t lv_disp_rotation_t; typedef lv_display_render_mode_t lv_disp_render_t; typedef lv_anim_completed_cb_t lv_anim_ready_cb_t; typedef lv_screen_load_anim_t lv_scr_load_anim_t; +typedef lv_buttonmatrix_ctrl_t lv_btnmatrix_ctrl_t; /********************** * GLOBAL PROTOTYPES @@ -248,6 +249,7 @@ static inline void lv_obj_move_background(lv_obj_t * obj) #define lv_obj_set_style_bg_img_src lv_obj_set_style_bg_image_src #define lv_obj_set_style_bg_img_recolor lv_obj_set_style_bg_image_recolor #define lv_obj_set_style_bg_img_recolor_opa lv_obj_set_style_bg_image_recolor_opa +#define lv_obj_set_style_arc_img_src lv_obj_set_style_arc_image_src #define lv_style_set_anim_time lv_style_set_anim_duration #define lv_style_set_img_opa lv_style_set_image_opa @@ -271,6 +273,71 @@ static inline void lv_obj_move_background(lv_obj_t * obj) #define lv_image_set_angle lv_image_set_rotation #define lv_image_set_zoom lv_image_set_scale +#define LV_IMGBTN_STATE_RELEASED LV_IMAGEBUTTON_STATE_RELEASED +#define LV_IMGBTN_STATE_PRESSED LV_IMAGEBUTTON_STATE_PRESSED +#define LV_IMGBTN_STATE_DISABLED LV_IMAGEBUTTON_STATE_DISABLED +#define LV_IMGBTN_STATE_CHECKED_RELEASED LV_IMAGEBUTTON_STATE_CHECKED_RELEASED +#define LV_IMGBTN_STATE_CHECKED_PRESSED LV_IMAGEBUTTON_STATE_CHECKED_PRESSED +#define LV_IMGBTN_STATE_CHECKED_DISABLED LV_IMAGEBUTTON_STATE_CHECKED_DISABLED +#define _LV_IMGBTN_STATE_NUM _LV_IMAGEBUTTON_STATE_NUM + +#define lv_imgbtn_create lv_imagebutton_create +#define lv_imgbtn_set_src lv_imagebutton_set_src +#define lv_imgbtn_set_state lv_imagebutton_set_state +#define lv_imgbtn_get_src_left lv_imagebutton_get_src_left +#define lv_imgbtn_get_src_middle lv_imagebutton_get_src_middle +#define lv_imgbtn_get_src_right lv_imagebutton_get_src_right + +#define lv_img_cache_invalidate_src lv_image_cache_drop + +#define lv_style_set_bg_img_src lv_style_set_bg_image_src +#define lv_style_set_bg_img_opa lv_style_set_bg_image_opa +#define lv_style_set_bg_img_recolor lv_style_set_bg_image_recolor +#define lv_style_set_bg_img_recolor_opa lv_style_set_bg_image_recolor_opa +#define lv_style_set_img_opa lv_style_set_image_opa +#define lv_style_set_img_recolor lv_style_set_image_recolor +#define lv_style_set_img_recolor_opa lv_style_set_image_recolor_opa +#define lv_style_set_arc_img_src lv_style_set_arc_image_src + +#define lv_obj_set_style_bg_img_src lv_obj_set_style_bg_image_src +#define lv_obj_set_style_bg_img_opa lv_obj_set_style_bg_image_opa +#define lv_obj_set_style_bg_img_recolor lv_obj_set_style_bg_image_recolor +#define lv_obj_set_style_bg_img_recolor_opa lv_obj_set_style_bg_image_recolor_opa +#define lv_obj_set_style_bg_img_tiled lv_obj_set_style_bg_image_tiled + +#define lv_obj_get_style_bg_img_src lv_obj_get_style_bg_image_src +#define lv_obj_get_style_bg_img_opa lv_obj_get_style_bg_image_opa +#define lv_obj_get_style_bg_img_recolor lv_obj_get_style_bg_image_recolor +#define lv_obj_get_style_bg_img_recolor_filtered lv_obj_get_style_bg_image_recolor_filtered +#define lv_obj_get_style_bg_img_recolor_opa lv_obj_get_style_bg_image_recolor_opa +#define lv_obj_get_style_bg_img_tiled lv_obj_get_style_bg_image_tiled + +#define LV_MENU_ROOT_BACK_BTN_DISABLED LV_MENU_ROOT_BACK_BUTTON_DISABLED +#define LV_MENU_ROOT_BACK_BTN_ENABLED LV_MENU_ROOT_BACK_BUTTON_ENABLED +#define lv_btnmatrix_set_selected_btn lv_buttonmatrix_set_selected_button +#define lv_btnmatrix_set_btn_ctrl lv_buttonmatrix_set_button_ctrl +#define lv_btnmatrix_clear_btn_ctrl lv_buttonmatrix_clear_button_ctrl +#define lv_btnmatrix_set_btn_ctrl_all lv_buttonmatrix_set_button_ctrl_all +#define lv_btnmatrix_clear_btn_ctrl_all lv_buttonmatrix_clear_button_ctrl_all +#define lv_btnmatrix_set_btn_width lv_buttonmatrix_set_button_width +#define lv_btnmatrix_get_selected_btn lv_buttonmatrix_get_selected_button +#define lv_btnmatrix_get_btn_text lv_buttonmatrix_get_button_text +#define lv_btnmatrix_has_btn_ctrl lv_buttonmatrix_has_button_ctrl + +#define lv_menu_set_mode_root_back_btn lv_menu_set_mode_root_back_button +#define lv_menu_get_main_header_back_btn lv_menu_get_main_header_back_button +#define lv_menu_get_sidebar_header_back_btn lv_menu_get_sidebar_header_back_button +#define lv_menu_back_btn_is_root lv_menu_back_button_is_root + +#define lv_event_get_target lv_event_get_target_obj + +#define lv_label_set_recolor(x, y) LV_UNUSED(x);LV_UNUSED(y) + +#define lv_img_decoder_get_info lv_image_decoder_get_info + +#define lv_mem_alloc lv_malloc +#define lv_mem_realloc lv_realloc +#define lv_mem_free lv_free /********************** * MACROS **********************/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 0a1e2981065783205519305e36d4245ea24fdea6..bfb3f2721b0fff4a6a0dbb510d086d2d6f1d751d 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -657,6 +657,33 @@ #endif #endif +/* Use ArtInChip GE2D GPU. */ +#ifndef LV_USE_DRAW_GE2D + #ifdef CONFIG_LV_USE_DRAW_GE2D + #define LV_USE_DRAW_GE2D CONFIG_LV_USE_DRAW_GE2D + #else + #define LV_USE_DRAW_GE2D 0 + #endif +#endif + +/* Use ArtInChip MPP hardware image decoder. */ +#ifndef LV_USE_MPP_DEC + #ifdef CONFIG_LV_USE_MPP_DEC + #define LV_USE_MPP_DEC CONFIG_LV_USE_MPP_DEC + #else + #define LV_USE_MPP_DEC 0 + #endif +#endif + +/* Use ArtInChip BMP decoder. */ +#ifndef LV_USE_AIC_BMP + #ifdef CONFIG_LV_USE_AIC_BMP + #define LV_USE_AIC_BMP CONFIG_LV_USE_AIC_BMP + #else + #define LV_USE_AIC_BMP 0 + #endif +#endif + /*======================= * FEATURE CONFIGURATION *=======================*/ diff --git a/src/lv_init.c b/src/lv_init.c index 49408ae95b942a920c85c385da9175a6f9b512fd..433bea7a3f5d3b337d10b56d17a9b3a68c2853b7 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -54,6 +54,12 @@ #if LV_USE_DRAW_G2D #include "draw/sunxi_g2d/lv_draw_g2d.h" #endif +#if LV_USE_DRAW_GE2D + #include "draw/artinchip/ge2d/lv_draw_ge2d.h" +#endif +#if LV_USE_MPP_DEC + #include "draw/artinchip/mpp_dec/lv_mpp_dec.h" +#endif #if LV_USE_WINDOWS #include "drivers/windows/lv_windows_context.h" #endif @@ -205,6 +211,10 @@ void lv_init(void) lv_draw_g2d_init(); #endif +#if LV_USE_DRAW_GE2D + lv_draw_ge2d_init(); +#endif + #if LV_USE_DRAW_DAVE2D lv_draw_dave2d_init(); #endif @@ -314,6 +324,10 @@ void lv_init(void) lv_bmp_init(); #endif +#if LV_USE_MPP_DEC + lv_mpp_dec_init(); +#endif + #if LV_USE_ETC2 lv_etc2_init(); #endif @@ -419,6 +433,14 @@ void lv_deinit(void) lv_draw_g2d_deinit(); #endif +#if LV_USE_DRAW_GE2D + lv_draw_ge2d_deinit(); +#endif + +#if LV_USE_MPP_DEC + lv_mpp_dec_deinit(); +#endif + lv_draw_deinit(); _lv_group_deinit();