Initial commit with Waveshare ESP32-S3-LCD-1.47 demo files

This commit is contained in:
drjones
2026-06-30 01:06:03 -07:00
commit 23107b98d7
2986 changed files with 819030 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
CSRCS += lv_gpu_arm2d.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/**
* @file lv_gpu_arm2d.h
*
*/
#ifndef LV_GPU_ARM2D_H
#define LV_GPU_ARM2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
#include "../sw/lv_draw_sw.h"
#if LV_USE_GPU_ARM2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_arm2d_ctx_t;
struct _lv_disp_drv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_arm2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_arm2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_ARM2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_ARM2D_H*/

View File

@@ -0,0 +1,53 @@
/**
* @file lv_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_init(void)
{
/*Nothing to init now*/
}
void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx)
{
if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,219 @@
/**
* @file lv_draw.h
*
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_style.h"
#include "../misc/lv_txt.h"
#include "lv_img_decoder.h"
#include "lv_img_cache.h"
#include "lv_draw_rect.h"
#include "lv_draw_label.h"
#include "lv_draw_img.h"
#include "lv_draw_line.h"
#include "lv_draw_triangle.h"
#include "lv_draw_arc.h"
#include "lv_draw_mask.h"
#include "lv_draw_transform.h"
#include "lv_draw_layer.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
void * user_data;
} lv_draw_mask_t;
typedef struct _lv_draw_layer_ctx_t {
lv_area_t area_full;
lv_area_t area_act;
lv_coord_t max_row_with_alpha;
lv_coord_t max_row_with_no_alpha;
void * buf;
struct {
const lv_area_t * clip_area;
lv_area_t * buf_area;
void * buf;
bool screen_transp;
} original;
} lv_draw_layer_ctx_t;
typedef struct _lv_draw_ctx_t {
/**
* Pointer to a buffer to draw into
*/
void * buf;
/**
* The position and size of `buf` (absolute coordinates)
*/
lv_area_t * buf_area;
/**
* The current clip area with absolute coordinates, always the same or smaller than `buf_area`
*/
const lv_area_t * clip_area;
void (*init_buf)(struct _lv_draw_ctx_t * draw_ctx);
void (*draw_rect)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void (*draw_arc)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
void (*draw_img_decoded)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
lv_res_t (*draw_img)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
void (*draw_letter)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void (*draw_line)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
void (*draw_polygon)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc,
const lv_point_t * points, uint16_t point_cnt);
/**
* Get an area of a transformed image (zoomed and/or rotated)
* @param draw_ctx pointer to a draw context
* @param dest_area get this area of the result image. It assumes that the original image is placed to the 0;0 position.
* @param src_buf the source image
* @param src_w width of the source image in [px]
* @param src_h height of the source image in [px]
* @param src_stride the stride in [px].
* @param draw_dsc an `lv_draw_img_dsc_t` descriptor containing the transformation parameters
* @param cf the color format of `src_buf`
* @param cbuf place the colors of the pixels on `dest_area` here in RGB format
* @param abuf place the opacity of the pixels on `dest_area` here
*/
void (*draw_transform)(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf,
lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride,
const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf);
/**
* Replace the buffer with a rect without decoration like radius or borders
*/
void (*draw_bg)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_area_t * coords);
/**
* Wait until all background operations are finished. (E.g. GPU operations)
*/
void (*wait_for_finish)(struct _lv_draw_ctx_t * draw_ctx);
/**
* Copy an area from buffer to an other
* @param draw_ctx pointer to a draw context
* @param dest_buf copy the buffer into this buffer
* @param dest_stride the width of the dest_buf in pixels
* @param dest_area the destination area
* @param src_buf copy from this buffer
* @param src_stride the width of src_buf in pixels
* @param src_area the source area.
*
* @note dest_area and src_area must have the same width and height
* but can have different x and y position.
* @note dest_area and src_area must be clipped to the real dimensions of the buffers
*/
void (*buffer_copy)(struct _lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride,
const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
/**
* Initialize a new layer context.
* The original buffer and area data are already saved from `draw_ctx` to `layer_ctx`
* @param draw_ctx pointer to the current draw context
* @param layer_area the coordinates of the layer
* @param flags OR-ed flags from @lv_draw_layer_flags_t
* @return pointer to the layer context, or NULL on error
*/
struct _lv_draw_layer_ctx_t * (*layer_init)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
/**
* Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`.
* It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE`
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
* @param flags OR-ed flags from @lv_draw_layer_flags_t
*/
void (*layer_adjust)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
/**
* Blend a rendered layer to `layer_ctx->area_act`
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
* @param draw_dsc pointer to an image draw descriptor
*/
void (*layer_blend)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
const lv_draw_img_dsc_t * draw_dsc);
/**
* Destroy a layer context. The original buffer and area data of the `draw_ctx` will be restored
* and the `layer_ctx` itself will be freed automatically.
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
*/
void (*layer_destroy)(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx);
/**
* Size of a layer context in bytes.
*/
size_t layer_instance_size;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_init(void);
void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* POST INCLUDES
*********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_H*/

View File

@@ -0,0 +1,25 @@
CSRCS += lv_draw_arc.c
CSRCS += lv_draw.c
CSRCS += lv_draw_img.c
CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c
CSRCS += lv_draw_mask.c
CSRCS += lv_draw_rect.c
CSRCS += lv_draw_transform.c
CSRCS += lv_draw_layer.c
CSRCS += lv_draw_triangle.c
CSRCS += lv_img_buf.c
CSRCS += lv_img_cache.c
CSRCS += lv_img_decoder.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw"
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d/lv_draw_arm2d.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/lv_draw_nxp.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl/lv_draw_sdl.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk

View File

@@ -0,0 +1,152 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_arc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_arc_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
void lv_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
draw_ctx->draw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
// const lv_draw_backend_t * backend = lv_draw_backend_get();
// backend->draw_arc(center_x, center_y, radius, start_angle, end_angle, clip_area, dsc);
}
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area)
{
lv_coord_t rout = radius;
/*Special case: full arc invalidation */
if(end_angle == start_angle + 360) {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
return;
}
if(start_angle > 360) start_angle -= 360;
if(end_angle > 360) end_angle -= 360;
lv_coord_t rin = radius - w;
lv_coord_t extra_area = rounded ? w / 2 + 1 : 0;
uint8_t start_quarter = start_angle / 90;
uint8_t end_quarter = end_angle / 90;
/*360 deg still counts as quarter 3 (360 / 90 would be 4)*/
if(start_quarter == 4) start_quarter = 3;
if(end_quarter == 4) end_quarter = 3;
if(start_quarter == end_quarter && start_angle <= end_angle) {
if(start_quarter == 0) {
area->y1 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 1) {
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
}
else if(start_quarter == 2) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
}
else if(start_quarter == 0 && end_quarter == 1) {
area->x1 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((LV_MIN(lv_trigo_sin(end_angle),
lv_trigo_sin(start_angle)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + rout + extra_area;
}
else if(start_quarter == 1 && end_quarter == 2) {
area->x1 = x - rout - extra_area;
area->y1 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + ((LV_MAX(lv_trigo_sin(start_angle + 90),
lv_trigo_sin(end_angle + 90)) * rin) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 2 && end_quarter == 3) {
area->x1 = x + ((lv_trigo_sin(start_angle + 90) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y - rout - extra_area;
area->x2 = x + ((lv_trigo_sin(end_angle + 90) * rout) >> LV_TRIGO_SHIFT) + extra_area;
area->y2 = y + (LV_MAX(lv_trigo_sin(end_angle) * rin,
lv_trigo_sin(start_angle) * rin) >> LV_TRIGO_SHIFT) + extra_area;
}
else if(start_quarter == 3 && end_quarter == 0) {
area->x1 = x + ((LV_MIN(lv_trigo_sin(end_angle + 90),
lv_trigo_sin(start_angle + 90)) * rin) >> LV_TRIGO_SHIFT) - extra_area;
area->y1 = y + ((lv_trigo_sin(start_angle) * rout) >> LV_TRIGO_SHIFT) - extra_area;
area->x2 = x + rout + extra_area;
area->y2 = y + ((lv_trigo_sin(end_angle) * rout) >> LV_TRIGO_SHIFT) + extra_area;
}
else {
area->x1 = x - rout;
area->y1 = y - rout;
area->x2 = x + rout;
area->y2 = y + rout;
}
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,83 @@
/**
* @file lv_draw_arc.h
*
*/
#ifndef LV_DRAW_ARC_H
#define LV_DRAW_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
uint16_t start_angle;
uint16_t end_angle;
const void * img_src;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t rounded : 1;
} lv_draw_arc_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc);
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param clip_area the arc will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_draw_arc(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
/**
* Get an area the should be invalidated when the arcs angle changed between start_angle and end_ange
* @param x the x coordinate of the center of the arc
* @param y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param w width of the arc
* @param rounded true: the arc is rounded
* @param area store the area to invalidate here
*/
void lv_draw_arc_get_area(lv_coord_t x, lv_coord_t y, uint16_t radius, uint16_t start_angle, uint16_t end_angle,
lv_coord_t w, bool rounded, lv_area_t * area);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_ARC_H*/

View File

@@ -0,0 +1,375 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_img.h"
#include "lv_img_cache.h"
#include "../hal/lv_hal_disp.h"
#include "../misc/lv_log.h"
#include "../core/lv_refr.h"
#include "../misc/lv_mem.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t /* LV_ATTRIBUTE_FAST_MEM */ decode_and_draw(lv_draw_ctx_t * draw_ctx,
const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg);
static void draw_cleanup(_lv_img_cache_entry_t * cache);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
dsc->recolor = lv_color_black();
dsc->opa = LV_OPA_COVER;
dsc->zoom = LV_IMG_ZOOM_NONE;
dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
}
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const void * src)
{
if(src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
show_error(draw_ctx, coords, "No\ndata");
return;
}
if(dsc->opa <= LV_OPA_MIN) return;
lv_res_t res = LV_RES_INV;
if(draw_ctx->draw_img) {
res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
}
if(res != LV_RES_OK) {
res = decode_and_draw(draw_ctx, dsc, coords, src);
}
if(res != LV_RES_OK) {
LV_LOG_WARN("Image draw error");
show_error(draw_ctx, coords, "No\ndata");
}
}
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
{
uint8_t px_size = 0;
switch(cf) {
case LV_IMG_CF_UNKNOWN:
case LV_IMG_CF_RAW:
px_size = 0;
break;
case LV_IMG_CF_TRUE_COLOR:
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
px_size = LV_COLOR_SIZE;
break;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
break;
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_ALPHA_1BIT:
px_size = 1;
break;
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_ALPHA_2BIT:
px_size = 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_ALPHA_4BIT:
px_size = 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_8BIT:
px_size = 8;
break;
default:
px_size = 0;
break;
}
return px_size;
}
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
{
bool is_chroma_keyed = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
case LV_IMG_CF_RAW_CHROMA_KEYED:
is_chroma_keyed = true;
break;
default:
is_chroma_keyed = false;
break;
}
return is_chroma_keyed;
}
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf)
{
bool has_alpha = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RAW_ALPHA:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
has_alpha = true;
break;
default:
has_alpha = false;
break;
}
return has_alpha;
}
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src)
{
lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
if(src == NULL) return img_src_type;
const uint8_t * u8_p = src;
/*The first or fourth byte depending on platform endianess shows the type of the image source*/
#if LV_BIG_ENDIAN_SYSTEM
if(u8_p[3] >= 0x20 && u8_p[3] <= 0x7F) {
#else
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
#endif
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
}
#if LV_BIG_ENDIAN_SYSTEM
else if(u8_p[3] >= 0x80) {
#else
else if(u8_p[0] >= 0x80) {
#endif
img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
}
else {
img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
}
if(LV_IMG_SRC_UNKNOWN == img_src_type) {
LV_LOG_WARN("lv_img_src_get_type: unknown image type");
}
return img_src_type;
}
void lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
{
if(draw_ctx->draw_img_decoded == NULL) return;
draw_ctx->draw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t LV_ATTRIBUTE_FAST_MEM decode_and_draw(lv_draw_ctx_t * draw_ctx,
const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src)
{
if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
if(cdsc == NULL) return LV_RES_INV;
lv_img_cf_t cf;
if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
else if(LV_IMG_CF_ALPHA_8BIT == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_ALPHA_8BIT;
else if(LV_IMG_CF_RGB565A8 == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_RGB565A8;
else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
else cf = LV_IMG_CF_TRUE_COLOR;
if(cf == LV_IMG_CF_ALPHA_8BIT) {
if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
/* resume normal method */
cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
cdsc->dec_dsc.img_data = NULL;
}
}
if(cdsc->dec_dsc.error_msg != NULL) {
LV_LOG_WARN("Image draw error");
show_error(draw_ctx, coords, cdsc->dec_dsc.error_msg);
}
/*The decoder could open the image and gave the entire uncompressed image.
*Just draw it!*/
else if(cdsc->dec_dsc.img_data) {
lv_area_t map_area_rot;
lv_area_copy(&map_area_rot, coords);
if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
_lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
map_area_rot.x1 += coords->x1;
map_area_rot.y1 += coords->y1;
map_area_rot.x2 += coords->x1;
map_area_rot.y2 += coords->y1;
}
lv_area_t clip_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&clip_com, draw_ctx->clip_area, &map_area_rot);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
draw_ctx->clip_area = &clip_com;
lv_draw_img_decoded(draw_ctx, draw_dsc, coords, cdsc->dec_dsc.img_data, cf);
draw_ctx->clip_area = clip_area_ori;
}
/*The whole uncompressed image is not available. Try to read it line-by-line*/
else {
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = _lv_area_intersect(&mask_com, draw_ctx->clip_area, coords);
/*Out of mask. There is nothing to draw so the image is drawn successfully.*/
if(union_ok == false) {
draw_cleanup(cdsc);
return LV_RES_OK;
}
int32_t width = lv_area_get_width(&mask_com);
uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
lv_area_t line;
lv_area_copy(&line, &mask_com);
lv_area_set_height(&line, 1);
int32_t x = mask_com.x1 - coords->x1;
int32_t y = mask_com.y1 - coords->y1;
int32_t row;
lv_res_t read_res;
for(row = mask_com.y1; row <= mask_com.y2; row++) {
lv_area_t mask_line;
union_ok = _lv_area_intersect(&mask_line, clip_area_ori, &line);
if(union_ok == false) continue;
read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
if(read_res != LV_RES_OK) {
lv_img_decoder_close(&cdsc->dec_dsc);
LV_LOG_WARN("Image draw can't read the line");
lv_mem_buf_release(buf);
draw_cleanup(cdsc);
draw_ctx->clip_area = clip_area_ori;
return LV_RES_INV;
}
draw_ctx->clip_area = &mask_line;
lv_draw_img_decoded(draw_ctx, draw_dsc, &line, buf, cf);
line.y1++;
line.y2++;
y++;
}
draw_ctx->clip_area = clip_area_ori;
lv_mem_buf_release(buf);
}
draw_cleanup(cdsc);
return LV_RES_OK;
}
static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg)
{
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_color = lv_color_white();
lv_draw_rect(draw_ctx, &rect_dsc, coords);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_draw_label(draw_ctx, &label_dsc, coords, msg, NULL);
}
static void draw_cleanup(_lv_img_cache_entry_t * cache)
{
/*Automatically close images with no caching*/
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(&cache->dec_dsc);
#else
LV_UNUSED(cache);
#endif
}

View File

@@ -0,0 +1,104 @@
/**
* @file lv_draw_img.h
*
*/
#ifndef LV_DRAW_IMG_H
#define LV_DRAW_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "lv_img_buf.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
int16_t angle;
uint16_t zoom;
lv_point_t pivot;
lv_color_t recolor;
lv_opa_t recolor_opa;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 4;
int32_t frame_id;
uint8_t antialias : 1;
} lv_draw_img_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc);
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
*/
void lv_draw_img(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords,
const void * src);
void lv_draw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src);
/**
* Get the pixel size of a color format in bits
* @param cf a color format (`LV_IMG_CF_...`)
* @return the pixel size in bits
*/
uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf);
/**
* Check if a color format is chroma keyed or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: chroma keyed; false: not chroma keyed
*/
bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf);
/**
* Check if a color format has alpha channel or not
* @param cf a color format (`LV_IMG_CF_...`)
* @return true: has alpha channel; false: doesn't have alpha channel
*/
bool lv_img_cf_has_alpha(lv_img_cf_t cf);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_IMG_H*/

View File

@@ -0,0 +1,417 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_label.h"
#include "../misc/lv_math.h"
#include "../hal/lv_hal_disp.h"
#include "../core/lv_refr.h"
#include "../misc/lv_bidi.h"
#include "../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
#define LV_LABEL_HINT_UPDATE_TH 1024 /*Update the "hint" if the label's y coordinates have changed more then this*/
/**********************
* TYPEDEFS
**********************/
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_label_dsc_t));
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
dsc->font = LV_FONT_DEFAULT;
dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL;
dsc->sel_color = lv_color_black();
dsc->sel_bg_color = lv_palette_main(LV_PALETTE_BLUE);
dsc->bidi_dir = LV_BASE_DIR_LTR;
}
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
void LV_ATTRIBUTE_FAST_MEM lv_draw_label(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint)
{
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->font == NULL) {
LV_LOG_WARN("dsc->font == NULL");
return;
}
if(draw_ctx->draw_letter == NULL) {
LV_LOG_WARN("draw->draw_letter == NULL (there is no function to draw letters)");
return;
}
lv_draw_label_dsc_t dsc_mod = *dsc;
const lv_font_t * font = dsc->font;
int32_t w;
/*No need to waste processor time if string is empty*/
if(txt == NULL || txt[0] == '\0')
return;
lv_area_t clipped_area;
bool clip_ok = _lv_area_intersect(&clipped_area, coords, draw_ctx->clip_area);
if(!clip_ok) return;
lv_text_align_t align = dsc->align;
lv_base_dir_t base_dir = dsc->bidi_dir;
lv_bidi_calculate_align(&align, &base_dir, txt);
if((dsc->flag & LV_TEXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
}
else {
/*If EXPAND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
lv_txt_get_size(&p, txt, dsc->font, dsc->letter_space, dsc->line_space, LV_COORD_MAX,
dsc->flag);
w = p.x;
}
int32_t line_height_font = lv_font_get_line_height(font);
int32_t line_height = line_height_font + dsc->line_space;
/*Init variables for the first line*/
int32_t line_width = 0;
lv_point_t pos;
pos.x = coords->x1;
pos.y = coords->y1;
int32_t x_ofs = 0;
int32_t y_ofs = 0;
x_ofs = dsc->ofs_x;
y_ofs = dsc->ofs_y;
pos.y += y_ofs;
uint32_t line_start = 0;
int32_t last_line_start = -1;
/*Check the hint to use the cached info*/
if(hint && y_ofs == 0 && coords->y1 < 0) {
/*If the label changed too much recalculate the hint.*/
if(LV_ABS(hint->coord_y - coords->y1) > LV_LABEL_HINT_UPDATE_TH - 2 * line_height) {
hint->line_start = -1;
}
last_line_start = hint->line_start;
}
/*Use the hint if it's valid*/
if(hint && last_line_start >= 0) {
line_start = last_line_start;
pos.y += hint->y;
}
uint32_t line_end = line_start + _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
/*Go the first visible line*/
while(pos.y + line_height_font < draw_ctx->clip_area->y1) {
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
pos.y += line_height;
/*Save at the threshold coordinate*/
if(hint && pos.y >= -LV_LABEL_HINT_UPDATE_TH && hint->line_start < 0) {
hint->line_start = line_start;
hint->y = pos.y - coords->y1;
hint->coord_y = coords->y1;
}
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
uint32_t sel_start = dsc->sel_start;
uint32_t sel_end = dsc->sel_end;
if(sel_start > sel_end) {
uint32_t tmp = sel_start;
sel_start = sel_end;
sel_end = tmp;
}
lv_draw_line_dsc_t line_dsc;
if((dsc->decor & LV_TEXT_DECOR_UNDERLINE) || (dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH)) {
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = dsc->color;
line_dsc.width = font->underline_thickness ? font->underline_thickness : 1;
line_dsc.opa = dsc->opa;
line_dsc.blend_mode = dsc->blend_mode;
}
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint32_t par_start = 0;
lv_color_t recolor = lv_color_black();
lv_color_t color = lv_color_black();
int32_t letter_w;
lv_draw_rect_dsc_t draw_dsc_sel;
lv_draw_rect_dsc_init(&draw_dsc_sel);
draw_dsc_sel.bg_color = dsc->sel_bg_color;
int32_t pos_x_start = pos.x;
/*Write out all lines*/
while(txt[line_start] != '\0') {
pos.x += x_ofs;
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = 0;
#if LV_USE_BIDI
char * bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
_lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0);
#else
const char * bidi_txt = txt + line_start;
#endif
while(i < line_end - line_start) {
uint32_t logical_char_pos = 0;
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
#if LV_USE_BIDI
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start);
uint32_t t = _lv_txt_encoded_get_char_id(bidi_txt, i);
logical_char_pos += _lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, t, NULL);
#else
logical_char_pos = _lv_txt_encoded_get_char_id(txt, line_start + i);
#endif
}
uint32_t letter;
uint32_t letter_next;
_lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
/*Handle the re-color command*/
if((dsc->flag & LV_TEXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
par_start = i;
cmd_state = CMD_STATE_PAR;
continue;
}
else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char*/
cmd_state = CMD_STATE_WAIT;
}
else if(cmd_state == CMD_STATE_IN) { /*Command end*/
cmd_state = CMD_STATE_WAIT;
continue;
}
}
/*Skip the color parameter and wait the space after it*/
if(cmd_state == CMD_STATE_PAR) {
if(letter == ' ') {
/*Get the parameter*/
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
lv_memcpy_small(buf, &bidi_txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
int r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
}
else {
recolor.full = dsc->color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
color = dsc->color;
if(cmd_state == CMD_STATE_IN) color = recolor;
letter_w = lv_font_get_glyph_width(font, letter, letter_next);
if(sel_start != 0xFFFF && sel_end != 0xFFFF) {
if(logical_char_pos >= sel_start && logical_char_pos < sel_end) {
lv_area_t sel_coords;
sel_coords.x1 = pos.x;
sel_coords.y1 = pos.y;
sel_coords.x2 = pos.x + letter_w + dsc->letter_space - 1;
sel_coords.y2 = pos.y + line_height - 1;
lv_draw_rect(draw_ctx, &draw_dsc_sel, &sel_coords);
color = dsc->sel_color;
}
}
dsc_mod.color = color;
lv_draw_letter(draw_ctx, &dsc_mod, &pos, letter);
if(letter_w > 0) {
pos.x += letter_w + dsc->letter_space;
}
}
if(dsc->decor & LV_TEXT_DECOR_STRIKETHROUGH) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + (dsc->font->line_height / 2) + line_dsc.width / 2;
p2.x = pos.x;
p2.y = p1.y;
line_dsc.color = color;
lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
}
if(dsc->decor & LV_TEXT_DECOR_UNDERLINE) {
lv_point_t p1;
lv_point_t p2;
p1.x = pos_x_start;
p1.y = pos.y + dsc->font->line_height - dsc->font->base_line - font->underline_position;
p2.x = pos.x;
p2.y = p1.y;
line_dsc.color = color;
lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
}
#if LV_USE_BIDI
lv_mem_buf_release(bidi_txt);
bidi_txt = NULL;
#endif
/*Go to next line*/
line_start = line_end;
line_end += _lv_txt_get_next_line(&txt[line_start], font, dsc->letter_space, w, NULL, dsc->flag);
pos.x = coords->x1;
/*Align to middle*/
if(align == LV_TEXT_ALIGN_CENTER) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(align == LV_TEXT_ALIGN_RIGHT) {
line_width =
lv_txt_get_width(&txt[line_start], line_end - line_start, font, dsc->letter_space, dsc->flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > draw_ctx->clip_area->y2) return;
}
LV_ASSERT_MEM_INTEGRITY();
}
void lv_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter)
{
draw_ctx->draw_letter(draw_ctx, dsc, pos_p, letter);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
}
else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
switch(hex) {
case 'A':
result = 10;
break;
case 'B':
result = 11;
break;
case 'C':
result = 12;
break;
case 'D':
result = 13;
break;
case 'E':
result = 14;
break;
case 'F':
result = 15;
break;
default:
result = 0;
break;
}
}
return result;
}

View File

@@ -0,0 +1,100 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../misc/lv_bidi.h"
#include "../misc/lv_txt.h"
#include "../misc/lv_color.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_LABEL_NO_TXT_SEL (0xFFFF)
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_font_t * font;
uint32_t sel_start;
uint32_t sel_end;
lv_color_t color;
lv_color_t sel_color;
lv_color_t sel_bg_color;
lv_coord_t line_space;
lv_coord_t letter_space;
lv_coord_t ofs_x;
lv_coord_t ofs_y;
lv_opa_t opa;
lv_base_dir_t bidi_dir;
lv_text_align_t align;
lv_text_flag_t flag;
lv_text_decor_t decor : 3;
lv_blend_mode_t blend_mode: 3;
} lv_draw_label_dsc_t;
/** Store some info to speed up drawing of very large texts
* It takes a lot of time to get the first visible character because
* all the previous characters needs to be checked to calculate the positions.
* This structure stores an earlier (e.g. at -1000 px) coordinate and the index of that line.
* Therefore the calculations can start from here.*/
typedef struct _lv_draw_label_hint_t {
/** Index of the line at `y` coordinate*/
int32_t line_start;
/** Give the `y` coordinate of the first letter at `line start` index. Relative to the label's coordinates*/
int32_t y;
/** The 'y1' coordinate of the label when the hint was saved.
* Used to invalidate the hint if the label has moved too much.*/
int32_t coord_y;
} lv_draw_label_hint_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc);
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param dsc pointer to draw descriptor
* @param txt `\0` terminated text to write
* @param hint pointer to a `lv_draw_label_hint_t` variable.
* It is managed by the draw to speed up the drawing of very long texts (thousands of lines).
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_label(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint);
void lv_draw_letter(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LABEL_H*/

View File

@@ -0,0 +1,93 @@
/**
* @file lv_draw_layer.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_arc.h"
#include "../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_draw_layer_ctx_t * lv_draw_layer_create(lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area,
lv_draw_layer_flags_t flags)
{
if(draw_ctx->layer_init == NULL) return NULL;
lv_draw_layer_ctx_t * layer_ctx = lv_mem_alloc(draw_ctx->layer_instance_size);
LV_ASSERT_MALLOC(layer_ctx);
if(layer_ctx == NULL) {
LV_LOG_WARN("Couldn't allocate a new layer context");
return NULL;
}
lv_memset_00(layer_ctx, draw_ctx->layer_instance_size);
lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
layer_ctx->original.buf = draw_ctx->buf;
layer_ctx->original.buf_area = draw_ctx->buf_area;
layer_ctx->original.clip_area = draw_ctx->clip_area;
layer_ctx->original.screen_transp = disp_refr->driver->screen_transp;
layer_ctx->area_full = *layer_area;
lv_draw_layer_ctx_t * init_layer_ctx = draw_ctx->layer_init(draw_ctx, layer_ctx, flags);
if(NULL == init_layer_ctx) {
lv_mem_free(layer_ctx);
}
return init_layer_ctx;
}
void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags)
{
if(draw_ctx->layer_adjust) draw_ctx->layer_adjust(draw_ctx, layer_ctx, flags);
}
void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_img_dsc_t * draw_dsc)
{
if(draw_ctx->layer_blend) draw_ctx->layer_blend(draw_ctx, layer_ctx, draw_dsc);
}
void lv_draw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx)
{
lv_draw_wait_for_finish(draw_ctx);
draw_ctx->buf = layer_ctx->original.buf;
draw_ctx->buf_area = layer_ctx->original.buf_area;
draw_ctx->clip_area = layer_ctx->original.clip_area;
lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
disp_refr->driver->screen_transp = layer_ctx->original.screen_transp;
if(draw_ctx->layer_destroy) draw_ctx->layer_destroy(draw_ctx, layer_ctx);
lv_mem_free(layer_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,83 @@
/**
* @file lv_draw_layer.h
*
*/
#ifndef LV_DRAW_LAYER_H
#define LV_DRAW_LAYER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_ctx_t;
struct _lv_draw_layer_ctx_t;
typedef enum {
LV_DRAW_LAYER_FLAG_NONE,
LV_DRAW_LAYER_FLAG_HAS_ALPHA,
LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE,
} lv_draw_layer_flags_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a new layer context. It is used to start and independent rendering session
* with the current draw_ctx
* @param draw_ctx pointer to the current draw context
* @param layer_area the coordinates of the layer
* @param flags OR-ed flags from @lv_draw_layer_flags_t
* @return pointer to the layer context, or NULL on error
*/
struct _lv_draw_layer_ctx_t * lv_draw_layer_create(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area,
lv_draw_layer_flags_t flags);
/**
* Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`.
* It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE`
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
* @param flags OR-ed flags from @lv_draw_layer_flags_t
*/
void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
/**
* Blend a rendered layer to `layer_ctx->area_act`
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
* @param draw_dsc pointer to an image draw descriptor
*/
void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_img_dsc_t * draw_dsc);
/**
* Destroy a layer context.
* @param draw_ctx pointer to the current draw context
* @param layer_ctx pointer to a layer context
*/
void lv_draw_layer_destroy(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LAYER_H*/

View File

@@ -0,0 +1,56 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../core/lv_refr.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_line_dsc_t));
dsc->width = 1;
dsc->opa = LV_OPA_COVER;
dsc->color = lv_color_black();
}
void LV_ATTRIBUTE_FAST_MEM lv_draw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc,
const lv_point_t * point1, const lv_point_t * point2)
{
if(dsc->width == 0) return;
if(dsc->opa <= LV_OPA_MIN) return;
draw_ctx->draw_line(draw_ctx, dsc, point1, point2);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,67 @@
/**
* @file lv_draw_line.h
*
*/
#ifndef LV_DRAW_LINE_H
#define LV_DRAW_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t color;
lv_coord_t width;
lv_coord_t dash_width;
lv_coord_t dash_gap;
lv_opa_t opa;
lv_blend_mode_t blend_mode : 2;
uint8_t round_start : 1;
uint8_t round_end : 1;
uint8_t raw_end : 1; /*Do not bother with perpendicular line ending if it's not visible for any reason*/
} lv_draw_line_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc);
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param clip the line will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc,
const lv_point_t * point1, const lv_point_t * point2);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_LINE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/**
* @file lv_draw_mask.h
*
*/
#ifndef LV_DRAW_MASK_H
#define LV_DRAW_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_area.h"
#include "../misc/lv_color.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_MASK_ID_INV (-1)
#if LV_DRAW_COMPLEX
# define _LV_MASK_MAX_NUM 16
#else
# define _LV_MASK_MAX_NUM 1
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_DRAW_MASK_RES_TRANSP,
LV_DRAW_MASK_RES_FULL_COVER,
LV_DRAW_MASK_RES_CHANGED,
LV_DRAW_MASK_RES_UNKNOWN
};
typedef uint8_t lv_draw_mask_res_t;
typedef struct {
void * param;
void * custom_id;
} _lv_draw_mask_saved_t;
typedef _lv_draw_mask_saved_t _lv_draw_mask_saved_arr_t[_LV_MASK_MAX_NUM];
#if LV_DRAW_COMPLEX == 0
static inline uint8_t lv_draw_mask_get_cnt(void)
{
return 0;
}
static inline bool lv_draw_mask_is_any(const lv_area_t * a)
{
LV_UNUSED(a);
return false;
}
#endif
#if LV_DRAW_COMPLEX
enum {
LV_DRAW_MASK_TYPE_LINE,
LV_DRAW_MASK_TYPE_ANGLE,
LV_DRAW_MASK_TYPE_RADIUS,
LV_DRAW_MASK_TYPE_FADE,
LV_DRAW_MASK_TYPE_MAP,
LV_DRAW_MASK_TYPE_POLYGON,
};
typedef uint8_t lv_draw_mask_type_t;
enum {
LV_DRAW_MASK_LINE_SIDE_LEFT = 0,
LV_DRAW_MASK_LINE_SIDE_RIGHT,
LV_DRAW_MASK_LINE_SIDE_TOP,
LV_DRAW_MASK_LINE_SIDE_BOTTOM,
};
/**
* A common callback type for every mask type.
* Used internally by the library.
*/
typedef lv_draw_mask_res_t (*lv_draw_mask_xcb_t)(lv_opa_t * mask_buf, lv_coord_t abs_x, lv_coord_t abs_y,
lv_coord_t len,
void * p);
typedef uint8_t lv_draw_mask_line_side_t;
typedef struct {
lv_draw_mask_xcb_t cb;
lv_draw_mask_type_t type;
} _lv_draw_mask_common_dsc_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
/*First point*/
lv_point_t p1;
/*Second point*/
lv_point_t p2;
/*Which side to keep?*/
lv_draw_mask_line_side_t side : 2;
} cfg;
/*A point of the line*/
lv_point_t origo;
/*X / (1024*Y) steepness (X is 0..1023 range). What is the change of X in 1024 Y?*/
int32_t xy_steep;
/*Y / (1024*X) steepness (Y is 0..1023 range). What is the change of Y in 1024 X?*/
int32_t yx_steep;
/*Helper which stores yx_steep for flat lines and xy_steep for steep (non flat) lines*/
int32_t steep;
/*Steepness in 1 px in 0..255 range. Used only by flat lines.*/
int32_t spx;
/*1: It's a flat line? (Near to horizontal)*/
uint8_t flat : 1;
/*Invert the mask. The default is: Keep the left part.
*It is used to select left/right/top/bottom*/
uint8_t inv: 1;
} lv_draw_mask_line_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t vertex_p;
lv_coord_t start_angle;
lv_coord_t end_angle;
} cfg;
lv_draw_mask_line_param_t start_line;
lv_draw_mask_line_param_t end_line;
uint16_t delta_deg;
} lv_draw_mask_angle_param_t;
typedef struct {
uint8_t * buf;
lv_opa_t * cir_opa; /*Opacity of values on the circumference of an 1/4 circle*/
uint16_t * x_start_on_y; /*The x coordinate of the circle for each y value*/
uint16_t * opa_start_on_y; /*The index of `cir_opa` for each y value*/
int32_t life; /*How many times the entry way used*/
uint32_t used_cnt; /*Like a semaphore to count the referencing masks*/
lv_coord_t radius; /*The radius of the entry*/
} _lv_draw_mask_radius_circle_dsc_t;
typedef _lv_draw_mask_radius_circle_dsc_t _lv_draw_mask_radius_circle_dsc_arr_t[LV_CIRCLE_CACHE_SIZE];
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t rect;
lv_coord_t radius;
/*Invert the mask. 0: Keep the pixels inside.*/
uint8_t outer: 1;
} cfg;
_lv_draw_mask_radius_circle_dsc_t * circle;
} lv_draw_mask_radius_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
lv_coord_t y_top;
lv_coord_t y_bottom;
lv_opa_t opa_top;
lv_opa_t opa_bottom;
} cfg;
} lv_draw_mask_fade_param_t;
typedef struct _lv_draw_mask_map_param_t {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_area_t coords;
const lv_opa_t * map;
} cfg;
} lv_draw_mask_map_param_t;
typedef struct {
/*The first element must be the common descriptor*/
_lv_draw_mask_common_dsc_t dsc;
struct {
lv_point_t * points;
uint16_t point_cnt;
} cfg;
} lv_draw_mask_polygon_param_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add a draw mask. Everything drawn after it (until removing the mask) will be affected by the mask.
* @param param an initialized mask parameter. Only the pointer is saved.
* @param custom_id a custom pointer to identify the mask. Used in `lv_draw_mask_remove_custom`.
* @return the an integer, the ID of the mask. Can be used in `lv_draw_mask_remove_id`.
*/
int16_t lv_draw_mask_add(void * param, void * custom_id);
//! @cond Doxygen_Suppress
/**
* Apply the added buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_apply(lv_opa_t * mask_buf, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t len);
/**
* Apply the specified buffers on a line. Used internally by the library's drawing routines.
* @param mask_buf store the result mask here. Has to be `len` byte long. Should be initialized with `0xFF`.
* @param abs_x absolute X coordinate where the line to calculate start
* @param abs_y absolute Y coordinate where the line to calculate start
* @param len length of the line to calculate (in pixel count)
* @param ids ID array of added buffers
* @param ids_count number of ID array
* @return One of these values:
* - `LV_DRAW_MASK_RES_FULL_TRANSP`: the whole line is transparent. `mask_buf` is not set to zero
* - `LV_DRAW_MASK_RES_FULL_COVER`: the whole line is fully visible. `mask_buf` is unchanged
* - `LV_DRAW_MASK_RES_CHANGED`: `mask_buf` has changed, it shows the desired opacity of each pixel in the given line
*/
lv_draw_mask_res_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_apply_ids(lv_opa_t * mask_buf, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t len,
const int16_t * ids, int16_t ids_count);
//! @endcond
/**
* Remove a mask with a given ID
* @param id the ID of the mask. Returned by `lv_draw_mask_add`
* @return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_id(int16_t id);
/**
* Remove all mask with a given custom ID
* @param custom_id a pointer used in `lv_draw_mask_add`
* @return return the parameter of the removed mask.
* If more masks have `custom_id` ID then the last mask's parameter will be returned
*/
void * lv_draw_mask_remove_custom(void * custom_id);
/**
* Free the data from the parameter.
* It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom`
* Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add`
* and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom`
* @param p pointer to a mask parameter
*/
void lv_draw_mask_free_param(void * p);
/**
* Called by LVGL the rendering of a screen is ready to clean up
* the temporal (cache) data of the masks
*/
void _lv_draw_mask_cleanup(void);
//! @cond Doxygen_Suppress
/**
* Count the currently added masks
* @return number of active masks
*/
uint8_t /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_mask_get_cnt(void);
/**
* Check if there is any added draw mask
* @param a an area to test for affecting masks.
* @return true: there is t least 1 draw mask; false: there are no draw masks
*/
bool lv_draw_mask_is_any(const lv_area_t * a);
//! @endcond
/**
*Initialize a line mask from two points.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param p1x X coordinate of the first point of the line
* @param p1y Y coordinate of the first point of the line
* @param p2x X coordinate of the second point of the line
* @param p2y y coordinate of the second point of the line
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_points_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t p1y, lv_coord_t p2x,
lv_coord_t p2y, lv_draw_mask_line_side_t side);
/**
*Initialize a line mask from a point and an angle.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param px X coordinate of a point of the line
* @param py X coordinate of a point of the line
* @param angle right 0 deg, bottom: 90
* @param side and element of `lv_draw_mask_line_side_t` to describe which side to keep.
* With `LV_DRAW_MASK_LINE_SIDE_LEFT/RIGHT` and horizontal line all pixels are kept
* With `LV_DRAW_MASK_LINE_SIDE_TOP/BOTTOM` and vertical line all pixels are kept
*/
void lv_draw_mask_line_angle_init(lv_draw_mask_line_param_t * param, lv_coord_t p1x, lv_coord_t py, int16_t angle,
lv_draw_mask_line_side_t side);
/**
* Initialize an angle mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param vertex_x X coordinate of the angle vertex (absolute coordinates)
* @param vertex_y Y coordinate of the angle vertex (absolute coordinates)
* @param start_angle start angle in degrees. 0 deg on the right, 90 deg, on the bottom
* @param end_angle end angle
*/
void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vertex_x, lv_coord_t vertex_y,
lv_coord_t start_angle, lv_coord_t end_angle);
/**
* Initialize a fade mask.
* @param param pointer to an `lv_draw_mask_radius_param_t` to initialize
* @param rect coordinates of the rectangle to affect (absolute coordinates)
* @param radius radius of the rectangle
* @param inv true: keep the pixels inside the rectangle; keep the pixels outside of the rectangle
*/
void lv_draw_mask_radius_init(lv_draw_mask_radius_param_t * param, const lv_area_t * rect, lv_coord_t radius, bool inv);
/**
* Initialize a fade mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the area to affect (absolute coordinates)
* @param opa_top opacity on the top
* @param y_top at which coordinate start to change to opacity to `opa_bottom`
* @param opa_bottom opacity at the bottom
* @param y_bottom at which coordinate reach `opa_bottom`.
*/
void lv_draw_mask_fade_init(lv_draw_mask_fade_param_t * param, const lv_area_t * coords, lv_opa_t opa_top,
lv_coord_t y_top,
lv_opa_t opa_bottom, lv_coord_t y_bottom);
/**
* Initialize a map mask.
* @param param pointer to a `lv_draw_mask_param_t` to initialize
* @param coords coordinates of the map (absolute coordinates)
* @param map array of bytes with the mask values
*/
void lv_draw_mask_map_init(lv_draw_mask_map_param_t * param, const lv_area_t * coords, const lv_opa_t * map);
void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_point_t * points, uint16_t point_cnt);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_MASK_H*/

View File

@@ -0,0 +1,73 @@
/**
* @file lv_draw_rect.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_rect.h"
#include "../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
{
lv_memset_00(dsc, sizeof(lv_draw_rect_dsc_t));
dsc->bg_color = lv_color_white();
dsc->bg_grad.stops[0].color = lv_color_white();
dsc->bg_grad.stops[1].color = lv_color_black();
dsc->bg_grad.stops[1].frac = 0xFF;
dsc->bg_grad.stops_count = 2;
dsc->border_color = lv_color_black();
dsc->shadow_color = lv_color_black();
dsc->bg_img_symbol_font = LV_FONT_DEFAULT;
dsc->bg_opa = LV_OPA_COVER;
dsc->bg_img_opa = LV_OPA_COVER;
dsc->outline_opa = LV_OPA_COVER;
dsc->border_opa = LV_OPA_COVER;
dsc->shadow_opa = LV_OPA_COVER;
dsc->border_side = LV_BORDER_SIDE_FULL;
}
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param mask the rectangle will be drawn only in this mask
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
draw_ctx->draw_rect(draw_ctx, dsc, coords);
LV_ASSERT_MEM_INTEGRITY();
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,96 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
#include "../misc/lv_style.h"
#include "sw/lv_draw_sw_gradient.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE 0x7FFF /**< A very big radius to always draw as circle*/
LV_EXPORT_CONST_INT(LV_RADIUS_CIRCLE);
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_coord_t radius;
lv_blend_mode_t blend_mode;
/*Background*/
lv_opa_t bg_opa;
lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/
lv_grad_dsc_t bg_grad;
/*Background img*/
const void * bg_img_src;
const void * bg_img_symbol_font;
lv_color_t bg_img_recolor;
lv_opa_t bg_img_opa;
lv_opa_t bg_img_recolor_opa;
uint8_t bg_img_tiled;
/*Border*/
lv_color_t border_color;
lv_coord_t border_width;
lv_opa_t border_opa;
uint8_t border_post : 1; /*There is a border it will be drawn later.*/
lv_border_side_t border_side : 5;
/*Outline*/
lv_color_t outline_color;
lv_coord_t outline_width;
lv_coord_t outline_pad;
lv_opa_t outline_opa;
/*Shadow*/
lv_color_t shadow_color;
lv_coord_t shadow_width;
lv_coord_t shadow_ofs_x;
lv_coord_t shadow_ofs_y;
lv_coord_t shadow_spread;
lv_opa_t shadow_opa;
} lv_draw_rect_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc);
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param clip the rectangle will be drawn only in this area
* @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_draw_rect(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_RECT_H*/

View File

@@ -0,0 +1,54 @@
/**
* @file lv_draw_transform.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_transform.h"
#include "../misc/lv_assert.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w,
lv_coord_t src_h,
lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf)
{
LV_ASSERT_NULL(draw_ctx);
if(draw_ctx->draw_transform == NULL) {
LV_LOG_WARN("draw_ctx->draw_transform == NULL");
return;
}
draw_ctx->draw_transform(draw_ctx, dest_area, src_buf, src_w, src_h, src_stride, draw_dsc, cf, cbuf, abuf);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,44 @@
/**
* @file lv_draw_transform.h
*
*/
#ifndef LV_DRAW_TRANSFORM_H
#define LV_DRAW_TRANSFORM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_transform(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf,
lv_coord_t src_w, lv_coord_t src_h,
lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRANSFORM_H*/

View File

@@ -0,0 +1,52 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "lv_draw_triangle.h"
#include "../misc/lv_math.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[],
uint16_t point_cnt)
{
draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, point_cnt);
}
void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[])
{
draw_ctx->draw_polygon(draw_ctx, draw_dsc, points, 3);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,42 @@
/**
* @file lv_draw_triangle.h
*
*/
#ifndef LV_DRAW_TRIANGLE_H
#define LV_DRAW_TRIANGLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[],
uint16_t point_cnt);
void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[]);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_TRIANGLE_H*/

View File

@@ -0,0 +1,392 @@
/**
* @file lv_img_buf.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include <string.h>
#include "lv_img_buf.h"
#include "lv_draw_img.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
#include "../misc/lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_color_t lv_img_buf_get_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
{
lv_color_t p_color = lv_color_black();
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->header.cf == LV_IMG_CF_RGB565A8) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
#if LV_COLOR_SIZE == 32
p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
#endif
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8, 16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2, 4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = dsc->header.w * y + x;
p_color.full = buf_u8[px];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
p_color = color;
}
return p_color;
}
lv_opa_t lv_img_buf_get_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
return opa_table[px_opa];
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
return buf_u8[px];
}
return LV_OPA_COVER;
}
void lv_img_buf_set_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
buf_u8[px + px_size - 1] = opa;
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
opa = opa >> 7; /*opa -> [0,1]*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
opa = opa >> 6; /*opa -> [0,3]*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 4 ,8, 12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
opa = opa >> 4; /*opa -> [0,15]*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = opa;
}
}
void lv_img_buf_set_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
uint8_t * buf_u8 = (uint8_t *)dsc->data;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size);
}
else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
uint32_t px = dsc->header.w * y * px_size + x * px_size;
lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
uint8_t bit = x & 0x7;
x = x >> 3;
/*Get the current pixel.
*dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
*so the possible real width are 8 ,16, 24 ...*/
uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
/*Get the current pixel.
*dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
*so the possible real width are 4, 8 ,12 ...*/
uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
/*Get the current pixel.
*dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
*so the possible real width are 2 ,4, 6 ...*/
uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
uint32_t px = dsc->header.w * y + x;
buf_u8[px] = c.full;
}
}
void lv_img_buf_set_palette(const lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
{
if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
return;
}
lv_color32_t c32;
c32.full = lv_color_to32(c);
uint8_t * buf = (uint8_t *)dsc->data;
lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
}
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
/*Allocate image descriptor*/
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
if(dsc == NULL)
return NULL;
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
/*Get image data size*/
dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
if(dsc->data_size == 0) {
lv_mem_free(dsc);
return NULL;
}
/*Allocate raw buffer*/
dsc->data = lv_mem_alloc(dsc->data_size);
if(dsc->data == NULL) {
lv_mem_free(dsc);
return NULL;
}
lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
/*Fill in header*/
dsc->header.always_zero = 0;
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
return dsc;
}
void lv_img_buf_free(lv_img_dsc_t * dsc)
{
if(dsc != NULL) {
if(dsc->data != NULL)
lv_mem_free((void *)dsc->data);
lv_mem_free(dsc);
}
}
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RGB565A8:
return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
case LV_IMG_CF_ALPHA_1BIT:
return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
case LV_IMG_CF_ALPHA_2BIT:
return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
case LV_IMG_CF_ALPHA_4BIT:
return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
case LV_IMG_CF_ALPHA_8BIT:
return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
case LV_IMG_CF_INDEXED_1BIT:
return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
case LV_IMG_CF_INDEXED_2BIT:
return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
case LV_IMG_CF_INDEXED_4BIT:
return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
case LV_IMG_CF_INDEXED_8BIT:
return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
default:
return 0;
}
}
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot)
{
#if LV_DRAW_COMPLEX
if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
return;
}
lv_point_t p[4] = {
{0, 0},
{w, 0},
{0, h},
{w, h},
};
lv_point_transform(&p[0], angle, zoom, pivot);
lv_point_transform(&p[1], angle, zoom, pivot);
lv_point_transform(&p[2], angle, zoom, pivot);
lv_point_transform(&p[3], angle, zoom, pivot);
res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2;
res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2;
res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2;
res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2;
#else
LV_UNUSED(angle);
LV_UNUSED(zoom);
LV_UNUSED(pivot);
res->x1 = 0;
res->y1 = 0;
res->x2 = w - 1;
res->y2 = h - 1;
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,249 @@
/**
* @file lv_img_buf.h
*
*/
#ifndef LV_IMG_BUF_H
#define LV_IMG_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../misc/lv_color.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/*If image pixels contains alpha we need to know how much byte is a pixel*/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
#define LV_IMG_PX_SIZE_ALPHA_BYTE 2
#elif LV_COLOR_DEPTH == 16
#define LV_IMG_PX_SIZE_ALPHA_BYTE 3
#elif LV_COLOR_DEPTH == 32
#define LV_IMG_PX_SIZE_ALPHA_BYTE 4
#endif
#define LV_IMG_BUF_SIZE_TRUE_COLOR(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) ((LV_COLOR_SIZE / 8) * w * h)
#define LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) (LV_IMG_PX_SIZE_ALPHA_BYTE * w * h)
/*+ 1: to be sure no fractional row*/
#define LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) ((((w / 8) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) ((((w / 4) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) ((((w / 2) + 1) * h))
#define LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) ((w * h))
/*4 * X: for palette*/
#define LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h) + 4 * 2)
#define LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h) + 4 * 4)
#define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16)
#define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256)
#define _LV_ZOOM_INV_UPSCALE 5
/**********************
* TYPEDEFS
**********************/
/*Image color format*/
enum {
LV_IMG_CF_UNKNOWN = 0,
LV_IMG_CF_RAW, /**< Contains the file as it is. Needs custom decoder function*/
LV_IMG_CF_RAW_ALPHA, /**< Contains the file as it is. The image has alpha. Needs custom decoder
function*/
LV_IMG_CF_RAW_CHROMA_KEYED, /**< Contains the file as it is. The image is chroma keyed. Needs
custom decoder function*/
LV_IMG_CF_TRUE_COLOR, /**< Color format and depth should match with LV_COLOR settings*/
LV_IMG_CF_TRUE_COLOR_ALPHA, /**< Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels
will be transparent*/
LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (can't be chroma keyed)*/
LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/
LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/
LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/
LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/
LV_IMG_CF_RGB888,
LV_IMG_CF_RGBA8888,
LV_IMG_CF_RGBX8888,
LV_IMG_CF_RGB565,
LV_IMG_CF_RGBA5658,
LV_IMG_CF_RGB565A8,
LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_18, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_19, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_20, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_21, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_22, /**< Reserved for further use.*/
LV_IMG_CF_RESERVED_23, /**< Reserved for further use.*/
LV_IMG_CF_USER_ENCODED_0, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_1, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_2, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_3, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_4, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_5, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_6, /**< User holder encoding format.*/
LV_IMG_CF_USER_ENCODED_7, /**< User holder encoding format.*/
};
typedef uint8_t lv_img_cf_t;
/**
* The first 8 bit is very important to distinguish the different source types.
* For more info see `lv_img_get_src_type()` in lv_img.c
* On big endian systems the order is reversed so cf and always_zero must be at
* the end of the struct.
*/
#if LV_BIG_ENDIAN_SYSTEM
typedef struct {
uint32_t h : 11; /*Height of the image map*/
uint32_t w : 11; /*Width of the image map*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
} lv_img_header_t;
#else
typedef struct {
uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/
uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a
non-printable character*/
uint32_t reserved : 2; /*Reserved to be used later*/
uint32_t w : 11; /*Width of the image map*/
uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;
#endif
/** Image header it is compatible with
* the result from image converter utility*/
typedef struct {
lv_img_header_t header; /**< A header describing the basics of the image*/
uint32_t data_size; /**< Size of the image in bytes*/
const uint8_t * data; /**< Pointer to the data of the image*/
} lv_img_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Allocate an image buffer in RAM
* @param w width of image
* @param h height of image
* @param cf a color format (`LV_IMG_CF_...`)
* @return an allocated image, or NULL on failure
*/
lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Get the color of an image's pixel
* @param dsc an image descriptor
* @param x x coordinate of the point to get
* @param y x coordinate of the point to get
* @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
* Not used in other cases.
* @param safe true: check out of bounds
* @return color of the point
*/
lv_color_t lv_img_buf_get_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color);
/**
* Get the alpha value of an image's pixel
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param safe true: check out of bounds
* @return alpha value of the point
*/
lv_opa_t lv_img_buf_get_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y);
/**
* Set the color of a pixel of an image. The alpha channel won't be affected.
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the point
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* Set the alpha value of a pixel of an image. The color won't be affected
* @param dsc pointer to an image descriptor
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa the desired opacity
* @param safe true: check out of bounds
*/
void lv_img_buf_set_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
/**
* Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param dsc pointer to an image descriptor
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_img_buf_set_palette(const lv_img_dsc_t * dsc, uint8_t id, lv_color_t c);
/**
* Free an allocated image buffer
* @param dsc image buffer to free
*/
void lv_img_buf_free(lv_img_dsc_t * dsc);
/**
* Get the memory consumption of a raw bitmap, given color format and dimensions.
* @param w width
* @param h height
* @param cf color format
* @return size in bytes
*/
uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Get the area of a rectangle if its rotated and scaled
* @param res store the coordinates here
* @param w width of the rectangle to transform
* @param h height of the rectangle to transform
* @param angle angle of rotation
* @param zoom zoom, (256 no zoom)
* @param pivot x,y pivot coordinates of rotation
*/
void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom,
const lv_point_t * pivot);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_BUF_H*/

View File

@@ -0,0 +1,215 @@
/**
* @file lv_img_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../misc/lv_assert.h"
#include "lv_img_cache.h"
#include "lv_img_decoder.h"
#include "lv_draw_img.h"
#include "../hal/lv_hal_tick.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/*Decrement life with this value on every open*/
#define LV_IMG_CACHE_AGING 1
/*Boost life by this factor (multiply time_to_open with this value)*/
#define LV_IMG_CACHE_LIFE_GAIN 1
/*Don't let life to be greater than this limit because it would require a lot of time to
* "die" from very high values*/
#define LV_IMG_CACHE_LIFE_LIMIT 1000
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static uint16_t entry_cnt;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
{
/*Is the image cached?*/
_lv_img_cache_entry_t * cached_src = NULL;
#if LV_IMG_CACHE_DEF_SIZE
if(entry_cnt == 0) {
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
return NULL;
}
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
/*Decrement all lifes. Make the entries older*/
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
cache[i].life -= LV_IMG_CACHE_AGING;
}
}
for(i = 0; i < entry_cnt; i++) {
if(color.full == cache[i].dec_dsc.color.full &&
frame_id == cache[i].dec_dsc.frame_id &&
lv_img_cache_match(src, cache[i].dec_dsc.src)) {
/*If opened increment its life.
*Image difficult to open should live longer to keep avoid frequent their recaching.
*Therefore increase `life` with `time_to_open`*/
cached_src = &cache[i];
cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
LV_LOG_TRACE("image source found in the cache");
break;
}
}
/*The image is not cached then cache it now*/
if(cached_src) return cached_src;
/*Find an entry to reuse. Select the entry with the least life*/
cached_src = &cache[0];
for(i = 1; i < entry_cnt; i++) {
if(cache[i].life < cached_src->life) {
cached_src = &cache[i];
}
}
/*Close the decoder to reuse if it was opened (has a valid source)*/
if(cached_src->dec_dsc.src) {
lv_img_decoder_close(&cached_src->dec_dsc);
LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
}
else {
LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
}
#else
cached_src = &LV_GC_ROOT(_lv_img_cache_single);
#endif
/*Open the image and measure the time to open*/
uint32_t t_start = lv_tick_get();
lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
if(open_res == LV_RES_INV) {
LV_LOG_WARN("Image draw cannot open the image resource");
lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));
cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/
return NULL;
}
cached_src->life = 0;
/*If `time_to_open` was not set in the open function set it here*/
if(cached_src->dec_dsc.time_to_open == 0) {
cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
}
if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
return cached_src;
}
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_entry_cnt)
{
#if LV_IMG_CACHE_DEF_SIZE == 0
LV_UNUSED(new_entry_cnt);
LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMG_CACHE_DEF_SIZE = 0");
#else
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
/*Clean the cache before free it*/
lv_img_cache_invalidate_src(NULL);
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
}
/*Reallocate the cache*/
LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(_lv_img_cache_entry_t) * new_entry_cnt);
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_img_cache_array));
if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
entry_cnt = 0;
return;
}
entry_cnt = new_entry_cnt;
/*Clean the cache*/
lv_memset_00(LV_GC_ROOT(_lv_img_cache_array), entry_cnt * sizeof(_lv_img_cache_entry_t));
#endif
}
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src)
{
LV_UNUSED(src);
#if LV_IMG_CACHE_DEF_SIZE
_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
uint16_t i;
for(i = 0; i < entry_cnt; i++) {
if(src == NULL || lv_img_cache_match(src, cache[i].dec_dsc.src)) {
if(cache[i].dec_dsc.src != NULL) {
lv_img_decoder_close(&cache[i].dec_dsc);
}
lv_memset_00(&cache[i], sizeof(_lv_img_cache_entry_t));
}
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_IMG_CACHE_DEF_SIZE
static bool lv_img_cache_match(const void * src1, const void * src2)
{
lv_img_src_t src_type = lv_img_src_get_type(src1);
if(src_type == LV_IMG_SRC_VARIABLE)
return src1 == src2;
if(src_type != LV_IMG_SRC_FILE)
return false;
if(lv_img_src_get_type(src2) != LV_IMG_SRC_FILE)
return false;
return strcmp(src1, src2) == 0;
}
#endif

View File

@@ -0,0 +1,78 @@
/**
* @file lv_img_cache.h
*
*/
#ifndef LV_IMG_CACHE_H
#define LV_IMG_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* When loading images from the network it can take a long time to download and decode the image.
*
* To avoid repeating this heavy load images can be cached.
*/
typedef struct {
lv_img_decoder_dsc_t dec_dsc; /**< Image information*/
/** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
* Decrement all lifes by one every in every ::lv_img_cache_open.
* If life == 0 the entry can be reused*/
int32_t life;
} _lv_img_cache_entry_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Open an image using the image decoder interface and cache it.
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
* The image is closed if a new image is opened and the new image takes its place in the cache.
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return pointer to the cache entry or NULL if can open the image
*/
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id);
/**
* Set the number of images to be cached.
* More cached images mean more opened image at same time which might mean more memory usage.
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
* @param new_entry_cnt number of image to cache
*/
void lv_img_cache_set_size(uint16_t new_slot_num);
/**
* Invalidate an image source in the cache.
* Useful if the image source is updated therefore it needs to be cached again.
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
*/
void lv_img_cache_invalidate_src(const void * src);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_CACHE_H*/

View File

@@ -0,0 +1,730 @@
/**
* @file lv_img_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img_decoder.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_img.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR
#define CF_BUILT_IN_LAST LV_IMG_CF_RGB565A8
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
lv_color_t * palette;
lv_opa_t * opa;
} lv_img_decoder_built_in_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));
lv_img_decoder_t * decoder;
/*Create a decoder for the built in color format*/
decoder = lv_img_decoder_create();
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) {
LV_LOG_WARN("lv_img_decoder_init: out of memory");
return;
}
lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
}
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. E.g. file name or variable.
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{
lv_memset_00(header, sizeof(lv_img_header_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * d;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
if(d->info_cb) {
res = d->info_cb(d, src, header);
if(res == LV_RES_OK) break;
}
}
return res;
}
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{
lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));
if(src == NULL) return LV_RES_INV;
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
if(img_dsc->data == NULL) return LV_RES_INV;
}
dsc->color = color;
dsc->src_type = src_type;
dsc->frame_id = frame_id;
if(dsc->src_type == LV_IMG_SRC_FILE) {
size_t fnlen = strlen(src);
dsc->src = lv_mem_alloc(fnlen + 1);
LV_ASSERT_MALLOC(dsc->src);
if(dsc->src == NULL) {
LV_LOG_WARN("lv_img_decoder_open: out of memory");
return LV_RES_INV;
}
strcpy((char *)dsc->src, src);
}
else {
dsc->src = src;
}
lv_res_t res = LV_RES_INV;
lv_img_decoder_t * decoder;
_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
/*Info and Open callbacks are required*/
if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;
res = decoder->info_cb(decoder, src, &dsc->header);
if(res != LV_RES_OK) continue;
dsc->decoder = decoder;
res = decoder->open_cb(decoder, dsc);
/*Opened successfully. It is a good decoder for this image source*/
if(res == LV_RES_OK) return res;
/*Prepare for the next loop*/
lv_memset_00(&dsc->header, sizeof(lv_img_header_t));
dsc->error_msg = NULL;
dsc->img_data = NULL;
dsc->user_data = NULL;
dsc->time_to_open = 0;
}
if(dsc->src_type == LV_IMG_SRC_FILE)
lv_mem_free((void *)dsc->src);
return res;
}
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
lv_res_t res = LV_RES_INV;
if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);
return res;
}
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
{
if(dsc->decoder) {
if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_mem_free((void *)dsc->src);
dsc->src = NULL;
}
}
}
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void)
{
lv_img_decoder_t * decoder;
decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
LV_ASSERT_MALLOC(decoder);
if(decoder == NULL) return NULL;
lv_memset_00(decoder, sizeof(lv_img_decoder_t));
return decoder;
}
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder)
{
_lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
lv_mem_free(decoder);
}
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
{
decoder->info_cb = info_cb;
}
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
{
decoder->open_cb = open_cb;
}
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
{
decoder->read_line_cb = read_line_cb;
}
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
{
decoder->close_cb = close_cb;
}
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
}
else if(src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
uint32_t rn;
res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
lv_fs_close(&f);
if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
LV_LOG_WARN("Image get info get read file header");
return LV_RES_INV;
}
}
if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
}
else if(src_type == LV_IMG_SRC_SYMBOL) {
/*The size depend on the font but it is unknown here. It should be handled outside of the
*function*/
header->w = 1;
header->h = 1;
/*Symbols always have transparent parts. Important because of cover check in the draw
*function. The actual value doesn't matter because lv_draw_label will draw it*/
header->cf = LV_IMG_CF_ALPHA_1BIT;
}
else {
LV_LOG_WARN("Image get info found unknown src type");
return LV_RES_INV;
}
return LV_RES_OK;
}
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Support only "*.bin" files*/
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder can't open the file");
return LV_RES_INV;
}
/*If the file was open successfully save the file descriptor*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_fs_close(&f);
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_memcpy_small(&user_data->f, &f, sizeof(f));
}
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*The variables should have valid data*/
if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
return LV_RES_INV;
}
}
lv_img_cf_t cf = dsc->header.cf;
/*Process A8, RGB565A8, need load file to ram after https://github.com/lvgl/lvgl/pull/3337*/
if(cf == LV_IMG_CF_ALPHA_8BIT || cf == LV_IMG_CF_RGB565A8) {
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
return LV_RES_OK;
}
else {
/*If it's a file, read all to memory*/
uint32_t len = dsc->header.w * dsc->header.h;
len *= cf == LV_IMG_CF_RGB565A8 ? 3 : 1;
uint8_t * fs_buf = lv_mem_alloc(len);
if(fs_buf == NULL) return LV_RES_INV;
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_res_t res = lv_fs_read(&user_data->f, fs_buf, len, NULL);
if(res != LV_FS_RES_OK) {
lv_mem_free(fs_buf);
return LV_RES_INV;
}
dsc->img_data = fs_buf;
return LV_RES_OK;
}
}
/*Process true color formats*/
else if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
return LV_RES_OK;
}
else {
/*If it's a file it need to be read line by line later*/
return LV_RES_OK;
}
}
/*Process indexed images. Build a palette*/
else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
cf == LV_IMG_CF_INDEXED_8BIT) {
uint8_t px_size = lv_img_cf_get_px_size(cf);
uint32_t palette_size = 1 << px_size;
/*Allocate the palette*/
if(dsc->user_data == NULL) {
dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
return LV_RES_INV;
}
lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
user_data->palette = lv_mem_alloc(palette_size * sizeof(lv_color_t));
LV_ASSERT_MALLOC(user_data->palette);
user_data->opa = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
LV_ASSERT_MALLOC(user_data->opa);
if(user_data->palette == NULL || user_data->opa == NULL) {
LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
lv_img_decoder_built_in_close(decoder, dsc);
return LV_RES_INV;
}
if(dsc->src_type == LV_IMG_SRC_FILE) {
/*Read the palette from file*/
lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
lv_color32_t cur_color;
uint32_t i;
for(i = 0; i < palette_size; i++) {
lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
user_data->opa[i] = cur_color.ch.alpha;
}
}
else {
/*The palette begins in the beginning of the image data. Just point to it.*/
lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;
uint32_t i;
for(i = 0; i < palette_size; i++) {
user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
user_data->opa[i] = palette_p[i].ch.alpha;
}
}
return LV_RES_OK;
}
/*Alpha indexed images.*/
else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT) {
return LV_RES_OK; /*Nothing to process*/
}
/*Unknown format. Can't decode it.*/
else {
/*Free the potentially allocated memories*/
lv_img_decoder_built_in_close(decoder, dsc);
LV_LOG_WARN("Image decoder open: unknown color format");
return LV_RES_INV;
}
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
LV_UNUSED(decoder); /*Unused*/
lv_res_t res = LV_RES_INV;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
/*For TRUE_COLOR images read line required only for files.
*For variables the image data was returned in `open`*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
}
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
}
else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
}
else {
LV_LOG_WARN("Built-in image decoder read not supports the color format");
return LV_RES_INV;
}
return res;
}
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
if(user_data) {
if(dsc->src_type == LV_IMG_SRC_FILE) {
lv_fs_close(&user_data->f);
}
if(user_data->palette) lv_mem_free(user_data->palette);
if(user_data->opa) lv_mem_free(user_data->opa);
lv_mem_free(user_data);
dsc->user_data = NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
lv_fs_res_t res;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
pos += 4; /*Skip the header*/
res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder seek failed");
return LV_RES_INV;
}
uint32_t btr = len * (px_size >> 3);
uint32_t br = 0;
res = lv_fs_read(&user_data->f, buf, btr, &br);
if(res != LV_FS_RES_OK || btr != br) {
LV_LOG_WARN("Built-in image decoder read failed");
return LV_RES_INV;
}
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
};
/*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
lv_color_t bg_color = dsc->color;
lv_coord_t i;
for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
}
const lv_opa_t * opa_table = NULL;
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
uint32_t ofs = 0;
int8_t pos = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_ALPHA_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
pos = 7 - (x & 0x7);
opa_table = alpha1_opa_table;
break;
case LV_IMG_CF_ALPHA_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
pos = 6 - (x & 0x3) * 2;
opa_table = alpha2_opa_table;
break;
case LV_IMG_CF_ALPHA_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
pos = 4 - (x & 0x1) * 4;
opa_table = alpha4_opa_table;
break;
case LV_IMG_CF_ALPHA_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t * buf)
{
uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
int8_t pos = 0;
uint32_t ofs = 0;
switch(dsc->header.cf) {
case LV_IMG_CF_INDEXED_1BIT:
w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
ofs += w * y + (x >> 3); /*First pixel*/
ofs += 8; /*Skip the palette*/
pos = 7 - (x & 0x7);
break;
case LV_IMG_CF_INDEXED_2BIT:
w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
ofs += w * y + (x >> 2); /*First pixel*/
ofs += 16; /*Skip the palette*/
pos = 6 - (x & 0x3) * 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
ofs += w * y + (x >> 1); /*First pixel*/
ofs += 64; /*Skip the palette*/
pos = 4 - (x & 0x1) * 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
ofs += 1024; /*Skip the palette*/
pos = 0;
break;
}
lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
uint8_t * fs_buf = lv_mem_buf_get(w);
if(fs_buf == NULL) return LV_RES_INV;
const uint8_t * data_tmp = NULL;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
data_tmp = img_dsc->data + ofs;
}
else {
lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
lv_fs_read(&user_data->f, fs_buf, w, NULL);
data_tmp = fs_buf;
}
lv_coord_t i;
for(i = 0; i < len; i++) {
uint8_t val_act = (*data_tmp >> pos) & mask;
lv_color_t color = user_data->palette[val_act];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
lv_mem_buf_release(fs_buf);
return LV_RES_OK;
}

View File

@@ -0,0 +1,274 @@
/**
* @file lv_img_decoder.h
*
*/
#ifndef LV_IMG_DECODER_H
#define LV_IMG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include <stdint.h>
#include "lv_img_buf.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Source of image.*/
enum {
LV_IMG_SRC_VARIABLE, /** Binary/C variable*/
LV_IMG_SRC_FILE, /** File in filesystem*/
LV_IMG_SRC_SYMBOL, /** Symbol (@ref lv_symbol_def.h)*/
LV_IMG_SRC_UNKNOWN, /** Unknown source*/
};
typedef uint8_t lv_img_src_t;
/*Decoder function definitions*/
struct _lv_img_decoder_dsc_t;
struct _lv_img_decoder_t;
/**
* Get info from an image and store in the `header`
* @param src the image source. Can be a pointer to a C array or a file name (Use
* `lv_img_src_get_type` to determine the type)
* @param header store the info here
* @return LV_RES_OK: info written correctly; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_info_f_t)(struct _lv_img_decoder_t * decoder, const void * src,
lv_img_header_t * header);
/**
* Open an image for decoding. Prepare it as it is required to read it later
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
*/
typedef lv_res_t (*lv_img_decoder_open_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_read_line_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
typedef void (*lv_img_decoder_close_f_t)(struct _lv_img_decoder_t * decoder, struct _lv_img_decoder_dsc_t * dsc);
typedef struct _lv_img_decoder_t {
lv_img_decoder_info_f_t info_cb;
lv_img_decoder_open_f_t open_cb;
lv_img_decoder_read_line_f_t read_line_cb;
lv_img_decoder_close_f_t close_cb;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_img_decoder_t;
/**Describe an image decoding session. Stores data about the decoding*/
typedef struct _lv_img_decoder_dsc_t {
/**The decoder which was able to open the image source*/
lv_img_decoder_t * decoder;
/**The image source. A file path like "S:my_img.png" or pointer to an `lv_img_dsc_t` variable*/
const void * src;
/**Color to draw the image. USed when the image has alpha channel only*/
lv_color_t color;
/**Frame of the image, using with animated images*/
int32_t frame_id;
/**Type of the source: file or variable. Can be set in `open` function if required*/
lv_img_src_t src_type;
/**Info about the opened image: color format, size, etc. MUST be set in `open` function*/
lv_img_header_t header;
/** Pointer to a buffer where the image's data (pixels) are stored in a decoded, plain format.
* MUST be set in `open` function*/
const uint8_t * img_data;
/** How much time did it take to open the image. [ms]
* If not set `lv_img_cache` will measure and set the time to open*/
uint32_t time_to_open;
/**A text to display instead of the image when the image can't be opened.
* Can be set in `open` function or set NULL.*/
const char * error_msg;
/**Store any custom data here is required*/
void * user_data;
} lv_img_decoder_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the image decoder module
*/
void _lv_img_decoder_init(void);
/**
* Get information about an image.
* Try the created image decoder one by one. Once one is able to get info that info will be used.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register()`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param header the image info will be stored here
* @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
*/
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header);
/**
* Open an image.
* Try the created image decoders one by one. Once one is able to open the image that decoder is saved in `dsc`
* @param dsc describes a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable.
* @param src the image source. Can be
* 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_drv_register())`)
* 2) Variable: Pointer to an `lv_img_dsc_t` variable
* 3) Symbol: E.g. `LV_SYMBOL_OK`
* @param color The color of the image with `LV_IMG_CF_ALPHA_...`
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
* @return LV_RES_OK: opened the image. `dsc->img_data` and `dsc->header` are set.
* LV_RES_INV: none of the registered image decoders were able to open the image.
*/
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id);
/**
* Read a line from an opened image
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
* @param x start X coordinate (from left)
* @param y start Y coordinate (from top)
* @param len number of pixels to read
* @param buf store the data here
* @return LV_RES_OK: success; LV_RES_INV: an error occurred
*/
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len,
uint8_t * buf);
/**
* Close a decoding session
* @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
*/
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc);
/**
* Create a new image decoder
* @return pointer to the new image decoder
*/
lv_img_decoder_t * lv_img_decoder_create(void);
/**
* Delete an image decoder
* @param decoder pointer to an image decoder
*/
void lv_img_decoder_delete(lv_img_decoder_t * decoder);
/**
* Set a callback to get information about the image
* @param decoder pointer to an image decoder
* @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
*/
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb);
/**
* Set a callback to open an image
* @param decoder pointer to an image decoder
* @param open_cb a function to open an image
*/
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb);
/**
* Set a callback to a decoded line of an image
* @param decoder pointer to an image decoder
* @param read_line_cb a function to read a line of an image
*/
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb);
/**
* Set a callback to close a decoding session. E.g. close files and free other resources.
* @param decoder pointer to an image decoder
* @param close_cb a function to close a decoding session
*/
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb);
/**
* Get info about a built-in image
* @param decoder the decoder where this function belongs
* @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
* @param header store the image data here
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
/**
* Open a built in image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
* @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
*/
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
lv_res_t lv_img_decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_DECODER_H*/

View File

@@ -0,0 +1,7 @@
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp"
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk

View File

@@ -0,0 +1,9 @@
CSRCS += lv_draw_pxp.c
CSRCS += lv_draw_pxp_blend.c
CSRCS += lv_gpu_nxp_pxp_osa.c
CSRCS += lv_gpu_nxp_pxp.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp"

View File

@@ -0,0 +1,275 @@
/**
* @file lv_draw_pxp.c
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp.h"
#if LV_USE_GPU_NXP_PXP
#include "lv_draw_pxp_blend.h"
#if LV_COLOR_DEPTH != 32
#include "../../../core/lv_refr.h"
#endif
/*********************
* DEFINES
*********************/
/* Minimum area (in pixels) for PXP blit/fill processing. */
#ifndef LV_GPU_NXP_PXP_SIZE_LIMIT
#define LV_GPU_NXP_PXP_SIZE_LIMIT 5000
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_pxp_wait_for_finish(lv_draw_ctx_t * draw_ctx);
static void lv_draw_pxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
static void lv_draw_pxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf);
static void lv_draw_pxp_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_pxp_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_pxp_ctx_t * pxp_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
pxp_draw_ctx->base_draw.draw_img_decoded = lv_draw_pxp_img_decoded;
pxp_draw_ctx->blend = lv_draw_pxp_blend;
pxp_draw_ctx->base_draw.wait_for_finish = lv_draw_pxp_wait_for_finish;
pxp_draw_ctx->base_draw.buffer_copy = lv_draw_pxp_buffer_copy;
}
void lv_draw_pxp_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_deinit_ctx(drv, draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* During rendering, LVGL might initializes new draw_ctxs and start drawing into
* a separate buffer (called layer). If the content to be rendered has "holes",
* e.g. rounded corner, LVGL temporarily sets the disp_drv.screen_transp flag.
* It means the renderers should draw into an ARGB buffer.
* With 32 bit color depth it's not a big problem but with 16 bit color depth
* the target pixel format is ARGB8565 which is not supported by the GPU.
* In this case, the PXP callbacks should fallback to SW rendering.
*/
static inline bool need_argb8565_support()
{
#if LV_COLOR_DEPTH != 32
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->screen_transp == 1)
return true;
#endif
return false;
}
static void lv_draw_pxp_wait_for_finish(lv_draw_ctx_t * draw_ctx)
{
lv_gpu_nxp_pxp_wait();
lv_draw_sw_wait_for_finish(draw_ctx);
}
static void lv_draw_pxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(need_argb8565_support()) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
return;
}
lv_area_t blend_area;
/*Let's get the blend area which is the intersection of the area to draw and the clip area*/
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area))
return; /*Fully clipped, nothing to do*/
/*Make the blend area relative to the buffer*/
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
if(dsc->mask_buf != NULL || dsc->blend_mode != LV_BLEND_MODE_NORMAL ||
lv_area_get_size(&blend_area) < LV_GPU_NXP_PXP_SIZE_LIMIT) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
return;
}
/*Fill/Blend only non masked, normal blended*/
lv_color_t * dest_buf = draw_ctx->buf;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
const lv_color_t * src_buf = dsc->src_buf;
if(src_buf == NULL) {
lv_gpu_nxp_pxp_fill(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa);
}
else {
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (dsc->blend_area->x1 - draw_ctx->buf_area->x1);
src_area.y1 = blend_area.y1 - (dsc->blend_area->y1 - draw_ctx->buf_area->y1);
src_area.x2 = src_area.x1 + lv_area_get_width(dsc->blend_area) - 1;
src_area.y2 = src_area.y1 + lv_area_get_height(dsc->blend_area) - 1;
lv_coord_t src_stride = lv_area_get_width(dsc->blend_area);
lv_gpu_nxp_pxp_blit(dest_buf, &blend_area, dest_stride, src_buf, &src_area, src_stride,
dsc->opa, LV_DISP_ROT_NONE);
}
}
static void lv_draw_pxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf)
{
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(need_argb8565_support()) {
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
return;
}
const lv_color_t * src_buf = (const lv_color_t *)map_p;
if(!src_buf) {
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
return;
}
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
bool has_scale = (dsc->zoom != LV_IMG_ZOOM_NONE);
bool has_rotation = (dsc->angle != 0);
bool unsup_rotation = false;
lv_area_t blend_area;
if(has_rotation)
lv_area_copy(&blend_area, &rel_coords);
else if(!_lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
bool has_mask = lv_draw_mask_is_any(&blend_area);
lv_coord_t src_width = lv_area_get_width(coords);
lv_coord_t src_height = lv_area_get_height(coords);
if(has_rotation) {
/*
* PXP can only rotate at 90x angles.
*/
if(dsc->angle % 900) {
PXP_LOG_TRACE("Rotation angle %d is not supported. PXP can rotate only 90x angle.", dsc->angle);
unsup_rotation = true;
}
/*
* PXP is set to process 16x16 blocks to optimize the system for memory
* bandwidth and image processing time.
* The output engine essentially truncates any output pixels after the
* desired number of pixels has been written.
* When rotating a source image and the output is not divisible by the block
* size, the incorrect pixels could be truncated and the final output image
* can look shifted.
*/
if(src_width % 16 || src_height % 16) {
PXP_LOG_TRACE("Rotation is not supported for image w/o alignment to block size 16x16.");
unsup_rotation = true;
}
}
if(has_mask || has_scale || unsup_rotation || lv_area_get_size(&blend_area) < LV_GPU_NXP_PXP_SIZE_LIMIT
#if LV_COLOR_DEPTH != 32
|| lv_img_cf_has_alpha(cf)
#endif
) {
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
return;
}
lv_color_t * dest_buf = draw_ctx->buf;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (coords->x1 - draw_ctx->buf_area->x1);
src_area.y1 = blend_area.y1 - (coords->y1 - draw_ctx->buf_area->y1);
src_area.x2 = src_area.x1 + src_width - 1;
src_area.y2 = src_area.y1 + src_height - 1;
lv_coord_t src_stride = lv_area_get_width(coords);
lv_gpu_nxp_pxp_blit_transform(dest_buf, &blend_area, dest_stride, src_buf, &src_area, src_stride,
dsc, cf);
}
static void lv_draw_pxp_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
LV_UNUSED(draw_ctx);
if(lv_area_get_size(dest_area) < LV_GPU_NXP_PXP_SIZE_LIMIT) {
lv_draw_sw_buffer_copy(draw_ctx, dest_buf, dest_stride, dest_area, src_buf, src_stride, src_area);
return;
}
lv_gpu_nxp_pxp_buffer_copy(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride);
}
#endif /*LV_USE_GPU_NXP_PXP*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_draw_pxp.h
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_PXP_H
#define LV_DRAW_PXP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP
#include "../../sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_pxp_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_pxp_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_pxp_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_PXP_H*/

View File

@@ -0,0 +1,573 @@
/**
* @file lv_draw_pxp_blend.c
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_pxp_blend.h"
#if LV_USE_GPU_NXP_PXP
#include "lvgl_support.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
#error Color swap not implemented. Disable LV_COLOR_16_SWAP feature.
#endif
#if LV_COLOR_DEPTH == 16
#define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565
#define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565
#define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565
#define PXP_TEMP_BUF_SIZE LCD_WIDTH * LCD_HEIGHT * 2U
#elif LV_COLOR_DEPTH == 32
#define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatARGB8888
#define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatARGB8888
#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \
(!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3))
#define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatARGB8888
#else
#define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB888
#endif
#define PXP_TEMP_BUF_SIZE LCD_WIDTH * LCD_HEIGHT * 4U
#elif
#error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32.
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static LV_ATTRIBUTE_MEM_ALIGN uint8_t temp_buf[PXP_TEMP_BUF_SIZE];
/**
* BLock Image Transfer - copy rectangular image from src buffer to dst buffer
* with combination of transformation (rotation, scale, recolor) and opacity, alpha channel
* or color keying. This requires two steps. First step is used for transformation into
* a temporary buffer and the second one will handle the color format or opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] dsc Image descriptor
* @param[in] cf Color format
*/
static void lv_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
/**
* BLock Image Transfer - copy rectangular image from src buffer to dst buffer
* with transformation and full opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] dsc Image descriptor
* @param[in] cf Color format
*/
static void lv_pxp_blit_cover(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
/**
* BLock Image Transfer - copy rectangular image from src buffer to dst buffer
* without transformation but handling color format or opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] dsc Image descriptor
* @param[in] cf Color format
*/
static void lv_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
lv_color_t color, lv_opa_t opa)
{
lv_coord_t dest_w = lv_area_get_width(dest_area);
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_gpu_nxp_pxp_reset();
/*OUT buffer configure*/
pxp_output_buffer_config_t outputConfig = {
.pixelFormat = PXP_OUT_PIXEL_FORMAT,
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.buffer1Addr = (uint32_t)NULL,
.pitchBytes = dest_stride * sizeof(lv_color_t),
.width = dest_w,
.height = dest_h
};
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig);
if(opa >= (lv_opa_t)LV_OPA_MAX) {
/*Simple color fill without opacity - AS disabled*/
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
}
else {
/*Fill with opacity - AS used as source (same as OUT)*/
pxp_as_buffer_config_t asBufferConfig = {
.pixelFormat = PXP_AS_PIXEL_FORMAT,
.bufferAddr = (uint32_t)outputConfig.buffer0Addr,
.pitchBytes = outputConfig.pitchBytes
};
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
}
/*Disable PS, use as color generator*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color));
/**
* Configure Porter-Duff blending - src settings are unused for fill without opacity (opa = 0xff).
*
* Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
* srcFactorMode is actually applied on PS alpha value
* dstFactorMode is actually applied on AS alpha value
*/
pxp_porter_duff_config_t pdConfig = {
.enable = 1,
.dstColorMode = kPXP_PorterDuffColorNoAlpha,
.srcColorMode = kPXP_PorterDuffColorNoAlpha,
.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
.srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
.dstFactorMode = kPXP_PorterDuffFactorStraight,
.srcFactorMode = (opa >= (lv_opa_t)LV_OPA_MAX) ? kPXP_PorterDuffFactorStraight : kPXP_PorterDuffFactorInversed,
.dstGlobalAlpha = opa,
.srcGlobalAlpha = opa,
.dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
.srcAlphaMode = kPXP_PorterDuffAlphaStraight /*don't care*/
};
PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
lv_gpu_nxp_pxp_run();
}
void lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa, lv_disp_rot_t angle)
{
lv_coord_t dest_w = lv_area_get_width(dest_area);
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_coord_t src_w = lv_area_get_width(src_area);
lv_coord_t src_h = lv_area_get_height(src_area);
lv_gpu_nxp_pxp_reset();
/* convert rotation angle */
pxp_rotate_degree_t pxp_rot;
switch(angle) {
case LV_DISP_ROT_NONE:
pxp_rot = kPXP_Rotate0;
break;
case LV_DISP_ROT_90:
pxp_rot = kPXP_Rotate90;
break;
case LV_DISP_ROT_180:
pxp_rot = kPXP_Rotate180;
break;
case LV_DISP_ROT_270:
pxp_rot = kPXP_Rotate270;
break;
default:
pxp_rot = kPXP_Rotate0;
break;
}
PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable);
pxp_as_blend_config_t asBlendConfig = {
.alpha = opa,
.invertAlpha = false,
.alphaMode = kPXP_AlphaRop,
.ropMode = kPXP_RopMergeAs
};
if(opa >= (lv_opa_t)LV_OPA_MAX) {
/*Simple blit, no effect - Disable PS buffer*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
}
else {
pxp_ps_buffer_config_t psBufferConfig = {
.pixelFormat = PXP_PS_PIXEL_FORMAT,
.swapByte = false,
.bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.bufferAddrU = 0U,
.bufferAddrV = 0U,
.pitchBytes = dest_stride * sizeof(lv_color_t)
};
asBlendConfig.alphaMode = kPXP_AlphaOverride;
PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
}
/*AS buffer - source image*/
pxp_as_buffer_config_t asBufferConfig = {
.pixelFormat = PXP_AS_PIXEL_FORMAT,
.bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
.pitchBytes = src_stride * sizeof(lv_color_t)
};
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, false);
/*Output buffer.*/
pxp_output_buffer_config_t outputBufferConfig = {
.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.buffer1Addr = (uint32_t)0U,
.pitchBytes = dest_stride * sizeof(lv_color_t),
.width = dest_w,
.height = dest_h
};
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
lv_gpu_nxp_pxp_run();
}
void lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
{
bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
bool has_rotation = (dsc->angle != 0);
if(has_recolor || has_rotation) {
if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_has_alpha(cf) && !lv_img_cf_is_chroma_keyed(cf)) {
lv_pxp_blit_cover(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
return;
}
else {
/*Recolor and/or rotation with alpha or opacity is done in two steps.*/
lv_pxp_blit_opa(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
return;
}
}
lv_pxp_blit_cf(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
}
void lv_gpu_nxp_pxp_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride)
{
lv_coord_t src_width = lv_area_get_width(src_area);
lv_coord_t src_height = lv_area_get_height(src_area);
lv_gpu_nxp_pxp_reset();
const pxp_pic_copy_config_t picCopyConfig = {
.srcPicBaseAddr = (uint32_t)src_buf,
.srcPitchBytes = src_stride * sizeof(lv_color_t),
.srcOffsetX = src_area->x1,
.srcOffsetY = src_area->y1,
.destPicBaseAddr = (uint32_t)dest_buf,
.destPitchBytes = dest_stride * sizeof(lv_color_t),
.destOffsetX = dest_area->x1,
.destOffsetY = dest_area->y1,
.width = src_width,
.height = src_height,
.pixelFormat = PXP_AS_PIXEL_FORMAT
};
PXP_StartPictureCopy(LV_GPU_NXP_PXP_ID, &picCopyConfig);
lv_gpu_nxp_pxp_wait();
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
{
lv_area_t temp_area;
lv_area_copy(&temp_area, dest_area);
lv_coord_t temp_stride = dest_stride;
lv_coord_t temp_w = lv_area_get_width(&temp_area);
lv_coord_t temp_h = lv_area_get_height(&temp_area);
/*Step 1: Transform with full opacity to temporary buffer*/
lv_pxp_blit_cover((lv_color_t *)temp_buf, &temp_area, temp_stride, src_buf, src_area, src_stride, dsc, cf);
/*Switch width and height if angle requires it*/
if(dsc->angle == 900 || dsc->angle == 2700) {
temp_area.x2 = temp_area.x1 + temp_h - 1;
temp_area.y2 = temp_area.y1 + temp_w - 1;
}
/*Step 2: Blit temporary result with required opacity to output*/
lv_pxp_blit_cf(dest_buf, &temp_area, dest_stride, (lv_color_t *)temp_buf, &temp_area, temp_stride, dsc, cf);
}
static void lv_pxp_blit_cover(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
{
lv_coord_t dest_w = lv_area_get_width(dest_area);
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_coord_t src_w = lv_area_get_width(src_area);
lv_coord_t src_h = lv_area_get_height(src_area);
bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
bool has_rotation = (dsc->angle != 0);
lv_point_t pivot = dsc->pivot;
lv_coord_t piv_offset_x;
lv_coord_t piv_offset_y;
lv_gpu_nxp_pxp_reset();
if(has_rotation) {
/*Convert rotation angle and calculate offsets caused by pivot*/
pxp_rotate_degree_t pxp_angle;
switch(dsc->angle) {
case 0:
pxp_angle = kPXP_Rotate0;
piv_offset_x = 0;
piv_offset_y = 0;
break;
case 900:
piv_offset_x = pivot.x + pivot.y - dest_h;
piv_offset_y = pivot.y - pivot.x;
pxp_angle = kPXP_Rotate90;
break;
case 1800:
piv_offset_x = 2 * pivot.x - dest_w;
piv_offset_y = 2 * pivot.y - dest_h;
pxp_angle = kPXP_Rotate180;
break;
case 2700:
piv_offset_x = pivot.x - pivot.y;
piv_offset_y = pivot.x + pivot.y - dest_w;
pxp_angle = kPXP_Rotate270;
break;
default:
piv_offset_x = 0;
piv_offset_y = 0;
pxp_angle = kPXP_Rotate0;
}
PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_angle, kPXP_FlipDisable);
lv_area_move(dest_area, piv_offset_x, piv_offset_y);
}
/*AS buffer - source image*/
pxp_as_buffer_config_t asBufferConfig = {
.pixelFormat = PXP_AS_PIXEL_FORMAT,
.bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
.pitchBytes = src_stride * sizeof(lv_color_t)
};
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
/*Disable PS buffer*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
if(has_recolor)
/*Use as color generator*/
PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(dsc->recolor));
/*Output buffer*/
pxp_output_buffer_config_t outputBufferConfig = {
.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.buffer1Addr = (uint32_t)0U,
.pitchBytes = dest_stride * sizeof(lv_color_t),
.width = dest_w,
.height = dest_h
};
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
if(has_recolor || lv_img_cf_has_alpha(cf)) {
/**
* Configure Porter-Duff blending.
*
* Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
* srcFactorMode is actually applied on PS alpha value
* dstFactorMode is actually applied on AS alpha value
*/
pxp_porter_duff_config_t pdConfig = {
.enable = 1,
.dstColorMode = kPXP_PorterDuffColorWithAlpha,
.srcColorMode = kPXP_PorterDuffColorNoAlpha,
.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
.srcGlobalAlphaMode = lv_img_cf_has_alpha(cf) ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha,
.dstFactorMode = kPXP_PorterDuffFactorStraight,
.srcFactorMode = kPXP_PorterDuffFactorInversed,
.dstGlobalAlpha = has_recolor ? dsc->recolor_opa : 0x00,
.srcGlobalAlpha = 0xff,
.dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
.srcAlphaMode = kPXP_PorterDuffAlphaStraight
};
PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
}
lv_gpu_nxp_pxp_run();
}
static void lv_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
{
lv_coord_t dest_w = lv_area_get_width(dest_area);
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_coord_t src_w = lv_area_get_width(src_area);
lv_coord_t src_h = lv_area_get_height(src_area);
lv_gpu_nxp_pxp_reset();
pxp_as_blend_config_t asBlendConfig = {
.alpha = dsc->opa,
.invertAlpha = false,
.alphaMode = kPXP_AlphaRop,
.ropMode = kPXP_RopMergeAs
};
if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_is_chroma_keyed(cf) && !lv_img_cf_has_alpha(cf)) {
/*Simple blit, no effect - Disable PS buffer*/
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
}
else {
/*PS must be enabled to fetch background pixels.
PS and OUT buffers are the same, blend will be done in-place*/
pxp_ps_buffer_config_t psBufferConfig = {
.pixelFormat = PXP_PS_PIXEL_FORMAT,
.swapByte = false,
.bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.bufferAddrU = 0U,
.bufferAddrV = 0U,
.pitchBytes = dest_stride * sizeof(lv_color_t)
};
if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) {
asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaEmbedded : kPXP_AlphaOverride;
}
else {
asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaMultiply : kPXP_AlphaOverride;
}
PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
}
/*AS buffer - source image*/
pxp_as_buffer_config_t asBufferConfig = {
.pixelFormat = PXP_AS_PIXEL_FORMAT,
.bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
.pitchBytes = src_stride * sizeof(lv_color_t)
};
PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
if(lv_img_cf_is_chroma_keyed(cf)) {
lv_color_t colorKeyLow = LV_COLOR_CHROMA_KEY;
lv_color_t colorKeyHigh = LV_COLOR_CHROMA_KEY;
bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
if(has_recolor) {
/* New color key after recoloring */
lv_color_t colorKey = lv_color_mix(dsc->recolor, LV_COLOR_CHROMA_KEY, dsc->recolor_opa);
LV_COLOR_SET_R(colorKeyLow, colorKey.ch.red != 0 ? colorKey.ch.red - 1 : 0);
LV_COLOR_SET_G(colorKeyLow, colorKey.ch.green != 0 ? colorKey.ch.green - 1 : 0);
LV_COLOR_SET_B(colorKeyLow, colorKey.ch.blue != 0 ? colorKey.ch.blue - 1 : 0);
#if LV_COLOR_DEPTH == 16
LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0x1f ? colorKey.ch.red + 1 : 0x1f);
LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0x3f ? colorKey.ch.green + 1 : 0x3f);
LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0x1f ? colorKey.ch.blue + 1 : 0x1f);
#else /*LV_COLOR_DEPTH == 32*/
LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0xff ? colorKey.ch.red + 1 : 0xff);
LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0xff ? colorKey.ch.green + 1 : 0xff);
LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0xff ? colorKey.ch.blue + 1 : 0xff);
#endif
}
PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_color_to32(colorKeyLow),
lv_color_to32(colorKeyHigh));
}
PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_img_cf_is_chroma_keyed(cf));
/*Output buffer.*/
pxp_output_buffer_config_t outputBufferConfig = {
.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
.interlacedMode = kPXP_OutputProgressive,
.buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
.buffer1Addr = (uint32_t)0U,
.pitchBytes = dest_stride * sizeof(lv_color_t),
.width = dest_w,
.height = dest_h
};
PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
lv_gpu_nxp_pxp_run();
}
#endif /*LV_USE_GPU_NXP_PXP*/

View File

@@ -0,0 +1,130 @@
/**
* @file lv_draw_pxp_blend.h
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_PXP_BLEND_H
#define LV_DRAW_PXP_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP
#include "lv_gpu_nxp_pxp.h"
#include "../../sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Fill area, with optional opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] color Color
* @param[in] opa Opacity
*/
void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
lv_color_t color, lv_opa_t opa);
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects.
* By default, image is copied directly, with optional opacity. This function can also
* rotate the display output buffer to a specified angle (90x step).
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] opa Opacity
* @param[in] angle Display rotation angle (90x)
*/
void lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa, lv_disp_rot_t angle);
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf with transformation.
*
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] dsc Image descriptor
* @param[in] cf Color format
*/
void lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf, no transformation or blending.
*
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
*/
void lv_gpu_nxp_pxp_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_PXP_BLEND_H*/

View File

@@ -0,0 +1,138 @@
/**
* @file lv_gpu_nxp_pxp.c
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_pxp.h"
#if LV_USE_GPU_NXP_PXP
#include "lv_gpu_nxp_pxp_osa.h"
#include "../../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Clean and invalidate cache.
*/
static inline void invalidate_cache(void);
/**********************
* STATIC VARIABLES
**********************/
static lv_nxp_pxp_cfg_t * pxp_cfg;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_gpu_nxp_pxp_init(void)
{
#if LV_USE_GPU_NXP_PXP_AUTO_INIT
pxp_cfg = lv_gpu_nxp_pxp_get_cfg();
#endif
if(!pxp_cfg || !pxp_cfg->pxp_interrupt_deinit || !pxp_cfg->pxp_interrupt_init ||
!pxp_cfg->pxp_run || !pxp_cfg->pxp_wait)
PXP_RETURN_INV("PXP configuration error.");
PXP_Init(LV_GPU_NXP_PXP_ID);
PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/
PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
if(pxp_cfg->pxp_interrupt_init() != LV_RES_OK) {
PXP_DisableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
PXP_Deinit(LV_GPU_NXP_PXP_ID);
PXP_RETURN_INV("PXP interrupt init failed.");
}
return LV_RES_OK;
}
void lv_gpu_nxp_pxp_deinit(void)
{
pxp_cfg->pxp_interrupt_deinit();
PXP_DisableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
PXP_Deinit(LV_GPU_NXP_PXP_ID);
}
void lv_gpu_nxp_pxp_reset(void)
{
/* Wait for previous command to complete before resetting the registers. */
lv_gpu_nxp_pxp_wait();
PXP_ResetControl(LV_GPU_NXP_PXP_ID);
PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/
PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/
}
void lv_gpu_nxp_pxp_run(void)
{
invalidate_cache();
pxp_cfg->pxp_run();
}
void lv_gpu_nxp_pxp_wait(void)
{
pxp_cfg->pxp_wait();
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline void invalidate_cache(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->clean_dcache_cb)
disp->driver->clean_dcache_cb(disp->driver);
}
#endif /*LV_USE_GPU_NXP_PXP*/

View File

@@ -0,0 +1,166 @@
/**
* @file lv_gpu_nxp_pxp.h
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_GPU_NXP_PXP_H
#define LV_GPU_NXP_PXP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP
#include "fsl_cache.h"
#include "fsl_pxp.h"
#include "../../../misc/lv_log.h"
/*********************
* DEFINES
*********************/
/** PXP module instance to use*/
#define LV_GPU_NXP_PXP_ID PXP
/** PXP interrupt line ID*/
#define LV_GPU_NXP_PXP_IRQ_ID PXP_IRQn
#ifndef LV_GPU_NXP_PXP_LOG_ERRORS
/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/
#define LV_GPU_NXP_PXP_LOG_ERRORS 1
#endif
#ifndef LV_GPU_NXP_PXP_LOG_TRACES
/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/
#define LV_GPU_NXP_PXP_LOG_TRACES 0
#endif
/**********************
* TYPEDEFS
**********************/
/**
* NXP PXP device configuration - call-backs used for
* interrupt init/wait/deinit.
*/
typedef struct {
/** Callback for PXP interrupt initialization*/
lv_res_t (*pxp_interrupt_init)(void);
/** Callback for PXP interrupt de-initialization*/
void (*pxp_interrupt_deinit)(void);
/** Callback for PXP start*/
void (*pxp_run)(void);
/** Callback for waiting of PXP completion*/
void (*pxp_wait)(void);
} lv_nxp_pxp_cfg_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Reset and initialize PXP device. This function should be called as a part
* of display init sequence.
*
* @retval LV_RES_OK PXP init completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_pxp_init(void);
/**
* Disable PXP device. Should be called during display deinit sequence.
*/
void lv_gpu_nxp_pxp_deinit(void);
/**
* Reset PXP device.
*/
void lv_gpu_nxp_pxp_reset(void);
/**
* Clear cache and start PXP.
*/
void lv_gpu_nxp_pxp_run(void);
/**
* Wait for PXP completion.
*/
void lv_gpu_nxp_pxp_wait(void);
/**********************
* MACROS
**********************/
#define PXP_COND_STOP(cond, txt) \
do { \
if (cond) { \
LV_LOG_ERROR("%s. STOP!", txt); \
for ( ; ; ); \
} \
} while(0)
#if LV_GPU_NXP_PXP_LOG_ERRORS
#define PXP_RETURN_INV(fmt, ...) \
do { \
LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
return LV_RES_INV; \
} while (0)
#else
#define PXP_RETURN_INV(fmt, ...) \
do { \
return LV_RES_INV; \
}while(0)
#endif /*LV_GPU_NXP_PXP_LOG_ERRORS*/
#if LV_GPU_NXP_PXP_LOG_TRACES
#define PXP_LOG_TRACE(fmt, ...) \
do { \
LV_LOG(fmt, ##__VA_ARGS__); \
} while (0)
#else
#define PXP_LOG_TRACE(fmt, ...) \
do { \
} while (0)
#endif /*LV_GPU_NXP_PXP_LOG_TRACES*/
#endif /*LV_USE_GPU_NXP_PXP*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_NXP_PXP_H*/

View File

@@ -0,0 +1,180 @@
/**
* @file lv_gpu_nxp_pxp_osa.c
*
*/
/**
* MIT License
*
* Copyright 2020, 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_nxp_pxp_osa.h"
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#include "../../../misc/lv_log.h"
#include "fsl_pxp.h"
#if defined(SDK_OS_FREE_RTOS)
#include "FreeRTOS.h"
#include "semphr.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* PXP interrupt initialization.
*/
static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void);
/**
* PXP interrupt de-initialization.
*/
static void _lv_gpu_nxp_pxp_interrupt_deinit(void);
/**
* Start the PXP job.
*/
static void _lv_gpu_nxp_pxp_run(void);
/**
* Wait for PXP completion.
*/
static void _lv_gpu_nxp_pxp_wait(void);
/**********************
* STATIC VARIABLES
**********************/
#if defined(SDK_OS_FREE_RTOS)
static SemaphoreHandle_t s_pxpIdleSem;
#endif
static volatile bool s_pxpIdle;
static lv_nxp_pxp_cfg_t pxp_default_cfg = {
.pxp_interrupt_init = _lv_gpu_nxp_pxp_interrupt_init,
.pxp_interrupt_deinit = _lv_gpu_nxp_pxp_interrupt_deinit,
.pxp_run = _lv_gpu_nxp_pxp_run,
.pxp_wait = _lv_gpu_nxp_pxp_wait,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void PXP_IRQHandler(void)
{
#if defined(SDK_OS_FREE_RTOS)
BaseType_t taskAwake = pdFALSE;
#endif
if(kPXP_CompleteFlag & PXP_GetStatusFlags(LV_GPU_NXP_PXP_ID)) {
PXP_ClearStatusFlags(LV_GPU_NXP_PXP_ID, kPXP_CompleteFlag);
#if defined(SDK_OS_FREE_RTOS)
xSemaphoreGiveFromISR(s_pxpIdleSem, &taskAwake);
portYIELD_FROM_ISR(taskAwake);
#else
s_pxpIdle = true;
#endif
}
}
lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void)
{
return &pxp_default_cfg;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void)
{
#if defined(SDK_OS_FREE_RTOS)
s_pxpIdleSem = xSemaphoreCreateBinary();
if(s_pxpIdleSem == NULL)
return LV_RES_INV;
NVIC_SetPriority(LV_GPU_NXP_PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
#endif
s_pxpIdle = true;
NVIC_EnableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
return LV_RES_OK;
}
static void _lv_gpu_nxp_pxp_interrupt_deinit(void)
{
NVIC_DisableIRQ(LV_GPU_NXP_PXP_IRQ_ID);
#if defined(SDK_OS_FREE_RTOS)
vSemaphoreDelete(s_pxpIdleSem);
#endif
}
/**
* Function to start PXP job.
*/
static void _lv_gpu_nxp_pxp_run(void)
{
s_pxpIdle = false;
PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable);
PXP_Start(LV_GPU_NXP_PXP_ID);
}
/**
* Function to wait for PXP completion.
*/
static void _lv_gpu_nxp_pxp_wait(void)
{
#if defined(SDK_OS_FREE_RTOS)
/* Return if PXP was never started, otherwise the semaphore will lock forever. */
if(s_pxpIdle == true)
return;
if(xSemaphoreTake(s_pxpIdleSem, portMAX_DELAY) == pdTRUE)
s_pxpIdle = true;
#else
while(s_pxpIdle == false) {
}
#endif
}
#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/

View File

@@ -0,0 +1,78 @@
/**
* @file lv_gpu_nxp_pxp_osa.h
*
*/
/**
* MIT License
*
* Copyright 2020, 2022 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_GPU_NXP_PXP_OSA_H
#define LV_GPU_NXP_PXP_OSA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
#include "lv_gpu_nxp_pxp.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* PXP device interrupt handler. Used to check PXP task completion status.
*/
void PXP_IRQHandler(void);
/**
* Helper function to get the PXP default configuration.
*/
lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_NXP_PXP_OSA_H*/

View File

@@ -0,0 +1,12 @@
CSRCS += lv_draw_vglite.c
CSRCS += lv_draw_vglite_arc.c
CSRCS += lv_draw_vglite_blend.c
CSRCS += lv_draw_vglite_line.c
CSRCS += lv_draw_vglite_rect.c
CSRCS += lv_vglite_buf.c
CSRCS += lv_vglite_utils.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite"

View File

@@ -0,0 +1,557 @@
/**
* @file lv_draw_vglite.c
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite.h"
#if LV_USE_GPU_NXP_VG_LITE
#include <math.h>
#include "lv_draw_vglite_blend.h"
#include "lv_draw_vglite_line.h"
#include "lv_draw_vglite_rect.h"
#include "lv_draw_vglite_arc.h"
#include "lv_vglite_buf.h"
#if LV_COLOR_DEPTH != 32
#include "../../../core/lv_refr.h"
#endif
/*********************
* DEFINES
*********************/
/* Minimum area (in pixels) for VG-Lite blit/fill processing. */
#ifndef LV_GPU_NXP_VG_LITE_SIZE_LIMIT
#define LV_GPU_NXP_VG_LITE_SIZE_LIMIT 5000
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_vglite_init_buf(lv_draw_ctx_t * draw_ctx);
static void lv_draw_vglite_wait_for_finish(lv_draw_ctx_t * draw_ctx);
static void lv_draw_vglite_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf);
static void lv_draw_vglite_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
static void lv_draw_vglite_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
static void lv_draw_vglite_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
static void lv_draw_vglite_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
static lv_res_t lv_draw_vglite_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
static lv_res_t lv_draw_vglite_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
const lv_area_t * coords);
static lv_res_t lv_draw_vglite_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
const lv_area_t * coords);
static void lv_draw_vglite_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_vglite_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_vglite_ctx_t * vglite_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
vglite_draw_ctx->base_draw.init_buf = lv_draw_vglite_init_buf;
vglite_draw_ctx->base_draw.draw_line = lv_draw_vglite_line;
vglite_draw_ctx->base_draw.draw_arc = lv_draw_vglite_arc;
vglite_draw_ctx->base_draw.draw_rect = lv_draw_vglite_rect;
vglite_draw_ctx->base_draw.draw_img_decoded = lv_draw_vglite_img_decoded;
vglite_draw_ctx->blend = lv_draw_vglite_blend;
vglite_draw_ctx->base_draw.wait_for_finish = lv_draw_vglite_wait_for_finish;
vglite_draw_ctx->base_draw.buffer_copy = lv_draw_vglite_buffer_copy;
}
void lv_draw_vglite_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_deinit_ctx(drv, draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* During rendering, LVGL might initializes new draw_ctxs and start drawing into
* a separate buffer (called layer). If the content to be rendered has "holes",
* e.g. rounded corner, LVGL temporarily sets the disp_drv.screen_transp flag.
* It means the renderers should draw into an ARGB buffer.
* With 32 bit color depth it's not a big problem but with 16 bit color depth
* the target pixel format is ARGB8565 which is not supported by the GPU.
* In this case, the VG-Lite callbacks should fallback to SW rendering.
*/
static inline bool need_argb8565_support()
{
#if LV_COLOR_DEPTH != 32
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->screen_transp == 1)
return true;
#endif
return false;
}
static void lv_draw_vglite_init_buf(lv_draw_ctx_t * draw_ctx)
{
lv_gpu_nxp_vglite_init_buf(draw_ctx->buf, draw_ctx->buf_area, lv_area_get_width(draw_ctx->buf_area));
}
static void lv_draw_vglite_wait_for_finish(lv_draw_ctx_t * draw_ctx)
{
vg_lite_finish();
lv_draw_sw_wait_for_finish(draw_ctx);
}
static void lv_draw_vglite_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(need_argb8565_support()) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
return;
}
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
bool done = false;
/*Fill/Blend only non masked, normal blended*/
if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL &&
lv_area_get_size(&blend_area) >= LV_GPU_NXP_VG_LITE_SIZE_LIMIT) {
const lv_color_t * src_buf = dsc->src_buf;
if(src_buf == NULL) {
done = (lv_gpu_nxp_vglite_fill(&blend_area, dsc->color, dsc->opa) == LV_RES_OK);
if(!done)
VG_LITE_LOG_TRACE("VG-Lite fill failed. Fallback.");
}
else {
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (dsc->blend_area->x1 - draw_ctx->buf_area->x1);
src_area.y1 = blend_area.y1 - (dsc->blend_area->y1 - draw_ctx->buf_area->y1);
src_area.x2 = src_area.x1 + lv_area_get_width(dsc->blend_area) - 1;
src_area.y2 = src_area.y1 + lv_area_get_height(dsc->blend_area) - 1;
lv_coord_t src_stride = lv_area_get_width(dsc->blend_area);
#if VG_LITE_BLIT_SPLIT_ENABLED
lv_color_t * dest_buf = draw_ctx->buf;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
done = (lv_gpu_nxp_vglite_blit_split(dest_buf, &blend_area, dest_stride,
src_buf, &src_area, src_stride, dsc->opa) == LV_RES_OK);
#else
done = (lv_gpu_nxp_vglite_blit(&blend_area, src_buf, &src_area, src_stride, dsc->opa) == LV_RES_OK);
#endif
if(!done)
VG_LITE_LOG_TRACE("VG-Lite blit failed. Fallback.");
}
}
if(!done)
lv_draw_sw_blend_basic(draw_ctx, dsc);
}
static void lv_draw_vglite_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf)
{
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(need_argb8565_support()) {
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
return;
}
const lv_color_t * src_buf = (const lv_color_t *)map_p;
if(!src_buf) {
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
return;
}
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t blend_area;
bool has_transform = dsc->angle != 0 || dsc->zoom != LV_IMG_ZOOM_NONE;
if(has_transform)
lv_area_copy(&blend_area, &rel_coords);
else if(!_lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
return; /*Fully clipped, nothing to do*/
bool has_mask = lv_draw_mask_is_any(&blend_area);
bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
bool done = false;
if(!has_mask && !has_recolor && !lv_img_cf_is_chroma_keyed(cf) &&
lv_area_get_size(&blend_area) >= LV_GPU_NXP_VG_LITE_SIZE_LIMIT
#if LV_COLOR_DEPTH != 32
&& !lv_img_cf_has_alpha(cf)
#endif
) {
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (coords->x1 - draw_ctx->buf_area->x1);
src_area.y1 = blend_area.y1 - (coords->y1 - draw_ctx->buf_area->y1);
src_area.x2 = src_area.x1 + lv_area_get_width(coords) - 1;
src_area.y2 = src_area.y1 + lv_area_get_height(coords) - 1;
lv_coord_t src_stride = lv_area_get_width(coords);
#if VG_LITE_BLIT_SPLIT_ENABLED
lv_color_t * dest_buf = draw_ctx->buf;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
if(has_transform)
/* VG-Lite blit split with transformation is not supported! */
done = false;
else
done = (lv_gpu_nxp_vglite_blit_split(dest_buf, &blend_area, dest_stride,
src_buf, &src_area, src_stride, dsc->opa) == LV_RES_OK);
#else
if(has_transform)
done = (lv_gpu_nxp_vglite_blit_transform(&blend_area, &rel_clip_area,
src_buf, &src_area, src_stride, dsc) == LV_RES_OK);
else
done = (lv_gpu_nxp_vglite_blit(&blend_area, src_buf, &src_area, src_stride, dsc->opa) == LV_RES_OK);
#endif
if(!done)
VG_LITE_LOG_TRACE("VG-Lite blit %sfailed. Fallback.", has_transform ? "transform " : "");
}
if(!done)
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf);
}
static void lv_draw_vglite_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
bool done = false;
if(lv_area_get_size(dest_area) >= LV_GPU_NXP_VG_LITE_SIZE_LIMIT) {
done = lv_gpu_nxp_vglite_buffer_copy(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride);
if(!done)
VG_LITE_LOG_TRACE("VG-Lite buffer copy failed. Fallback.");
}
if(!done)
lv_draw_sw_buffer_copy(draw_ctx, dest_buf, dest_stride, dest_area, src_buf, src_stride, src_area);
}
static void lv_draw_vglite_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2)
{
if(dsc->width == 0)
return;
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(point1->x == point2->x && point1->y == point2->y)
return;
if(need_argb8565_support()) {
lv_draw_sw_line(draw_ctx, dsc, point1, point2);
return;
}
lv_area_t rel_clip_area;
rel_clip_area.x1 = LV_MIN(point1->x, point2->x) - dsc->width / 2;
rel_clip_area.x2 = LV_MAX(point1->x, point2->x) + dsc->width / 2;
rel_clip_area.y1 = LV_MIN(point1->y, point2->y) - dsc->width / 2;
rel_clip_area.y2 = LV_MAX(point1->y, point2->y) + dsc->width / 2;
lv_area_t clipped_coords;
if(!_lv_area_intersect(&clipped_coords, &rel_clip_area, draw_ctx->clip_area))
return; /*Fully clipped, nothing to do*/
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_point_t rel_point1 = { point1->x - draw_ctx->buf_area->x1, point1->y - draw_ctx->buf_area->y1 };
lv_point_t rel_point2 = { point2->x - draw_ctx->buf_area->x1, point2->y - draw_ctx->buf_area->y1 };
bool done = false;
bool has_mask = lv_draw_mask_is_any(&rel_clip_area);
if(!has_mask) {
done = (lv_gpu_nxp_vglite_draw_line(&rel_point1, &rel_point2, &rel_clip_area, dsc) == LV_RES_OK);
if(!done)
VG_LITE_LOG_TRACE("VG-Lite draw line failed. Fallback.");
}
if(!done)
lv_draw_sw_line(draw_ctx, dsc, point1, point2);
}
static void lv_draw_vglite_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
if(need_argb8565_support()) {
lv_draw_sw_rect(draw_ctx, dsc, coords);
return;
}
lv_draw_rect_dsc_t vglite_dsc;
lv_memcpy(&vglite_dsc, dsc, sizeof(vglite_dsc));
vglite_dsc.bg_opa = 0;
vglite_dsc.bg_img_opa = 0;
vglite_dsc.border_opa = 0;
vglite_dsc.outline_opa = 0;
#if LV_DRAW_COMPLEX
/* Draw the shadow with CPU */
lv_draw_sw_rect(draw_ctx, &vglite_dsc, coords);
vglite_dsc.shadow_opa = 0;
#endif /*LV_DRAW_COMPLEX*/
/* Draw the background */
vglite_dsc.bg_opa = dsc->bg_opa;
if(lv_draw_vglite_bg(draw_ctx, &vglite_dsc, coords) != LV_RES_OK)
lv_draw_sw_rect(draw_ctx, &vglite_dsc, coords);
vglite_dsc.bg_opa = 0;
/* Draw the background image
* It will be done once draw_ctx->draw_img_decoded()
* callback gets called from lv_draw_sw_rect().
*/
vglite_dsc.bg_img_opa = dsc->bg_img_opa;
lv_draw_sw_rect(draw_ctx, &vglite_dsc, coords);
vglite_dsc.bg_img_opa = 0;
/* Draw the border */
vglite_dsc.border_opa = dsc->border_opa;
if(lv_draw_vglite_border(draw_ctx, &vglite_dsc, coords) != LV_RES_OK)
lv_draw_sw_rect(draw_ctx, &vglite_dsc, coords);
vglite_dsc.border_opa = 0;
/* Draw the outline */
vglite_dsc.outline_opa = dsc->outline_opa;
if(lv_draw_vglite_outline(draw_ctx, &vglite_dsc, coords) != LV_RES_OK)
lv_draw_sw_rect(draw_ctx, &vglite_dsc, coords);
}
static lv_res_t lv_draw_vglite_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
if(dsc->bg_opa <= (lv_opa_t)LV_OPA_MIN)
return LV_RES_INV;
lv_area_t rel_coords;
lv_area_copy(&rel_coords, coords);
/*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/
if(dsc->border_width > 1 && dsc->border_opa >= (lv_opa_t)LV_OPA_MAX && dsc->radius != 0) {
rel_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0;
rel_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0;
rel_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0;
rel_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0;
}
lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t clipped_coords;
if(!_lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area))
return LV_RES_OK; /*Fully clipped, nothing to do*/
bool has_mask = lv_draw_mask_is_any(&rel_coords);
lv_grad_dir_t grad_dir = dsc->bg_grad.dir;
lv_color_t bg_color = (grad_dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) ?
dsc->bg_color : dsc->bg_grad.stops[0].color;
if(bg_color.full == dsc->bg_grad.stops[1].color.full)
grad_dir = LV_GRAD_DIR_NONE;
/*
* Most simple case: just a plain rectangle (no mask, no radius, no gradient)
* shall be handled by draw_ctx->blend().
*
* Complex case: gradient or radius but no mask.
*/
if(!has_mask && ((dsc->radius != 0) || (grad_dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE))) {
lv_res_t res = lv_gpu_nxp_vglite_draw_bg(&rel_coords, &rel_clip_area, dsc);
if(res != LV_RES_OK)
VG_LITE_LOG_TRACE("VG-Lite draw bg failed. Fallback.");
return res;
}
return LV_RES_INV;
}
static lv_res_t lv_draw_vglite_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
const lv_area_t * coords)
{
if(dsc->border_opa <= (lv_opa_t)LV_OPA_MIN)
return LV_RES_INV;
if(dsc->border_width == 0)
return LV_RES_INV;
if(dsc->border_post)
return LV_RES_INV;
if(dsc->border_side != (lv_border_side_t)LV_BORDER_SIDE_FULL)
return LV_RES_INV;
lv_area_t rel_coords;
lv_coord_t border_width = dsc->border_width;
/* Move border inwards to align with software rendered border */
rel_coords.x1 = coords->x1 + ceil(border_width / 2.0f);
rel_coords.x2 = coords->x2 - floor(border_width / 2.0f);
rel_coords.y1 = coords->y1 + ceil(border_width / 2.0f);
rel_coords.y2 = coords->y2 - floor(border_width / 2.0f);
lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t clipped_coords;
if(!_lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area))
return LV_RES_OK; /*Fully clipped, nothing to do*/
lv_res_t res = lv_gpu_nxp_vglite_draw_border_generic(&rel_coords, &rel_clip_area, dsc, true);
if(res != LV_RES_OK)
VG_LITE_LOG_TRACE("VG-Lite draw border failed. Fallback.");
return res;
}
static lv_res_t lv_draw_vglite_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
const lv_area_t * coords)
{
if(dsc->outline_opa <= (lv_opa_t)LV_OPA_MIN)
return LV_RES_INV;
if(dsc->outline_width == 0)
return LV_RES_INV;
/* Move outline outwards to align with software rendered outline */
lv_coord_t outline_pad = dsc->outline_pad - 1;
lv_area_t rel_coords;
rel_coords.x1 = coords->x1 - outline_pad - floor(dsc->outline_width / 2.0f);
rel_coords.x2 = coords->x2 + outline_pad + ceil(dsc->outline_width / 2.0f);
rel_coords.y1 = coords->y1 - outline_pad - floor(dsc->outline_width / 2.0f);
rel_coords.y2 = coords->y2 + outline_pad + ceil(dsc->outline_width / 2.0f);
lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_area_t clipped_coords;
if(!_lv_area_intersect(&clipped_coords, &rel_coords, &rel_clip_area))
return LV_RES_OK; /*Fully clipped, nothing to do*/
lv_res_t res = lv_gpu_nxp_vglite_draw_border_generic(&rel_coords, &rel_clip_area, dsc, false);
if(res != LV_RES_OK)
VG_LITE_LOG_TRACE("VG-Lite draw outline failed. Fallback.");
return res;
}
static void lv_draw_vglite_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle)
{
bool done = false;
#if LV_DRAW_COMPLEX
if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
return;
if(dsc->width == 0)
return;
if(start_angle == end_angle)
return;
if(need_argb8565_support()) {
lv_draw_sw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
return;
}
lv_point_t rel_center = {center->x - draw_ctx->buf_area->x1, center->y - draw_ctx->buf_area->y1};
lv_area_t rel_clip_area;
lv_area_copy(&rel_clip_area, draw_ctx->clip_area);
lv_area_move(&rel_clip_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
bool has_mask = lv_draw_mask_is_any(&rel_clip_area);
if(!has_mask) {
done = (lv_gpu_nxp_vglite_draw_arc(&rel_center, (int32_t)radius, (int32_t)start_angle, (int32_t)end_angle,
&rel_clip_area, dsc) == LV_RES_OK);
if(!done)
VG_LITE_LOG_TRACE("VG-Lite draw arc failed. Fallback.");
}
#endif/*LV_DRAW_COMPLEX*/
if(!done)
lv_draw_sw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle);
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_draw_vglite.h
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_VGLITE_H
#define LV_DRAW_VGLITE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "../../sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_vglite_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_vglite_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_vglite_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_H*/

View File

@@ -0,0 +1,679 @@
/**
* @file lv_draw_vglite_arc.c
*
*/
/**
* MIT License
*
* Copyright 2021-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite_arc.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_buf.h"
#include <math.h>
/*********************
* DEFINES
*********************/
#define T_FRACTION 16384.0f
#define DICHOTO_ITER 5
static const uint16_t TperDegree[90] = {
0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581,
1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384,
3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222,
5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077,
7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936,
9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794,
10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637,
12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448,
14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212
};
/**********************
* TYPEDEFS
**********************/
/* intermediate arc params */
typedef struct _vg_arc {
int32_t angle; /* angle <90deg */
int32_t quarter; /* 0-3 counter-clockwise */
int32_t rad; /* radius */
int32_t p0x; /* point P0 */
int32_t p0y;
int32_t p1x; /* point P1 */
int32_t p1y;
int32_t p2x; /* point P2 */
int32_t p2y;
int32_t p3x; /* point P3 */
int32_t p3y;
} vg_arc;
typedef struct _cubic_cont_pt {
float p0;
float p1;
float p2;
float p3;
} cubic_cont_pt;
/**********************
* STATIC PROTOTYPES
**********************/
static void rotate_point(int32_t angle, int32_t * x, int32_t * y);
static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
int32_t start_angle, int32_t end_angle, const lv_point_t * center, bool cw);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_gpu_nxp_vglite_draw_arc(const lv_point_t * center, int32_t radius, int32_t start_angle, int32_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; /*Convert color to RGBA8888*/
vg_lite_path_t path;
vg_lite_color_t vgcol; /* vglite takes ABGR */
bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false;
vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
/* path: max size = 16 cubic bezier (7 words each) */
int32_t arc_path[16 * 7];
lv_memset_00(arc_path, sizeof(arc_path));
/*** Init path ***/
lv_coord_t width = dsc->width; /* inner arc radius = outer arc radius - width */
if(width > (lv_coord_t)radius)
width = radius;
int pidx = 0;
int32_t cp_x, cp_y; /* control point coords */
/* first control point of curve */
cp_x = radius;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_MOVE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
/* draw 1-5 outer quarters */
add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, center, true);
if(donut) {
/* close outer circle */
cp_x = radius;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_LINE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
/* start inner circle */
cp_x = radius - width;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_MOVE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
}
else if(dsc->rounded != 0U) { /* 1st rounded arc ending */
cp_x = radius - width / 2;
cp_y = 0;
rotate_point(end_angle, &cp_x, &cp_y);
lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
add_arc_path(arc_path, &pidx, width / 2, end_angle, (end_angle + 180),
&round_center, true);
}
else { /* 1st flat ending */
cp_x = radius - width;
cp_y = 0;
rotate_point(end_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_LINE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
}
/* draw 1-5 inner quarters */
add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, center, false);
/* last control point of curve */
if(donut) { /* close the loop */
cp_x = radius - width;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_LINE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
}
else if(dsc->rounded != 0U) { /* 2nd rounded arc ending */
cp_x = radius - width / 2;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
add_arc_path(arc_path, &pidx, width / 2, (start_angle + 180), (start_angle + 360),
&round_center, true);
}
else { /* 2nd flat ending */
cp_x = radius;
cp_y = 0;
rotate_point(start_angle, &cp_x, &cp_y);
arc_path[pidx++] = VLC_OP_LINE;
arc_path[pidx++] = center->x + cp_x;
arc_path[pidx++] = center->y + cp_y;
}
arc_path[pidx++] = VLC_OP_END;
err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, (uint32_t)pidx * sizeof(int32_t), arc_path,
(vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
if(lv_vglite_premult_and_swizzle(&vgcol, col32, dsc->opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
lv_vglite_set_scissor(clip_area);
/*** Draw arc ***/
err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Draw arc failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
lv_vglite_disable_scissor();
err = vg_lite_clear_path(&path);
VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void copy_arc(vg_arc * dst, vg_arc * src)
{
dst->quarter = src->quarter;
dst->rad = src->rad;
dst->angle = src->angle;
dst->p0x = src->p0x;
dst->p1x = src->p1x;
dst->p2x = src->p2x;
dst->p3x = src->p3x;
dst->p0y = src->p0y;
dst->p1y = src->p1y;
dst->p2y = src->p2y;
dst->p3y = src->p3y;
}
/**
* Rotate the point according given rotation angle rotation center is 0,0
*/
static void rotate_point(int32_t angle, int32_t * x, int32_t * y)
{
int32_t ori_x = *x;
int32_t ori_y = *y;
int16_t alpha = (int16_t)angle;
*x = ((lv_trigo_cos(alpha) * ori_x) / LV_TRIGO_SIN_MAX) - ((lv_trigo_sin(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
*y = ((lv_trigo_sin(alpha) * ori_x) / LV_TRIGO_SIN_MAX) + ((lv_trigo_cos(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
}
/**
* Set full arc control points depending on quarter.
* Control points match the best approximation of a circle.
* Arc Quarter position is:
* Q2 | Q3
* ---+---
* Q1 | Q0
*/
static void set_full_arc(vg_arc * fullarc)
{
/* the tangent lenght for the bezier circle approx */
float tang = ((float)fullarc->rad) * BEZIER_OPTIM_CIRCLE;
switch(fullarc->quarter) {
case 0:
/* first quarter */
fullarc->p0x = fullarc->rad;
fullarc->p0y = 0;
fullarc->p1x = fullarc->rad;
fullarc->p1y = (int32_t)tang;
fullarc->p2x = (int32_t)tang;
fullarc->p2y = fullarc->rad;
fullarc->p3x = 0;
fullarc->p3y = fullarc->rad;
break;
case 1:
/* second quarter */
fullarc->p0x = 0;
fullarc->p0y = fullarc->rad;
fullarc->p1x = 0 - (int32_t)tang;
fullarc->p1y = fullarc->rad;
fullarc->p2x = 0 - fullarc->rad;
fullarc->p2y = (int32_t)tang;
fullarc->p3x = 0 - fullarc->rad;
fullarc->p3y = 0;
break;
case 2:
/* third quarter */
fullarc->p0x = 0 - fullarc->rad;
fullarc->p0y = 0;
fullarc->p1x = 0 - fullarc->rad;
fullarc->p1y = 0 - (int32_t)tang;
fullarc->p2x = 0 - (int32_t)tang;
fullarc->p2y = 0 - fullarc->rad;
fullarc->p3x = 0;
fullarc->p3y = 0 - fullarc->rad;
break;
case 3:
/* fourth quarter */
fullarc->p0x = 0;
fullarc->p0y = 0 - fullarc->rad;
fullarc->p1x = (int32_t)tang;
fullarc->p1y = 0 - fullarc->rad;
fullarc->p2x = fullarc->rad;
fullarc->p2y = 0 - (int32_t)tang;
fullarc->p3x = fullarc->rad;
fullarc->p3y = 0;
break;
default:
LV_LOG_ERROR("Invalid arc quarter value.");
break;
}
}
/**
* Linear interpolation between two points 'a' and 'b'
* 't' parameter is the proportion ratio expressed in range [0 ; T_FRACTION ]
*/
static inline float lerp(float coord_a, float coord_b, uint16_t t)
{
float tf = (float)t;
return ((T_FRACTION - tf) * coord_a + tf * coord_b) / T_FRACTION;
}
/**
* Computes a point of bezier curve given 't' param
*/
static inline float comp_bezier_point(float t, cubic_cont_pt cp)
{
float t_sq = t * t;
float inv_t_sq = (1.0f - t) * (1.0f - t);
float apt = (1.0f - t) * inv_t_sq * cp.p0 + 3.0f * inv_t_sq * t * cp.p1 + 3.0f * (1.0f - t) * t_sq * cp.p2 + t * t_sq *
cp.p3;
return apt;
}
/**
* Find parameter 't' in curve at point 'pt'
* proceed by dichotomy on only 1 dimension,
* works only if the curve is monotonic
* bezier curve is defined by control points [p0 p1 p2 p3]
* 'dec' tells if curve is decreasing (true) or increasing (false)
*/
static uint16_t get_bez_t_from_pos(float pt, cubic_cont_pt cp, bool dec)
{
/* initialize dichotomy with boundary 't' values */
float t_low = 0.0f;
float t_mid = 0.5f;
float t_hig = 1.0f;
float a_pt;
/* dichotomy loop */
for(int i = 0; i < DICHOTO_ITER; i++) {
a_pt = comp_bezier_point(t_mid, cp);
/* check mid-point position on bezier curve versus targeted point */
if((a_pt > pt) != dec) {
t_hig = t_mid;
}
else {
t_low = t_mid;
}
/* define new 't' param for mid-point */
t_mid = (t_low + t_hig) / 2.0f;
}
/* return parameter 't' in integer range [0 ; T_FRACTION] */
return (uint16_t)floorf(t_mid * T_FRACTION + 0.5f);
}
/**
* Gives relative coords of the control points
* for the sub-arc starting at angle with given angle span
*/
static void get_subarc_control_points(vg_arc * arc, int32_t span)
{
vg_arc fullarc;
fullarc.angle = arc->angle;
fullarc.quarter = arc->quarter;
fullarc.rad = arc->rad;
set_full_arc(&fullarc);
/* special case of full arc */
if(arc->angle == 90) {
copy_arc(arc, &fullarc);
return;
}
/* compute 1st arc using the geometric construction of curve */
uint16_t t2 = TperDegree[arc->angle + span];
/* lerp for A */
float a2x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t2);
float a2y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t2);
/* lerp for B */
float b2x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t2);
float b2y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t2);
/* lerp for C */
float c2x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t2);
float c2y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t2);
/* lerp for D */
float d2x = lerp(a2x, b2x, t2);
float d2y = lerp(a2y, b2y, t2);
/* lerp for E */
float e2x = lerp(b2x, c2x, t2);
float e2y = lerp(b2y, c2y, t2);
float pt2x = lerp(d2x, e2x, t2);
float pt2y = lerp(d2y, e2y, t2);
/* compute sub-arc using the geometric construction of curve */
uint16_t t1 = TperDegree[arc->angle];
/* lerp for A */
float a1x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t1);
float a1y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t1);
/* lerp for B */
float b1x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t1);
float b1y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t1);
/* lerp for C */
float c1x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t1);
float c1y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t1);
/* lerp for D */
float d1x = lerp(a1x, b1x, t1);
float d1y = lerp(a1y, b1y, t1);
/* lerp for E */
float e1x = lerp(b1x, c1x, t1);
float e1y = lerp(b1y, c1y, t1);
float pt1x = lerp(d1x, e1x, t1);
float pt1y = lerp(d1y, e1y, t1);
/* find the 't3' parameter for point P(t1) on the sub-arc [P0 A2 D2 P(t2)] using dichotomy
* use position of x axis only */
uint16_t t3;
t3 = get_bez_t_from_pos(pt1x,
(cubic_cont_pt) {
.p0 = ((float)fullarc.p0x), .p1 = a2x, .p2 = d2x, .p3 = pt2x
},
(bool)(pt2x < (float)fullarc.p0x));
/* lerp for B */
float b3x = lerp(a2x, d2x, t3);
float b3y = lerp(a2y, d2y, t3);
/* lerp for C */
float c3x = lerp(d2x, pt2x, t3);
float c3y = lerp(d2y, pt2y, t3);
/* lerp for E */
float e3x = lerp(b3x, c3x, t3);
float e3y = lerp(b3y, c3y, t3);
arc->p0x = (int32_t)floorf(0.5f + pt1x);
arc->p0y = (int32_t)floorf(0.5f + pt1y);
arc->p1x = (int32_t)floorf(0.5f + e3x);
arc->p1y = (int32_t)floorf(0.5f + e3y);
arc->p2x = (int32_t)floorf(0.5f + c3x);
arc->p2y = (int32_t)floorf(0.5f + c3y);
arc->p3x = (int32_t)floorf(0.5f + pt2x);
arc->p3y = (int32_t)floorf(0.5f + pt2y);
}
/**
* Gives relative coords of the control points
*/
static void get_arc_control_points(vg_arc * arc, bool start)
{
vg_arc fullarc;
fullarc.angle = arc->angle;
fullarc.quarter = arc->quarter;
fullarc.rad = arc->rad;
set_full_arc(&fullarc);
/* special case of full arc */
if(arc->angle == 90) {
copy_arc(arc, &fullarc);
return;
}
/* compute sub-arc using the geometric construction of curve */
uint16_t t = TperDegree[arc->angle];
/* lerp for A */
float ax = lerp((float)fullarc.p0x, (float)fullarc.p1x, t);
float ay = lerp((float)fullarc.p0y, (float)fullarc.p1y, t);
/* lerp for B */
float bx = lerp((float)fullarc.p1x, (float)fullarc.p2x, t);
float by = lerp((float)fullarc.p1y, (float)fullarc.p2y, t);
/* lerp for C */
float cx = lerp((float)fullarc.p2x, (float)fullarc.p3x, t);
float cy = lerp((float)fullarc.p2y, (float)fullarc.p3y, t);
/* lerp for D */
float dx = lerp(ax, bx, t);
float dy = lerp(ay, by, t);
/* lerp for E */
float ex = lerp(bx, cx, t);
float ey = lerp(by, cy, t);
/* sub-arc's control points are tangents of DeCasteljau's algorithm */
if(start) {
arc->p0x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
arc->p0y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
arc->p1x = (int32_t)floorf(0.5f + ex);
arc->p1y = (int32_t)floorf(0.5f + ey);
arc->p2x = (int32_t)floorf(0.5f + cx);
arc->p2y = (int32_t)floorf(0.5f + cy);
arc->p3x = fullarc.p3x;
arc->p3y = fullarc.p3y;
}
else {
arc->p0x = fullarc.p0x;
arc->p0y = fullarc.p0y;
arc->p1x = (int32_t)floorf(0.5f + ax);
arc->p1y = (int32_t)floorf(0.5f + ay);
arc->p2x = (int32_t)floorf(0.5f + dx);
arc->p2y = (int32_t)floorf(0.5f + dy);
arc->p3x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
arc->p3y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
}
}
/**
* Add the arc control points into the path data for vglite,
* taking into account the real center of the arc (translation).
* arc_path: (in/out) the path data array for vglite
* pidx: (in/out) index of last element added in arc_path
* q_arc: (in) the arc data containing control points
* center: (in) the center of the circle in draw coordinates
* cw: (in) true if arc is clockwise
*/
static void add_split_arc_path(int32_t * arc_path, int * pidx, vg_arc * q_arc, const lv_point_t * center, bool cw)
{
/* assumes first control point already in array arc_path[] */
int idx = *pidx;
if(cw) {
#if BEZIER_DBG_CONTROL_POINTS
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p1x + center->x;
arc_path[idx++] = q_arc->p1y + center->y;
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p2x + center->x;
arc_path[idx++] = q_arc->p2y + center->y;
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p3x + center->x;
arc_path[idx++] = q_arc->p3y + center->y;
#else
arc_path[idx++] = VLC_OP_CUBIC;
arc_path[idx++] = q_arc->p1x + center->x;
arc_path[idx++] = q_arc->p1y + center->y;
arc_path[idx++] = q_arc->p2x + center->x;
arc_path[idx++] = q_arc->p2y + center->y;
arc_path[idx++] = q_arc->p3x + center->x;
arc_path[idx++] = q_arc->p3y + center->y;
#endif
}
else { /* reverse points order when counter-clockwise */
#if BEZIER_DBG_CONTROL_POINTS
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p2x + center->x;
arc_path[idx++] = q_arc->p2y + center->y;
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p1x + center->x;
arc_path[idx++] = q_arc->p1y + center->y;
arc_path[idx++] = VLC_OP_LINE;
arc_path[idx++] = q_arc->p0x + center->x;
arc_path[idx++] = q_arc->p0y + center->y;
#else
arc_path[idx++] = VLC_OP_CUBIC;
arc_path[idx++] = q_arc->p2x + center->x;
arc_path[idx++] = q_arc->p2y + center->y;
arc_path[idx++] = q_arc->p1x + center->x;
arc_path[idx++] = q_arc->p1y + center->y;
arc_path[idx++] = q_arc->p0x + center->x;
arc_path[idx++] = q_arc->p0y + center->y;
#endif
}
/* update index i n path array*/
*pidx = idx;
}
static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
int32_t start_angle, int32_t end_angle, const lv_point_t * center, bool cw)
{
if(end_angle < start_angle) end_angle += 360;
/* set number of arcs to draw */
vg_arc q_arc;
int32_t start_arc_angle = start_angle % 90;
int32_t end_arc_angle = end_angle % 90;
int32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0;
int32_t nbarc = (end_angle - start_angle - inv_start_arc_angle - end_arc_angle) / 90;
q_arc.rad = radius;
/* handle special case of start & end point in the same quarter */
if(((start_angle / 90) == (end_angle / 90)) && (nbarc <= 0)) {
q_arc.quarter = (start_angle / 90) % 4;
q_arc.angle = start_arc_angle;
get_subarc_control_points(&q_arc, end_arc_angle - start_arc_angle);
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
return;
}
if(cw) {
/* partial starting arc */
if(start_arc_angle > 0) {
q_arc.quarter = (start_angle / 90) % 4;
q_arc.angle = start_arc_angle;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, true);
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
/* full arcs */
for(int32_t q = 0; q < nbarc ; q++) {
q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
q_arc.angle = 90;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
/* partial ending arc */
if(end_arc_angle > 0) {
q_arc.quarter = (end_angle / 90) % 4;
q_arc.angle = end_arc_angle;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, false);
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
}
else { /* counter clockwise */
/* partial ending arc */
if(end_arc_angle > 0) {
q_arc.quarter = (end_angle / 90) % 4;
q_arc.angle = end_arc_angle;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, false);
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
/* full arcs */
for(int32_t q = nbarc - 1; q >= 0; q--) {
q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
q_arc.angle = 90;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
/* partial starting arc */
if(start_arc_angle > 0) {
q_arc.quarter = (start_angle / 90) % 4;
q_arc.angle = start_arc_angle;
/* get cubic points relative to center */
get_arc_control_points(&q_arc, true);
/* put cubic points in arc_path */
add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
}
}
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,83 @@
/**
* @file lv_draw_vglite_arc.h
*
*/
/**
* MIT License
*
* Copyright 2021-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_VGLITE_ARC_H
#define LV_DRAW_VGLITE_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw arc shape with effects
*
* @param[in] center Arc center with relative coordinates
* @param[in] radius Radius of external arc
* @param[in] start_angle Starting angle in degrees
* @param[in] end_angle Ending angle in degrees
* @param[in] clip_area Clipping area with relative coordinates to dest buff
* @param[in] dsc Arc description structure (width, rounded ending, opacity)
*
* @retval LV_RES_OK Draw completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_draw_arc(const lv_point_t * center, int32_t radius, int32_t start_angle, int32_t end_angle,
const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_ARC_H*/

View File

@@ -0,0 +1,635 @@
/**
* @file lv_draw_vglite_blend.c
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite_blend.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_buf.h"
#include "lv_vglite_utils.h"
/*********************
* DEFINES
*********************/
/** Stride in px required by VG-Lite HW*/
#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U
#if VG_LITE_BLIT_SPLIT_ENABLED
/**
* BLIT split threshold - BLITs with width or height higher than this value will be done
* in multiple steps. Value must be 16-aligned. Don't change.
*/
#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Blit image, with optional opacity.
*
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] opa Opacity
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
static lv_res_t lv_vglite_blit(const lv_area_t * src_area, lv_opa_t opa);
/**
* Check source memory and stride alignment.
*
* @param[in] src_buf Source buffer
* @param[in] src_stride Stride of source buffer in pixels
*
* @retval LV_RES_OK Alignment OK
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
static lv_res_t check_src_alignment(const lv_color_t * src_buf, lv_coord_t src_stride);
/**
* Creates matrix that translates to origin of given destination area.
*
* @param[in] dest_area Area with relative coordinates of destination buffer
*/
static inline void lv_vglite_set_translation_matrix(const lv_area_t * dest_area);
/**
* Creates matrix that translates to origin of given destination area with transformation (scale or rotate).
*
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dsc Image descriptor
*/
static inline void lv_vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_img_dsc_t * dsc);
#if VG_LITE_BLIT_SPLIT_ENABLED
/**
* Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
*
* @param[in/out] area Area to be updated
* @param[in/out] buf Pointer to be updated
*/
static void align_x(lv_area_t * area, lv_color_t ** buf);
/**
* Move buffer pointer to the area start and update variables, Y-axis only.
*
* @param[in/out] area Area to be updated
* @param[in/out] buf Pointer to be updated
* @param[in] stride Buffer stride in pixels
*/
static void align_y(lv_area_t * area, lv_color_t ** buf, lv_coord_t stride);
/**
* Blit image split in tiles, with optional opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] opa Opacity
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
static lv_res_t lv_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa);
#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
/**********************
* STATIC VARIABLES
**********************/
static vg_lite_matrix_t vgmatrix;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_gpu_nxp_vglite_fill(const lv_area_t * dest_area, lv_color_t color, lv_opa_t opa)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
vg_lite_color_t vgcol; /* vglite takes ABGR */
vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
if(lv_vglite_premult_and_swizzle(&vgcol, col32, opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
if(opa >= (lv_opa_t)LV_OPA_MAX) { /*Opaque fill*/
vg_lite_rectangle_t rect = {
.x = dest_area->x1,
.y = dest_area->y1,
.width = lv_area_get_width(dest_area),
.height = lv_area_get_height(dest_area)
};
err = vg_lite_clear(vgbuf, &rect, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Clear failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
}
else { /*fill with transparency*/
vg_lite_path_t path;
int32_t path_data[] = { /*VG rectangular path*/
VLC_OP_MOVE, dest_area->x1, dest_area->y1,
VLC_OP_LINE, dest_area->x2 + 1, dest_area->y1,
VLC_OP_LINE, dest_area->x2 + 1, dest_area->y2 + 1,
VLC_OP_LINE, dest_area->x1, dest_area->y2 + 1,
VLC_OP_LINE, dest_area->x1, dest_area->y1,
VLC_OP_END
};
err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(path_data), path_data,
(vg_lite_float_t) dest_area->x1, (vg_lite_float_t) dest_area->y1,
((vg_lite_float_t) dest_area->x2) + 1.0f, ((vg_lite_float_t) dest_area->y2) + 1.0f);
VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
/*Draw rectangle*/
err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Draw rectangle failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
err = vg_lite_clear_path(&path);
VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
}
return LV_RES_OK;
}
#if VG_LITE_BLIT_SPLIT_ENABLED
lv_res_t lv_gpu_nxp_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa)
{
/* Set src_vgbuf structure. */
lv_vglite_set_src_buf(src_buf, src_area, src_stride);
lv_color_t * orig_dest_buf = dest_buf;
lv_res_t rv = lv_vglite_blit_split(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, opa);
/* Restore the original dest_vgbuf memory address. */
lv_vglite_set_dest_buf_ptr(orig_dest_buf);
return rv;
}
#else
lv_res_t lv_gpu_nxp_vglite_blit(const lv_area_t * dest_area,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa)
{
if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
VG_LITE_RETURN_INV("Check src alignment failed.");
/* Set src_vgbuf structure. */
lv_vglite_set_src_buf(src_buf, src_area, src_stride);
/* Set scissor. */
lv_vglite_set_scissor(dest_area);
/* Set vgmatrix. */
lv_vglite_set_translation_matrix(dest_area);
/* Start blit. */
lv_res_t rv = lv_vglite_blit(src_area, opa);
/* Disable scissor. */
lv_vglite_disable_scissor();
return rv;
}
lv_res_t lv_gpu_nxp_vglite_blit_transform(const lv_area_t * dest_area, const lv_area_t * clip_area,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc)
{
lv_res_t rv = LV_RES_INV;
if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
VG_LITE_RETURN_INV("Check src alignment failed.");
/* Set src_vgbuf structure. */
lv_vglite_set_src_buf(src_buf, src_area, src_stride);
/* Set scissor */
lv_vglite_set_scissor(clip_area);
/* Set vgmatrix. */
lv_vglite_set_transformation_matrix(dest_area, dsc);
/* Start blit. */
rv = lv_vglite_blit(src_area, dsc->opa);
/* Disable scissor. */
lv_vglite_disable_scissor();
return rv;
}
#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
lv_res_t lv_gpu_nxp_vglite_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
VG_LITE_RETURN_INV("Check src alignment failed.");
vg_lite_buffer_t src_vgbuf;
/* Set src_vgbuf structure. */
lv_vglite_set_buf(&src_vgbuf, src_buf, src_area, src_stride);
vg_lite_buffer_t dest_vgbuf;
/* Set dest_vgbuf structure. */
lv_vglite_set_buf(&dest_vgbuf, dest_buf, dest_area, dest_stride);
uint32_t rect[] = {
(uint32_t)src_area->x1, /* start x */
(uint32_t)src_area->y1, /* start y */
(uint32_t)lv_area_get_width(src_area), /* width */
(uint32_t)lv_area_get_height(src_area) /* height */
};
/* Set scissor. */
lv_vglite_set_scissor(dest_area);
/* Set vgmatrix. */
lv_vglite_set_translation_matrix(dest_area);
err = vg_lite_blit_rect(&dest_vgbuf, &src_vgbuf, rect, &vgmatrix,
VG_LITE_BLEND_NONE, 0xFFFFFFFFU, VG_LITE_FILTER_POINT);
if(err != VG_LITE_SUCCESS) {
LV_LOG_ERROR("Blit rectangle failed.");
/* Disable scissor. */
lv_vglite_disable_scissor();
return LV_RES_INV;
}
if(lv_vglite_run() != LV_RES_OK) {
LV_LOG_ERROR("Run failed.");
/* Disable scissor. */
lv_vglite_disable_scissor();
return LV_RES_INV;
}
/* Disable scissor. */
lv_vglite_disable_scissor();
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
#if VG_LITE_BLIT_SPLIT_ENABLED
static lv_res_t lv_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa)
{
lv_res_t rv = LV_RES_INV;
VG_LITE_LOG_TRACE("Blit "
"Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
"Size: ([%dx%d] -> [%dx%d]) | "
"Addr: (0x%x -> 0x%x)",
src_area->x1, src_area->y1, src_area->x2, src_area->y2,
dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
lv_area_get_width(src_area), lv_area_get_height(src_area),
lv_area_get_width(dest_area), lv_area_get_height(dest_area),
(uintptr_t)src_buf, (uintptr_t)dest_buf);
/* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */
align_x(src_area, (lv_color_t **)&src_buf);
align_y(src_area, (lv_color_t **)&src_buf, src_stride);
align_x(dest_area, (lv_color_t **)&dest_buf);
align_y(dest_area, (lv_color_t **)&dest_buf, dest_stride);
/* Stage 2: If we're in limit, do a single BLIT */
if((src_area->x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
(src_area->y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
VG_LITE_RETURN_INV("Check src alignment failed.");
/* Set new dest_vgbuf and src_vgbuf memory addresses. */
lv_vglite_set_dest_buf_ptr(dest_buf);
lv_vglite_set_src_buf_ptr(src_buf);
/* Set scissor */
lv_vglite_set_scissor(dest_area);
/* Set vgmatrix. */
lv_vglite_set_translation_matrix(dest_area);
/* Start blit. */
rv = lv_vglite_blit(src_area, opa);
/* Disable scissor. */
lv_vglite_disable_scissor();
VG_LITE_LOG_TRACE("Single "
"Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
"Size: ([%dx%d] -> [%dx%d]) | "
"Addr: (0x%x -> 0x%x) %s",
src_area->x1, src_area->y1, src_area->x2, src_area->y2,
dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
lv_area_get_width(src_area), lv_area_get_height(src_area),
lv_area_get_width(dest_area), lv_area_get_height(dest_area),
(uintptr_t)src_buf, (uintptr_t)dest_buf,
rv == LV_RES_OK ? "OK!" : "FAILED!");
return rv;
};
/* Stage 3: Split the BLIT into multiple tiles */
VG_LITE_LOG_TRACE("Split "
"Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
"Size: ([%dx%d] -> [%dx%d]) | "
"Addr: (0x%x -> 0x%x)",
src_area->x1, src_area->y1, src_area->x2, src_area->y2,
dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
lv_area_get_width(src_area), lv_area_get_height(src_area),
lv_area_get_width(dest_area), lv_area_get_height(dest_area),
(uintptr_t)src_buf, (uintptr_t)dest_buf);
lv_coord_t width = lv_area_get_width(src_area);
lv_coord_t height = lv_area_get_height(src_area);
/* Number of tiles needed */
int total_tiles_x = (src_area->x1 + width + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
int total_tiles_y = (src_area->y1 + height + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
/* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
* the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
* different */
int shift_src_x = (src_area->x1 > dest_area->x1) ? (src_area->x1 - dest_area->x1) : 0;
int shift_dest_x = (src_area->x1 < dest_area->x1) ? (dest_area->x1 - src_area->x1) : 0;
VG_LITE_LOG_TRACE("X shift: src: %d, dst: %d", shift_src_x, shift_dest_x);
lv_color_t * tile_dest_buf;
lv_area_t tile_dest_area;
const lv_color_t * tile_src_buf;
lv_area_t tile_src_area;
for(int y = 0; y < total_tiles_y; y++) {
tile_src_area.y1 = 0; /* no vertical alignment, always start from 0 */
tile_src_area.y2 = height - y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tile_src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tile_src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
}
tile_src_buf = src_buf + y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * src_stride;
tile_dest_area.y1 = tile_src_area.y1; /* y has no alignment, always in sync with src */
tile_dest_area.y2 = tile_src_area.y2;
tile_dest_buf = dest_buf + y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * dest_stride;
for(int x = 0; x < total_tiles_x; x++) {
if(x == 0) {
/* 1st tile is special - there may be a gap between buffer start pointer
* and area.x1 value, as the pointer has to be aligned.
* tile_src_buf pointer - keep init value from Y-loop.
* Also, 1st tile start is not shifted! shift is applied from 2nd tile */
tile_src_area.x1 = src_area->x1;
tile_dest_area.x1 = dest_area->x1;
}
else {
/* subsequent tiles always starts from 0, but shifted*/
tile_src_area.x1 = 0 + shift_src_x;
tile_dest_area.x1 = 0 + shift_dest_x;
/* and advance start pointer + 1 tile size */
tile_src_buf += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
tile_dest_buf += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
}
/* Clip tile end coordinates */
tile_src_area.x2 = width + src_area->x1 - x * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tile_src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tile_src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
tile_dest_area.x2 = width + dest_area->x1 - x * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
if(tile_dest_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
tile_dest_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
}
if(x < (total_tiles_x - 1)) {
/* And adjust end coords if shifted, but not for last tile! */
tile_src_area.x2 += shift_src_x;
tile_dest_area.x2 += shift_dest_x;
}
if(check_src_alignment(tile_src_buf, src_stride) != LV_RES_OK)
VG_LITE_RETURN_INV("Check src alignment failed.");
/* Set new dest_vgbuf and src_vgbuf memory addresses. */
lv_vglite_set_dest_buf_ptr(tile_dest_buf);
lv_vglite_set_src_buf_ptr(tile_src_buf);
/* Set scissor */
lv_vglite_set_scissor(&tile_dest_area);
/* Set vgmatrix. */
lv_vglite_set_translation_matrix(&tile_dest_area);
/* Start blit. */
rv = lv_vglite_blit(&tile_src_area, opa);
/* Disable scissor. */
lv_vglite_disable_scissor();
VG_LITE_LOG_TRACE("Tile [%d, %d] "
"Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
"Size: ([%dx%d] -> [%dx%d]) | "
"Addr: (0x%x -> 0x%x) %s",
x, y,
tile_src_area.x1, tile_src_area.y1, tile_src_area.x2, tile_src_area.y2,
tile_dest_area.x1, tile_dest_area.y1, tile_dest_area.x2, tile_dest_area.y2,
lv_area_get_width(&tile_src_area), lv_area_get_height(&tile_src_area),
lv_area_get_width(&tile_dest_area), lv_area_get_height(&tile_dest_area),
(uintptr_t)tile_src_buf, (uintptr_t)tile_dest_buf,
rv == LV_RES_OK ? "OK!" : "FAILED!");
if(rv != LV_RES_OK) {
return rv;
}
}
}
return rv;
}
#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
static lv_res_t lv_vglite_blit(const lv_area_t * src_area, lv_opa_t opa)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
vg_lite_buffer_t * dst_vgbuf = lv_vglite_get_dest_buf();
vg_lite_buffer_t * src_vgbuf = lv_vglite_get_src_buf();
uint32_t rect[] = {
(uint32_t)src_area->x1, /* start x */
(uint32_t)src_area->y1, /* start y */
(uint32_t)lv_area_get_width(src_area), /* width */
(uint32_t)lv_area_get_height(src_area) /* height */
};
uint32_t color;
vg_lite_blend_t blend;
if(opa >= (lv_opa_t)LV_OPA_MAX) {
color = 0xFFFFFFFFU;
blend = VG_LITE_BLEND_SRC_OVER;
src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
}
else {
if(vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
color = (opa << 24) | 0x00FFFFFFU;
}
else {
color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
}
blend = VG_LITE_BLEND_SRC_OVER;
src_vgbuf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
}
err = vg_lite_blit_rect(dst_vgbuf, src_vgbuf, rect, &vgmatrix, blend, color, VG_LITE_FILTER_POINT);
VG_LITE_ERR_RETURN_INV(err, "Blit rectangle failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
return LV_RES_OK;
}
static lv_res_t check_src_alignment(const lv_color_t * src_buf, lv_coord_t src_stride)
{
/* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
/* Test for pointer alignment */
if((((uintptr_t)src_buf) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != (uintptr_t)0x0U)
VG_LITE_RETURN_INV("Src buffer ptr (0x%x) not aligned to 0x%x bytes.",
(size_t)src_buf, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
/* Test for stride alignment */
if((src_stride % (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0x0U)
VG_LITE_RETURN_INV("Src buffer stride (%d px) not aligned to %d px.",
src_stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
return LV_RES_OK;
}
static inline void lv_vglite_set_translation_matrix(const lv_area_t * dest_area)
{
vg_lite_identity(&vgmatrix);
vg_lite_translate((vg_lite_float_t)dest_area->x1, (vg_lite_float_t)dest_area->y1, &vgmatrix);
}
static inline void lv_vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_img_dsc_t * dsc)
{
lv_vglite_set_translation_matrix(dest_area);
bool has_scale = (dsc->zoom != LV_IMG_ZOOM_NONE);
bool has_rotation = (dsc->angle != 0);
vg_lite_translate(dsc->pivot.x, dsc->pivot.y, &vgmatrix);
if(has_rotation)
vg_lite_rotate(dsc->angle / 10.0f, &vgmatrix); /* angle is 1/10 degree */
if(has_scale) {
vg_lite_float_t scale = 1.0f * dsc->zoom / LV_IMG_ZOOM_NONE;
vg_lite_scale(scale, scale, &vgmatrix);
}
vg_lite_translate(0.0f - dsc->pivot.x, 0.0f - dsc->pivot.y, &vgmatrix);
}
#if VG_LITE_BLIT_SPLIT_ENABLED
static void align_x(lv_area_t * area, lv_color_t ** buf)
{
int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE / sizeof(lv_color_t)));
VG_LITE_COND_STOP(alignedAreaStartPx < 0, "Negative X alignment.");
area->x1 -= alignedAreaStartPx;
area->x2 -= alignedAreaStartPx;
*buf += alignedAreaStartPx;
}
static void align_y(lv_area_t * area, lv_color_t ** buf, lv_coord_t stride)
{
int LineToAlignMem;
int alignedAreaStartPy;
/* find how many lines of pixels will respect memory alignment requirement */
if((stride % (lv_coord_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) == 0x0U) {
alignedAreaStartPy = area->y1;
}
else {
LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
VG_LITE_COND_STOP(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t)),
"Complex case: need gcd function.");
alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
VG_LITE_COND_STOP(alignedAreaStartPy < 0, "Negative Y alignment.");
}
area->y1 -= alignedAreaStartPy;
area->y2 -= alignedAreaStartPy;
*buf += (uint32_t)(alignedAreaStartPy * stride);
}
#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,168 @@
/**
* @file lv_draw_vglite_blend.h
*
*/
/**
* MIT License
*
* Copyright 2020-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_VGLITE_BLEND_H
#define LV_DRAW_VGLITE_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_utils.h"
/*********************
* DEFINES
*********************/
/**
* Enable BLIT quality degradation workaround for RT595,
* recommended for screen's dimension > 352 pixels.
*/
#define RT595_BLIT_WRKRND_ENABLED 1
/* Internal compound symbol */
#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \
defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \
RT595_BLIT_WRKRND_ENABLED
#define VG_LITE_BLIT_SPLIT_ENABLED 1
#else
#define VG_LITE_BLIT_SPLIT_ENABLED 0
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Fill area, with optional opacity.
*
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] color Color
* @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
*
* @retval LV_RES_OK Fill completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_fill(const lv_area_t * dest_area, lv_color_t color, lv_opa_t opa);
#if VG_LITE_BLIT_SPLIT_ENABLED
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects.
* By default, image is copied directly, with optional opacity.
*
* @param[in/out] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] opa Opacity
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa);
#else
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects.
* By default, image is copied directly, with optional opacity.
*
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] opa Opacity
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit(const lv_area_t * dest_area,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
lv_opa_t opa);
/**
* BLock Image Transfer - copy rectangular image from src_buf to dst_buf with transformation.
* By default, image is copied directly, with optional opacity.
*
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] clip_area Clip area with relative coordinates of destination buffer
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
* @param[in] dsc Image descriptor
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_blit_transform(const lv_area_t * dest_area, const lv_area_t * clip_area,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
const lv_draw_img_dsc_t * dsc);
#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
/**
* BLock Image Transfer - simple copy of rectangular image from source to destination.
*
* @param[in] dest_buf Destination buffer
* @param[in] dest_area Area with relative coordinates of destination buffer
* @param[in] dest_stride Stride of destination buffer in pixels
* @param[in] src_buf Source buffer
* @param[in] src_area Source area with relative coordinates of source buffer
* @param[in] src_stride Stride of source buffer in pixels
*
* @retval LV_RES_OK Transfer complete
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_BLEND_H*/

View File

@@ -0,0 +1,142 @@
/**
* @file lv_draw_vglite_line.c
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite_line.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_buf.h"
#include <math.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_gpu_nxp_vglite_draw_line(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip_area, const lv_draw_line_dsc_t * dsc)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
vg_lite_path_t path;
vg_lite_color_t vgcol; /* vglite takes ABGR */
vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
vg_lite_cap_style_t cap_style = (dsc->round_start || dsc->round_end) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT;
vg_lite_join_style_t join_style = (dsc->round_start || dsc->round_end) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER;
bool is_dashed = (dsc->dash_width && dsc->dash_gap);
vg_lite_float_t stroke_dash_pattern[2] = {0, 0};
uint32_t stroke_dash_count = 0;
vg_lite_float_t stroke_dash_phase = 0;
if(is_dashed) {
stroke_dash_pattern[0] = (vg_lite_float_t)dsc->dash_width;
stroke_dash_pattern[1] = (vg_lite_float_t)dsc->dash_gap;
stroke_dash_count = sizeof(stroke_dash_pattern) / sizeof(vg_lite_float_t);
stroke_dash_phase = (vg_lite_float_t)dsc->dash_width / 2;
}
/* Choose vglite blend mode based on given lvgl blend mode */
vg_lite_blend_t vglite_blend_mode = lv_vglite_get_blend_mode(dsc->blend_mode);
/*** Init path ***/
lv_coord_t width = dsc->width;
int32_t line_path[] = { /*VG line path*/
VLC_OP_MOVE, point1->x, point1->y,
VLC_OP_LINE, point2->x, point2->y,
VLC_OP_END
};
err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(line_path), line_path,
(vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
lv_color32_t col32 = { .full = lv_color_to32(dsc->color) }; /*Convert color to RGBA8888*/
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
if(lv_vglite_premult_and_swizzle(&vgcol, col32, dsc->opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
/*** Draw line ***/
err = vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH);
VG_LITE_ERR_RETURN_INV(err, "Set draw path type failed.");
err = vg_lite_set_stroke(&path, cap_style, join_style, width, 8, stroke_dash_pattern, stroke_dash_count,
stroke_dash_phase, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Set stroke failed.");
err = vg_lite_update_stroke(&path);
VG_LITE_ERR_RETURN_INV(err, "Update stroke failed.");
lv_vglite_set_scissor(clip_area);
err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, vglite_blend_mode, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Draw line failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
lv_vglite_disable_scissor();
err = vg_lite_clear_path(&path);
VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,83 @@
/**
* @file lv_draw_vglite_line.h
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_VGLITE_LINE_H
#define LV_DRAW_VGLITE_LINE_H
#ifdef __cplusplus
extern "C"
{
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_utils.h"
#include "../../lv_draw_line.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw line shape with effects
*
* @param[in] point1 Starting point with relative coordinates
* @param[in] point2 Ending point with relative coordinates
* @param[in] clip_area Clipping area with relative coordinates to dest buff
* @param[in] dsc Line description structure (width, rounded ending, opacity, ...)
*
* @retval LV_RES_OK Draw completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_gpu_nxp_vglite_draw_line(const lv_point_t * point1, const lv_point_t * point2,
const lv_area_t * clip_area, const lv_draw_line_dsc_t * dsc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_RECT_H*/

View File

@@ -0,0 +1,459 @@
/**
* @file lv_draw_vglite_rect.c
*
*/
/**
* MIT License
*
* Copyright 2021-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_vglite_rect.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_buf.h"
#include <math.h>
/*********************
* DEFINES
*********************/
/*********************
* DEFINES
*********************/
/* Path data sizes for different elements */
#define CUBIC_PATH_DATA_SIZE 7 /* 1 opcode, 6 arguments */
#define LINE_PATH_DATA_SIZE 3 /* 1 opcode, 2 arguments */
#define MOVE_PATH_DATA_SIZE 3 /* 1 opcode, 2 arguments */
#define END_PATH_DATA_SIZE 1 /* 1 opcode, 0 arguments */
/* Maximum possible rectangle path size
* is in the rounded rectangle case:
* - 1 move for the path start
* - 4 cubics for the corners
* - 4 lines for the sides
* - 1 end for the path end */
#define RECT_PATH_DATA_MAX_SIZE 1 * MOVE_PATH_DATA_SIZE + 4 * CUBIC_PATH_DATA_SIZE + 4 * LINE_PATH_DATA_SIZE + 1 * END_PATH_DATA_SIZE
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Generates path data for rectangle drawing.
*
* @param[in/out] path The path data to initialize
* @param[in/out] path_size The resulting size of the created path data
* @param[in] dsc The style descriptor for the rectangle to be drawn
* @param[in] coords The coordinates of the rectangle to be drawn
*/
static void lv_vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size,
lv_coord_t radius,
const lv_area_t * coords);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_gpu_nxp_vglite_draw_bg(const lv_area_t * coords, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * dsc)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
lv_coord_t width = lv_area_get_width(coords);
lv_coord_t height = lv_area_get_height(coords);
vg_lite_color_t vgcol;
lv_coord_t radius = dsc->radius;
vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
if(dsc->radius < 0)
return LV_RES_INV;
/*** Init path ***/
int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
uint32_t path_data_size;
lv_vglite_create_rect_path_data(path_data, &path_data_size, radius, coords);
vg_lite_quality_t path_quality = dsc->radius > 0 ? VG_LITE_HIGH : VG_LITE_LOW;
vg_lite_path_t path;
err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
(vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
vg_lite_matrix_t * grad_matrix;
vg_lite_linear_gradient_t gradient;
/*** Init Color/Gradient ***/
if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
uint32_t colors[2];
uint32_t stops[2];
lv_color32_t col32[2];
/* Gradient setup */
uint8_t cnt = LV_MAX(dsc->bg_grad.stops_count, 2);
for(uint8_t i = 0; i < cnt; i++) {
col32[i].full = lv_color_to32(dsc->bg_grad.stops[i].color); /*Convert color to RGBA8888*/
stops[i] = dsc->bg_grad.stops[i].frac;
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_ABGR8888 : VG_LITE_ARGB8888;
if(lv_vglite_premult_and_swizzle(&colors[i], col32[i], dsc->bg_opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
}
lv_memset_00(&gradient, sizeof(vg_lite_linear_gradient_t));
err = vg_lite_init_grad(&gradient);
VG_LITE_ERR_RETURN_INV(err, "Init gradient failed");
err = vg_lite_set_grad(&gradient, cnt, colors, stops);
VG_LITE_ERR_RETURN_INV(err, "Set gradient failed.");
err = vg_lite_update_grad(&gradient);
VG_LITE_ERR_RETURN_INV(err, "Update gradient failed.");
grad_matrix = vg_lite_get_grad_matrix(&gradient);
vg_lite_identity(grad_matrix);
vg_lite_translate((float)coords->x1, (float)coords->y1, grad_matrix);
if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_VER) {
vg_lite_scale(1.0f, (float)height / 256.0f, grad_matrix);
vg_lite_rotate(90.0f, grad_matrix);
}
else { /*LV_GRAD_DIR_HOR*/
vg_lite_scale((float)width / 256.0f, 1.0f, grad_matrix);
}
}
lv_color32_t bg_col32 = {.full = lv_color_to32(dsc->bg_color)}; /*Convert color to RGBA8888*/
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
if(lv_vglite_premult_and_swizzle(&vgcol, bg_col32, dsc->bg_opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
lv_vglite_set_scissor(clip_area);
/*** Draw rectangle ***/
if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
}
else {
err = vg_lite_draw_gradient(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, &gradient, VG_LITE_BLEND_SRC_OVER);
}
VG_LITE_ERR_RETURN_INV(err, "Draw gradient failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
lv_vglite_disable_scissor();
err = vg_lite_clear_path(&path);
VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
err = vg_lite_clear_grad(&gradient);
VG_LITE_ERR_RETURN_INV(err, "Clear gradient failed.");
}
return LV_RES_OK;
}
lv_res_t lv_gpu_nxp_vglite_draw_border_generic(const lv_area_t * coords, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * dsc, bool border)
{
vg_lite_error_t err = VG_LITE_SUCCESS;
vg_lite_color_t vgcol; /* vglite takes ABGR */
lv_coord_t radius = dsc->radius;
vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
if(radius < 0)
return LV_RES_INV;
if(border) {
/* Draw border - only has radius if object has radius*/
lv_coord_t border_half = (lv_coord_t)floor(dsc->border_width / 2.0f);
if(radius > border_half)
radius = radius - border_half;
}
else {
/* Draw outline - always has radius, leave the same radius in the circle case */
lv_coord_t outline_half = (lv_coord_t)ceil(dsc->outline_width / 2.0f);
if(radius < (lv_coord_t)LV_RADIUS_CIRCLE - outline_half)
radius = radius + outline_half;
}
vg_lite_cap_style_t cap_style = (radius) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT;
vg_lite_join_style_t join_style = (radius) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER;
/* Choose vglite blend mode based on given lvgl blend mode */
vg_lite_blend_t vglite_blend_mode = lv_vglite_get_blend_mode(dsc->blend_mode);
/*** Init path ***/
int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
uint32_t path_data_size;
lv_vglite_create_rect_path_data(path_data, &path_data_size, radius, coords);
vg_lite_quality_t path_quality = dsc->radius > 0 ? VG_LITE_HIGH : VG_LITE_LOW;
vg_lite_path_t path;
err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
(vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
vg_lite_matrix_t matrix;
vg_lite_identity(&matrix);
lv_opa_t opa;
lv_color32_t col32;
lv_coord_t line_width;
if(border) {
opa = dsc->border_opa;
col32.full = lv_color_to32(dsc->border_color); /*Convert color to RGBA8888*/
line_width = dsc->border_width;
}
else {
opa = dsc->outline_opa;
col32.full = lv_color_to32(dsc->outline_color); /*Convert color to RGBA8888*/
line_width = dsc->outline_width;
}
vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
if(lv_vglite_premult_and_swizzle(&vgcol, col32, opa, color_format) != LV_RES_OK)
VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
/*** Draw border ***/
err = vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH);
VG_LITE_ERR_RETURN_INV(err, "Set draw path type failed.");
err = vg_lite_set_stroke(&path, cap_style, join_style, line_width, 8, NULL, 0, 0, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Set stroke failed.");
err = vg_lite_update_stroke(&path);
VG_LITE_ERR_RETURN_INV(err, "Update stroke failed.");
lv_vglite_set_scissor(clip_area);
err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, vglite_blend_mode, vgcol);
VG_LITE_ERR_RETURN_INV(err, "Draw border failed.");
if(lv_vglite_run() != LV_RES_OK)
VG_LITE_RETURN_INV("Run failed.");
lv_vglite_disable_scissor();
err = vg_lite_clear_path(&path);
VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size,
lv_coord_t radius,
const lv_area_t * coords)
{
lv_coord_t rect_width = lv_area_get_width(coords);
lv_coord_t rect_height = lv_area_get_height(coords);
/* Get the final radius. Can't be larger than the half of the shortest side */
int32_t shortest_side = LV_MIN(rect_width, rect_height);
int32_t final_radius = LV_MIN(radius, shortest_side / 2);
/* Path data element index */
uint8_t pidx = 0;
if((radius == (lv_coord_t)LV_RADIUS_CIRCLE) && (rect_width == rect_height)) {
/* Get the control point offset for rounded cases */
int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE);
/* Circle case */
/* Starting point */
path_data[pidx++] = VLC_OP_MOVE;
path_data[pidx++] = coords->x1 + final_radius;
path_data[pidx++] = coords->y1;
/* Top-right arc */
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = cpoff;
path_data[pidx++] = 0;
path_data[pidx++] = final_radius;
path_data[pidx++] = final_radius - cpoff;
path_data[pidx++] = final_radius;
path_data[pidx++] = final_radius;
/* Bottom-right arc*/
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0;
path_data[pidx++] = cpoff;
path_data[pidx++] = cpoff - final_radius;
path_data[pidx++] = final_radius;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = final_radius;
/* Bottom-left arc */
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0 - cpoff;
path_data[pidx++] = 0;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = cpoff - final_radius;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = 0 - final_radius;
/* Top-left arc*/
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0;
path_data[pidx++] = 0 - cpoff;
path_data[pidx++] = final_radius - cpoff;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = final_radius;
path_data[pidx++] = 0 - final_radius;
/* Ending point */
path_data[pidx++] = VLC_OP_END;
}
else if(radius > 0) {
/* Get the control point offset for rounded cases */
int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE);
/* Rounded rectangle case */
/* Starting point */
path_data[pidx++] = VLC_OP_MOVE;
path_data[pidx++] = coords->x1 + final_radius;
path_data[pidx++] = coords->y1;
/* Top side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x2 - final_radius + 1; // Extended for VGLite
path_data[pidx++] = coords->y1;
/* Top-right corner */
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = cpoff;
path_data[pidx++] = 0;
path_data[pidx++] = final_radius;
path_data[pidx++] = final_radius - cpoff;
path_data[pidx++] = final_radius;
path_data[pidx++] = final_radius;
/* Right side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x2 + 1; // Extended for VGLite
path_data[pidx++] = coords->y2 - final_radius + 1; // Extended for VGLite
/* Bottom-right corner*/
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0;
path_data[pidx++] = cpoff;
path_data[pidx++] = cpoff - final_radius;
path_data[pidx++] = final_radius;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = final_radius;
/* Bottom side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x1 + final_radius;
path_data[pidx++] = coords->y2 + 1; // Extended for VGLite
/* Bottom-left corner */
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0 - cpoff;
path_data[pidx++] = 0;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = cpoff - final_radius;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = 0 - final_radius;
/* Left side*/
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x1;
path_data[pidx++] = coords->y1 + final_radius;
/* Top-left corner */
path_data[pidx++] = VLC_OP_CUBIC_REL;
path_data[pidx++] = 0;
path_data[pidx++] = 0 - cpoff;
path_data[pidx++] = final_radius - cpoff;
path_data[pidx++] = 0 - final_radius;
path_data[pidx++] = final_radius;
path_data[pidx++] = 0 - final_radius;
/* Ending point */
path_data[pidx++] = VLC_OP_END;
}
else {
/* Non-rounded rectangle case */
/* Starting point */
path_data[pidx++] = VLC_OP_MOVE;
path_data[pidx++] = coords->x1;
path_data[pidx++] = coords->y1;
/* Top side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x2 + 1; // Extended for VGLite
path_data[pidx++] = coords->y1;
/* Right side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x2 + 1; // Extended for VGLite
path_data[pidx++] = coords->y2 + 1; // Extended for VGLite
/* Bottom side */
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x1;
path_data[pidx++] = coords->y2 + 1; // Extended for VGLite
/* Left side*/
path_data[pidx++] = VLC_OP_LINE;
path_data[pidx++] = coords->x1;
path_data[pidx++] = coords->y1;
/* Ending point */
path_data[pidx++] = VLC_OP_END;
}
/* Resulting path size */
*path_data_size = pidx * sizeof(int32_t);
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,97 @@
/**
* @file lv_draw_vglite_rect.h
*
*/
/**
* MIT License
*
* Copyright 2021-2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_DRAW_VGLITE_RECT_H
#define LV_DRAW_VGLITE_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_utils.h"
#include "../../lv_draw_rect.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw rectangle background with effects (rounded corners, gradient)
*
* @param[in] coords Coordinates of the rectangle background (relative to dest buff)
* @param[in] clip_area Clipping area with relative coordinates to dest buff
* @param[in] dsc Description of the rectangle background
*
* @retval LV_RES_OK Draw completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*
*/
lv_res_t lv_gpu_nxp_vglite_draw_bg(const lv_area_t * coords, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * dsc);
/**
* Draw rectangle border/outline shape with effects (rounded corners, opacity)
*
* @param[in] coords Coordinates of the rectangle border/outline (relative to dest buff)
* @param[in] clip_area Clipping area with relative coordinates to dest buff
* @param[in] dsc Description of the rectangle border/outline
* @param[in] border True for border, False for outline
*
* @retval LV_RES_OK Draw completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*
*/
lv_res_t lv_gpu_nxp_vglite_draw_border_generic(const lv_area_t * coords, const lv_area_t * clip_area,
const lv_draw_rect_dsc_t * dsc, bool border);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_VGLITE_RECT_H*/

View File

@@ -0,0 +1,141 @@
/**
* @file lv_vglite_buf.c
*
*/
/**
* MIT License
*
* Copyright 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_vglite_buf.h"
#if LV_USE_GPU_NXP_VG_LITE
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH == 16
#define VG_LITE_PX_FMT VG_LITE_RGB565
#elif LV_COLOR_DEPTH == 32
#define VG_LITE_PX_FMT VG_LITE_BGRA8888
#else
#error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32.
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static inline void lv_vglite_set_dest_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride);
static inline void lv_vglite_set_buf_ptr(vg_lite_buffer_t * vgbuf, const lv_color_t * buf);
/**********************
* STATIC VARIABLES
**********************/
static vg_lite_buffer_t dest_vgbuf;
static vg_lite_buffer_t src_vgbuf;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gpu_nxp_vglite_init_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride)
{
lv_vglite_set_dest_buf(buf, area, stride);
}
vg_lite_buffer_t * lv_vglite_get_dest_buf(void)
{
return &dest_vgbuf;
}
vg_lite_buffer_t * lv_vglite_get_src_buf(void)
{
return &src_vgbuf;
}
void lv_vglite_set_dest_buf_ptr(const lv_color_t * buf)
{
lv_vglite_set_buf_ptr(&dest_vgbuf, buf);
}
void lv_vglite_set_src_buf_ptr(const lv_color_t * buf)
{
lv_vglite_set_buf_ptr(&src_vgbuf, buf);
}
void lv_vglite_set_src_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride)
{
if(src_vgbuf.memory != (void *)buf)
lv_vglite_set_buf(&src_vgbuf, buf, area, stride);
}
void lv_vglite_set_buf(vg_lite_buffer_t * vgbuf, const lv_color_t * buf,
const lv_area_t * area, lv_coord_t stride)
{
vgbuf->format = VG_LITE_PX_FMT;
vgbuf->tiled = VG_LITE_LINEAR;
vgbuf->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
vgbuf->transparency_mode = VG_LITE_IMAGE_OPAQUE;
vgbuf->width = (int32_t)lv_area_get_width(area);
vgbuf->height = (int32_t)lv_area_get_height(area);
vgbuf->stride = (int32_t)(stride) * sizeof(lv_color_t);
lv_memset_00(&vgbuf->yuv, sizeof(vgbuf->yuv));
vgbuf->memory = (void *)buf;
vgbuf->address = (uint32_t)vgbuf->memory;
vgbuf->handle = NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline void lv_vglite_set_dest_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride)
{
lv_vglite_set_buf(&dest_vgbuf, buf, area, stride);
}
static inline void lv_vglite_set_buf_ptr(vg_lite_buffer_t * vgbuf, const lv_color_t * buf)
{
vgbuf->memory = (void *)buf;
vgbuf->address = (uint32_t)vgbuf->memory;
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,124 @@
/**
* @file lv_vglite_buf.h
*
*/
/**
* MIT License
*
* Copyright 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_VGLITE_BUF_H
#define LV_VGLITE_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "vg_lite.h"
#include "../../sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init vglite destination buffer. It will be done once per frame.
*
* @param[in] buf Destination buffer address (does not require alignment for VG_LITE_LINEAR mode)
* @param[in] area Destination buffer area (for width and height)
* @param[in] stride Stride of destination buffer
*/
void lv_gpu_nxp_vglite_init_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride);
/**
* Get vglite destination buffer pointer.
*
* @retval The vglite destination buffer
*/
vg_lite_buffer_t * lv_vglite_get_dest_buf(void);
/**
* Get vglite source buffer pointer.
*
* @retval The vglite source buffer
*/
vg_lite_buffer_t * lv_vglite_get_src_buf(void);
/**
* Set vglite destination buffer address only.
*
* @param[in] buf Destination buffer address (does not require alignment for VG_LITE_LINEAR mode)
*/
void lv_vglite_set_dest_buf_ptr(const lv_color_t * buf);
/**
* Set vglite source buffer address only.
*
* @param[in] buf Source buffer address
*/
void lv_vglite_set_src_buf_ptr(const lv_color_t * buf);
/**
* Set vglite source buffer. It will be done only if buffer addreess is different.
*
* @param[in] buf Source buffer address
* @param[in] area Source buffer area (for width and height)
* @param[in] stride Stride of source buffer
*/
void lv_vglite_set_src_buf(const lv_color_t * buf, const lv_area_t * area, lv_coord_t stride);
/**
* Set vglite buffer.
*
* @param[in] vgbuf Address of the VGLite buffer object
* @param[in] buf Address of the memory for the VGLite buffer
* @param[in] area buffer area (for width and height)
* @param[in] stride buffer stride
*/
void lv_vglite_set_buf(vg_lite_buffer_t * vgbuf, const lv_color_t * buf,
const lv_area_t * area, lv_coord_t stride);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_VGLITE_BUF_H*/

View File

@@ -0,0 +1,149 @@
/**
* @file lv_vglite_utils.c
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_vglite_utils.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "../../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Clean and invalidate cache.
*/
static inline void invalidate_cache(void);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_vglite_run(void)
{
invalidate_cache();
VG_LITE_ERR_RETURN_INV(vg_lite_flush(), "Flush failed.");
return LV_RES_OK;
}
lv_res_t lv_vglite_premult_and_swizzle(vg_lite_color_t * vg_col32, lv_color32_t lv_col32, lv_opa_t opa,
vg_lite_buffer_format_t vg_col_format)
{
lv_color32_t lv_col32_premul = lv_col32;
if(opa <= (lv_opa_t)LV_OPA_MAX) {
/* Only pre-multiply color if hardware pre-multiplication is not present */
if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
lv_col32_premul.ch.red = (uint8_t)(((uint16_t)lv_col32.ch.red * opa) >> 8);
lv_col32_premul.ch.green = (uint8_t)(((uint16_t)lv_col32.ch.green * opa) >> 8);
lv_col32_premul.ch.blue = (uint8_t)(((uint16_t)lv_col32.ch.blue * opa) >> 8);
}
lv_col32_premul.ch.alpha = opa;
}
switch(vg_col_format) {
case VG_LITE_BGRA8888:
*vg_col32 = lv_col32_premul.full;
break;
case VG_LITE_RGBA8888:
*vg_col32 = ((uint32_t)lv_col32_premul.ch.red << 24) | ((uint32_t)lv_col32_premul.ch.green << 16) |
((uint32_t)lv_col32_premul.ch.blue << 8) | (uint32_t)lv_col32_premul.ch.alpha;
break;
case VG_LITE_ABGR8888:
*vg_col32 = ((uint32_t)lv_col32_premul.ch.alpha << 24) | ((uint32_t)lv_col32_premul.ch.blue << 16) |
((uint32_t)lv_col32_premul.ch.green << 8) | (uint32_t)lv_col32_premul.ch.red;
break;
case VG_LITE_ARGB8888:
*vg_col32 = ((uint32_t)lv_col32_premul.ch.alpha << 24) | ((uint32_t)lv_col32_premul.ch.red << 16) |
((uint32_t)lv_col32_premul.ch.green << 8) | (uint32_t)lv_col32_premul.ch.blue;
break;
default:
return LV_RES_INV;
}
return LV_RES_OK;
}
vg_lite_blend_t lv_vglite_get_blend_mode(lv_blend_mode_t lv_blend_mode)
{
vg_lite_blend_t vg_blend_mode;
switch(lv_blend_mode) {
case LV_BLEND_MODE_ADDITIVE:
vg_blend_mode = VG_LITE_BLEND_ADDITIVE;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
vg_blend_mode = VG_LITE_BLEND_SUBTRACT;
break;
case LV_BLEND_MODE_MULTIPLY:
vg_blend_mode = VG_LITE_BLEND_MULTIPLY;
break;
case LV_BLEND_MODE_REPLACE:
vg_blend_mode = VG_LITE_BLEND_NONE;
break;
default:
vg_blend_mode = VG_LITE_BLEND_SRC_OVER;
break;
}
return vg_blend_mode;
}
/**********************
* STATIC FUNCTIONS
**********************/
static inline void invalidate_cache(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->clean_dcache_cb)
disp->driver->clean_dcache_cb(disp->driver);
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/

View File

@@ -0,0 +1,200 @@
/**
* @file lv_vglite_utils.h
*
*/
/**
* MIT License
*
* Copyright 2022, 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph)
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef LV_VGLITE_UTILS_H
#define LV_VGLITE_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_GPU_NXP_VG_LITE
#include "vg_lite.h"
#include "../../sw/lv_draw_sw.h"
#include "../../../misc/lv_log.h"
/*********************
* DEFINES
*********************/
#ifndef LV_GPU_NXP_VG_LITE_LOG_ERRORS
/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/
#define LV_GPU_NXP_VG_LITE_LOG_ERRORS 1
#endif
#ifndef LV_GPU_NXP_VG_LITE_LOG_TRACES
/** Enable logging of VG-Lite traces (\see LV_LOG_ERROR)*/
#define LV_GPU_NXP_VG_LITE_LOG_TRACES 0
#endif
/* The optimal Bezier control point offset for radial unit
* see: https://spencermortensen.com/articles/bezier-circle/
**/
#define BEZIER_OPTIM_CIRCLE 0.551915024494f
/* Draw lines for control points of Bezier curves */
#define BEZIER_DBG_CONTROL_POINTS 0
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* Enable scissor and set the clipping box.
*
* @param[in] clip_area Clip area with relative coordinates of destination buffer
*/
static inline void lv_vglite_set_scissor(const lv_area_t * clip_area);
/**
* Disable scissor.
*/
static inline void lv_vglite_disable_scissor(void);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Premultiplies and swizzles given LVGL 32bit color to obtain vglite color.
*
* @param[in/out] vg_col32 The obtained vglite color
* @param[in] lv_col32 The initial LVGL 32bit color
* @param[in] opa The opacity to premultiply with
* @param[in] vg_col_format The format of the resulting vglite color
*
* @retval LV_RES_OK Operation completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_vglite_premult_and_swizzle(vg_lite_color_t * vg_col32, lv_color32_t lv_col32, lv_opa_t opa,
vg_lite_buffer_format_t vg_col_format);
/**
* Get vglite blend mode.
*
* @param[in] lv_blend_mode The LVGL blend mode
*
* @retval The vglite blend mode
*/
vg_lite_blend_t lv_vglite_get_blend_mode(lv_blend_mode_t lv_blend_mode);
/**
* Clear cache and flush command to VG-Lite.
*
* @retval LV_RES_OK Run completed
* @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
*/
lv_res_t lv_vglite_run(void);
/**********************
* MACROS
**********************/
#define VG_LITE_COND_STOP(cond, txt) \
do { \
if (cond) { \
LV_LOG_ERROR("%s. STOP!", txt); \
for ( ; ; ); \
} \
} while(0)
#if LV_GPU_NXP_VG_LITE_LOG_ERRORS
#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \
do { \
if(err != VG_LITE_SUCCESS) { \
LV_LOG_ERROR(fmt" (err = %d)", \
err, ##__VA_ARGS__); \
return LV_RES_INV; \
} \
} while (0)
#else
#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \
do { \
if(err != VG_LITE_SUCCESS) { \
return LV_RES_INV; \
} \
}while(0)
#endif /*LV_GPU_NXP_VG_LITE_LOG_ERRORS*/
#if LV_GPU_NXP_VG_LITE_LOG_TRACES
#define VG_LITE_LOG_TRACE(fmt, ...) \
do { \
LV_LOG(fmt, ##__VA_ARGS__); \
} while (0)
#define VG_LITE_RETURN_INV(fmt, ...) \
do { \
LV_LOG_ERROR(fmt, ##__VA_ARGS__); \
return LV_RES_INV; \
} while (0)
#else
#define VG_LITE_LOG_TRACE(fmt, ...) \
do { \
} while (0)
#define VG_LITE_RETURN_INV(fmt, ...) \
do { \
return LV_RES_INV; \
}while(0)
#endif /*LV_GPU_NXP_VG_LITE_LOG_TRACES*/
/**********************
* STATIC FUNCTIONS
**********************/
static inline void lv_vglite_set_scissor(const lv_area_t * clip_area)
{
vg_lite_enable_scissor();
vg_lite_set_scissor((int32_t)clip_area->x1, (int32_t)clip_area->y1,
(int32_t)lv_area_get_width(clip_area),
(int32_t)lv_area_get_height(clip_area));
}
static inline void lv_vglite_disable_scissor(void)
{
vg_lite_disable_scissor();
}
#endif /*LV_USE_GPU_NXP_VG_LITE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_VGLITE_UTILS_H*/

View File

@@ -0,0 +1,7 @@
CSRCS += lv_gpu_d2_ra6m3.c
CSRCS += lv_gpu_d2_draw_label.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/renesas
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/renesas
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/renesas"

View File

@@ -0,0 +1,292 @@
/**
* @file lv_gpu_d2_draw_label.c
*
* @description HAL layer for display driver
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_draw_label.h"
#include "../../misc/lv_assert.h"
#include "../../core/lv_refr.h"
#include "lv_gpu_d2_ra6m3.h"
#if LV_USE_GPU_RA6M3_G2D
#include LV_GPU_RA6M3_G2D_INCLUDE
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
extern const uint8_t _lv_bpp1_opa_table[2];
extern const uint8_t _lv_bpp2_opa_table[4];
extern const uint8_t _lv_bpp4_opa_table[16];
extern const uint8_t _lv_bpp8_opa_table[256];
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void LV_ATTRIBUTE_FAST_MEM draw_letter_normal(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,
const lv_point_t * pos, lv_font_glyph_dsc_t * g, const uint8_t * map_p)
{
const uint8_t * bpp_opa_table_p;
uint32_t bitmask_init;
uint32_t bitmask;
uint32_t bpp = g->bpp;
lv_opa_t opa = dsc->opa;
uint32_t shades;
if(bpp == 3) bpp = 4;
#if LV_USE_IMGFONT
if(bpp == LV_IMGFONT_BPP) { //is imgfont
lv_area_t fill_area;
fill_area.x1 = pos->x;
fill_area.y1 = pos->y;
fill_area.x2 = pos->x + g->box_w - 1;
fill_area.y2 = pos->y + g->box_h - 1;
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
img_dsc.angle = 0;
img_dsc.zoom = LV_IMG_ZOOM_NONE;
img_dsc.opa = dsc->opa;
img_dsc.blend_mode = dsc->blend_mode;
lv_draw_img(draw_ctx, &img_dsc, &fill_area, map_p);
return;
}
#endif
switch(bpp) {
case 1:
bpp_opa_table_p = _lv_bpp1_opa_table;
bitmask_init = 0x80;
shades = 2;
break;
case 2:
bpp_opa_table_p = _lv_bpp2_opa_table;
bitmask_init = 0xC0;
shades = 4;
break;
case 4:
bpp_opa_table_p = _lv_bpp4_opa_table;
bitmask_init = 0xF0;
shades = 16;
break;
case 8:
bpp_opa_table_p = _lv_bpp8_opa_table;
bitmask_init = 0xFF;
shades = 256;
break; /*No opa table, pixel value will be used directly*/
default:
LV_LOG_WARN("lv_draw_letter: invalid bpp");
return; /*Invalid bpp. Can't render the letter*/
}
static lv_opa_t opa_table[256];
static lv_opa_t prev_opa = LV_OPA_TRANSP;
static uint32_t prev_bpp = 0;
if(opa < LV_OPA_MAX) {
if(prev_opa != opa || prev_bpp != bpp) {
uint32_t i;
for(i = 0; i < shades; i++) {
opa_table[i] = bpp_opa_table_p[i] == LV_OPA_COVER ? opa : ((bpp_opa_table_p[i] * opa) >> 8);
}
}
bpp_opa_table_p = opa_table;
prev_opa = opa;
prev_bpp = bpp;
}
int32_t col, row;
int32_t box_w = g->box_w;
int32_t box_h = g->box_h;
int32_t width_bit = box_w * bpp; /*Letter width in bits*/
/*Calculate the col/row start/end on the map*/
int32_t col_start = pos->x >= draw_ctx->clip_area->x1 ? 0 : draw_ctx->clip_area->x1 - pos->x;
int32_t col_end = pos->x + box_w <= draw_ctx->clip_area->x2 ? box_w : draw_ctx->clip_area->x2 - pos->x + 1;
int32_t row_start = pos->y >= draw_ctx->clip_area->y1 ? 0 : draw_ctx->clip_area->y1 - pos->y;
int32_t row_end = pos->y + box_h <= draw_ctx->clip_area->y2 ? box_h : draw_ctx->clip_area->y2 - pos->y + 1;
/*Move on the map too*/
uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp);
map_p += bit_ofs >> 3;
uint8_t letter_px;
uint32_t col_bit;
col_bit = bit_ofs & 0x7; /*"& 0x7" equals to "% 8" just faster*/
lv_draw_sw_blend_dsc_t blend_dsc;
lv_memset_00(&blend_dsc, sizeof(blend_dsc));
blend_dsc.color = dsc->color;
blend_dsc.opa = LV_OPA_COVER;
blend_dsc.blend_mode = dsc->blend_mode;
lv_coord_t hor_res = lv_disp_get_hor_res(_lv_refr_get_disp_refreshing());
uint32_t mask_buf_size = box_w * box_h > hor_res ? hor_res : box_w * box_h;
lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size);
blend_dsc.mask_buf = mask_buf;
int32_t mask_p = 0;
lv_area_t fill_area;
fill_area.x1 = col_start + pos->x;
fill_area.x2 = col_end + pos->x - 1;
fill_area.y1 = row_start + pos->y;
fill_area.y2 = fill_area.y1;
#if LV_DRAW_COMPLEX
lv_coord_t fill_w = lv_area_get_width(&fill_area);
lv_area_t mask_area;
lv_area_copy(&mask_area, &fill_area);
mask_area.y2 = mask_area.y1 + row_end;
bool mask_any = lv_draw_mask_is_any(&mask_area);
#endif
blend_dsc.blend_area = &fill_area;
blend_dsc.mask_area = &fill_area;
uint32_t col_bit_max = 8 - bpp;
uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp;
for(row = row_start ; row < row_end; row++) {
#if LV_DRAW_COMPLEX
int32_t mask_p_start = mask_p;
#endif
bitmask = bitmask_init >> col_bit;
for(col = col_start; col < col_end; col++) {
/*Load the pixel's opacity into the mask*/
letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit);
if(letter_px) {
mask_buf[mask_p] = bpp_opa_table_p[letter_px];
}
else {
mask_buf[mask_p] = 0;
}
/*Go to the next column*/
if(col_bit < col_bit_max) {
col_bit += bpp;
bitmask = bitmask >> bpp;
}
else {
col_bit = 0;
bitmask = bitmask_init;
map_p++;
}
/*Next mask byte*/
mask_p++;
}
#if LV_DRAW_COMPLEX
/*Apply masks if any*/
if(mask_any) {
blend_dsc.mask_res = lv_draw_mask_apply(mask_buf + mask_p_start, fill_area.x1, fill_area.y2,
fill_w);
if(blend_dsc.mask_res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf + mask_p_start, fill_w);
}
}
#endif
if((uint32_t) mask_p + (col_end - col_start) < mask_buf_size) {
fill_area.y2 ++;
}
else {
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
lv_draw_ra6m3_2d_blend(draw_ctx, &blend_dsc);
fill_area.y1 = fill_area.y2 + 1;
fill_area.y2 = fill_area.y1;
mask_p = 0;
}
col_bit += col_bit_row_ofs;
map_p += (col_bit >> 3);
col_bit = col_bit & 0x7;
}
/*Flush the last part*/
if(fill_area.y1 != fill_area.y2) {
fill_area.y2--;
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
lv_draw_ra6m3_2d_blend(draw_ctx, &blend_dsc);
mask_p = 0;
}
lv_mem_buf_release(mask_buf);
}
void lv_draw_gpu_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter)
{
const lv_font_t * font_p = dsc->font;
lv_opa_t opa = dsc->opa;
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("lv_draw_letter: font is NULL");
return;
}
lv_font_glyph_dsc_t g;
bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found
*but do not print warning for non printable ASCII chars (e.g. '\n')*/
if(letter >= 0x20 &&
letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter);
}
return;
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) return;
lv_point_t gpos;
gpos.x = pos_p->x + g.ofs_x;
gpos.y = pos_p->y + (dsc->font->line_height - dsc->font->base_line) - g.box_h - g.ofs_y;
/*If the letter is completely out of mask don't draw it*/
if(gpos.x + g.box_w < draw_ctx->clip_area->x1 ||
gpos.x > draw_ctx->clip_area->x2 ||
gpos.y + g.box_h < draw_ctx->clip_area->y1 ||
gpos.y > draw_ctx->clip_area->y2) {
return;
}
const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
if(map_p == NULL) {
LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
return;
}
if(font_p->subpx) {
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
draw_letter_subpx(pos_x, pos_y, &g, clip_area, map_p, color, opa, blend_mode);
#else
LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h");
#endif
}
else {
draw_letter_normal(draw_ctx, dsc, &gpos, &g, map_p);
}
}
#endif

View File

@@ -0,0 +1,742 @@
/**
* @file lv_gpu_d2_ra6m3.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_d2_ra6m3.h"
#include "../../core/lv_refr.h"
#include <math.h>
#if LV_USE_GPU_RA6M3_G2D
#include LV_GPU_RA6M3_G2D_INCLUDE
/*********************
* DEFINES
*********************/
#define LOG_ERRORS
#ifdef LOG_ERRORS
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define ERROR_LIST_SIZE (4)
#define D2_EXEC(a) lv_port_gpu_log_error(a, __func__, __LINE__)
#else
/* here is error logging not enabled */
#define D2_EXEC(a) a;
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
d2_s32 error;
const char * func;
int line;
} log_error_entry;
/**********************
* STATIC PROTOTYPES
**********************/
#ifdef LOG_ERRORS
static void lv_port_gpu_log_error(d2_s32 status, const char * func, int line);
#endif
static void invalidate_cache(void);
void lv_draw_gpu_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
/**********************
* STATIC VARIABLES
**********************/
#ifdef LOG_ERRORS
static log_error_entry log_error_list[ERROR_LIST_SIZE];
static int error_list_index;
static int error_count;
#endif
static d2_device * _d2_handle;
static d2_renderbuffer * renderbuffer;
static d2_s32 src_cf_val, dst_cf_val;
static lv_draw_img_dsc_t img_dsc;
static bool color_key_enabled, alpha_enabled, blend_enabled, colorize_enabled;
/**********************
* STATIC FUNCTIONS
**********************/
static d2_s32 lv_port_gpu_cf_lv_to_d2(lv_img_cf_t cf)
{
d2_s32 d2_cf;
#if (DLG_LVGL_CF == 1)
switch(cf & ~(1 << 5)) {
#else
switch(cf) {
#endif /* (DLG_LVGL_CF == 1) */
case LV_IMG_CF_TRUE_COLOR:
d2_cf = d2_mode_rgb565;
break;
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
d2_cf = d2_mode_rgb565;
break;
case LV_IMG_CF_ALPHA_1BIT:
d2_cf = d2_mode_alpha1;
break;
case LV_IMG_CF_ALPHA_2BIT:
d2_cf = d2_mode_alpha2;
break;
case LV_IMG_CF_ALPHA_4BIT:
d2_cf = d2_mode_alpha4;
break;
case LV_IMG_CF_ALPHA_8BIT:
d2_cf = d2_mode_alpha8;
break;
case LV_IMG_CF_INDEXED_1BIT:
d2_cf = d2_mode_i1 | d2_mode_clut;
break;
case LV_IMG_CF_INDEXED_2BIT:
d2_cf = d2_mode_i2 | d2_mode_clut;
break;
case LV_IMG_CF_INDEXED_4BIT:
d2_cf = d2_mode_i4 | d2_mode_clut;
break;
case LV_IMG_CF_INDEXED_8BIT:
d2_cf = d2_mode_i8 | d2_mode_clut;
break;
#if (DLG_LVGL_CF == 1)
case LV_IMG_CF_RGB565:
d2_cf = d2_mode_rgb565;
break;
case LV_IMG_CF_RGB888:
d2_cf = d2_mode_rgb888;
break;
case LV_IMG_CF_RGBA8888:
d2_cf = d2_mode_rgba8888;
break;
#endif /* DLG_LVGL_CF */
default:
return -1;
}
#if (DLG_LVGL_CF == 1)
return d2_cf | (cf & (1 << 5) ? d2_mode_rle : 0);
#else
return d2_cf;
#endif /* (DLG_LVGL_CF == 1) */
}
static bool lv_port_gpu_cf_fb_valid(d2_s32 cf)
{
if((cf & (d2_mode_rle | d2_mode_clut)) || cf < 0) {
return false;
}
switch(cf) {
case d2_mode_alpha8:
case d2_mode_rgb565:
case d2_mode_argb8888:
case d2_mode_argb4444:
case d2_mode_rgba8888:
case d2_mode_rgba4444:
return true;
default:
return false;
}
}
static bool lv_port_gpu_cf_has_alpha(d2_s32 cf)
{
switch(cf & ~(d2_mode_clut | d2_mode_rle)) {
case d2_mode_argb8888:
case d2_mode_rgba8888:
case d2_mode_argb4444:
case d2_mode_rgba4444:
case d2_mode_argb1555:
case d2_mode_rgba5551:
case d2_mode_ai44:
case d2_mode_i8:
case d2_mode_i4:
case d2_mode_i2:
case d2_mode_i1:
case d2_mode_alpha8:
case d2_mode_alpha4:
case d2_mode_alpha2:
case d2_mode_alpha1:
return true;
default:
return false;
}
}
static bool lv_port_gpu_cf_is_alpha(d2_s32 cf)
{
switch(cf & ~d2_mode_rle) {
case d2_mode_alpha8:
case d2_mode_alpha4:
case d2_mode_alpha2:
case d2_mode_alpha1:
return true;
default:
return false;
}
}
static d2_color lv_port_gpu_color_lv_to_d2(lv_color_t color)
{
uint8_t alpha, red, green, blue;
alpha = 0xFF;
red = color.ch.red << 3 | color.ch.red >> 2;
green = color.ch.green << 2 | color.ch.green >> 4;
blue = color.ch.blue << 3 | color.ch.blue >> 2;
return (alpha) << 24UL
| (red) << 16UL
| (green) << 8UL
| (blue) << 0UL;
}
static void lv_port_gpu_get_recolor_consts(d2_color * cl, d2_color * ch)
{
d2_color c = lv_port_gpu_color_lv_to_d2(img_dsc.recolor);
d2_alpha r, g, b, opa = img_dsc.recolor_opa > LV_OPA_MAX ? LV_OPA_COVER : img_dsc.recolor_opa;
r = ((uint32_t)((uint8_t)(c >> 16UL)) * opa) / 255;
g = ((uint32_t)((uint8_t)(c >> 8UL)) * opa) / 255;
b = ((uint32_t)((uint8_t)(c >> 0UL)) * opa) / 255;
*cl = r << 16UL | g << 8UL | b << 0UL;
r += 255 - opa;
g += 255 - opa;
b += 255 - opa;
*ch = r << 16UL | g << 8UL | b << 0UL;
}
static int lv_port_gpu_handle_indexed_color(const lv_color_t ** src, const d2_color ** clut, d2_s32 cf)
{
int clut_len = 0;
if(cf & d2_mode_clut) {
/* Calculate CLUT length in entries */
switch(cf & ~(d2_mode_clut | d2_mode_rle)) {
case d2_mode_i1:
clut_len = 2;
break;
case d2_mode_i2:
clut_len = 4;
break;
case d2_mode_i4:
clut_len = 16;
break;
case d2_mode_i8:
clut_len = 256;
break;
case d2_mode_ai44:
clut_len = 16;
break;
default:
return 0;
}
*clut = (const d2_color *)*src;
*src = (const lv_color_t *)((const uint32_t *)*src + clut_len);
}
return clut_len;
}
static int lv_port_gpu_cf_bpp(d2_s32 cf)
{
switch(cf & ~(d2_mode_clut | d2_mode_rle)) {
case d2_mode_argb8888:
return 32;
case d2_mode_rgba8888:
return 32;
case d2_mode_rgb888:
return 32;
case d2_mode_argb4444:
return 16;
case d2_mode_rgba4444:
return 16;
case d2_mode_argb1555:
return 16;
case d2_mode_rgba5551:
return 16;
case d2_mode_rgb565:
return 16;
case d2_mode_ai44:
return 8;
case d2_mode_i8:
return 8;
case d2_mode_i4:
return 4;
case d2_mode_i2:
return 2;
case d2_mode_i1:
return 1;
case d2_mode_alpha8:
return 8;
case d2_mode_alpha4:
return 4;
case d2_mode_alpha2:
return 2;
case d2_mode_alpha1:
return 1;
default:
return 0;
}
}
static d2_s32 lv_port_gpu_cf_get_default(void)
{
return d2_mode_rgb565;
}
static void lv_port_gpu_config_blit_clear(void)
{
alpha_enabled = false;
color_key_enabled = false;
blend_enabled = true;
colorize_enabled = false;
lv_draw_img_dsc_init(&img_dsc);
src_cf_val = lv_port_gpu_cf_get_default();
dst_cf_val = lv_port_gpu_cf_get_default();
}
void lv_port_gpu_init(void)
{
lv_port_gpu_config_blit_clear();
}
static void lv_port_gpu_rotate_point(int * x, int * y, float cos_angle, float sin_angle, int pivot_x, int pivot_y)
{
float fx, fy;
*x -= pivot_x;
*y -= pivot_y;
fx = ((float) * x) / 16.0f;
fy = ((float) * y) / 16.0f;
*x = (int)(((fx * cos_angle) - (fy * sin_angle)) * 16.0f);
*y = (int)(((fx * sin_angle) + (fy * cos_angle)) * 16.0f);
*x += pivot_x;
*y += pivot_y;
}
void lv_draw_ra6m3_g2d_init(void)
{
if(_d2_handle != NULL) {
return;
}
_d2_handle = d2_opendevice(0);
if(_d2_handle == NULL)
return;
/* set blocksize for default displaylist */
if(d2_setdlistblocksize(_d2_handle, 25) != D2_OK) {
LV_LOG_ERROR("Could NOT d2_setdlistblocksize\n");
d2_closedevice(_d2_handle);
return;
}
/* bind the hardware */
if(d2_inithw(_d2_handle, 0) != D2_OK) {
LV_LOG_ERROR("Could NOT d2_inithw\n");
d2_closedevice(_d2_handle);
return;
}
renderbuffer = d2_newrenderbuffer(_d2_handle, 20, 20);
if(!renderbuffer) {
LV_LOG_ERROR("NO renderbuffer\n");
d2_closedevice(_d2_handle);
return;
}
}
static void lv_port_gpu_hw_deinit(void)
{
if(_d2_handle == NULL)
return;
D2_EXEC(d2_freerenderbuffer(_d2_handle, renderbuffer));
D2_EXEC(d2_closedevice(_d2_handle));
renderbuffer = NULL;
_d2_handle = NULL;
}
void lv_port_gpu_flush(void)
{
lv_port_gpu_hw_deinit();
}
static void lv_port_gpu_start_render(void)
{
D2_EXEC(d2_selectrenderbuffer(_d2_handle, renderbuffer));
}
static void lv_port_gpu_complete_render(void)
{
D2_EXEC(d2_flushframe(_d2_handle));
}
void lv_port_gpu_wait(lv_draw_ctx_t * draw_ctx)
{
lv_port_gpu_complete_render();
lv_draw_sw_wait_for_finish(draw_ctx);
}
static void lv_port_gpu_execute_render(void)
{
if(_d2_handle) {
D2_EXEC(d2_executerenderbuffer(_d2_handle, renderbuffer, 0));
}
}
void lv_port_gpu_blit(int32_t x, int32_t y, lv_color_t * dst, const lv_area_t * fill_area)
{
uint32_t ModeSrc;
ModeSrc = d2_mode_rgb565;
lv_coord_t dst_width, dst_hight;
dst_width = lv_area_get_width(fill_area);
dst_hight = lv_area_get_height(fill_area);
d2_selectrenderbuffer(_d2_handle, renderbuffer);
// Generate render operations
d2_framebuffer(_d2_handle, (uint16_t *)&fb_background[0], LV_HOR_RES_MAX, LV_HOR_RES_MAX,
MAX(fill_area->y2 + 1, 2), lv_port_gpu_cf_get_default());
d2_cliprect(_d2_handle, 0, 0, LV_HOR_RES_MAX - 1, fill_area->y2);
d2_setblitsrc(_d2_handle, (void *) dst, dst_width, dst_width, dst_hight, ModeSrc);
d2_blitcopy(_d2_handle, dst_width, dst_hight, 0, 0, D2_FIX4(dst_width), D2_FIX4(dst_hight),
D2_FIX4(fill_area->x1), D2_FIX4(fill_area->y1), 0);
// Execute render operations
d2_executerenderbuffer(_d2_handle, renderbuffer, 0);
}
void lv_port_gpu_fill(lv_color_t * dest_buf, const lv_area_t * fill_area, lv_coord_t dst_width,
lv_color_t color, lv_opa_t opa)
{
invalidate_cache();
lv_port_gpu_start_render();
D2_EXEC(d2_framebuffer(_d2_handle, d1_maptovidmem(_d2_handle, dest_buf), MAX(dst_width, 2), MAX(dst_width, 2),
MAX(fill_area->y2 + 1, 2), lv_port_gpu_cf_get_default()));
D2_EXEC(d2_cliprect(_d2_handle, 0, 0, dst_width - 1, fill_area->y2));
D2_EXEC(d2_setalpha(_d2_handle, opa > LV_OPA_MAX ? 0xFF : opa));
D2_EXEC(d2_setcolor(_d2_handle, 0, lv_port_gpu_color_lv_to_d2(color)));
D2_EXEC(d2_renderbox(_d2_handle, D2_FIX4(fill_area->x1), D2_FIX4(fill_area->y1),
D2_FIX4(lv_area_get_width(fill_area)), D2_FIX4(lv_area_get_height(fill_area))));
lv_port_gpu_execute_render();
}
bool lv_port_gpu_config_blit(const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t dst_cf,
lv_img_cf_t src_cf, bool alpha_en, bool color_key_en, bool blend_en, bool colorize_en)
{
d2_s32 d2_src_cf, d2_dst_cf;
if(blend_en && draw_dsc->blend_mode != LV_BLEND_MODE_NORMAL
&& draw_dsc->blend_mode != LV_BLEND_MODE_ADDITIVE) {
return false;
}
d2_src_cf = lv_port_gpu_cf_lv_to_d2(src_cf);
d2_dst_cf = lv_port_gpu_cf_lv_to_d2(dst_cf);
if(d2_src_cf < 0 || !lv_port_gpu_cf_fb_valid(d2_dst_cf)) {
return false;
}
src_cf_val = d2_src_cf;
dst_cf_val = d2_dst_cf;
img_dsc = *draw_dsc;
/* Disable alpha if alpha channel does not exist */
alpha_enabled = lv_port_gpu_cf_has_alpha(src_cf_val) ? alpha_en : 0;
color_key_enabled = color_key_en;
blend_enabled = blend_en;
colorize_enabled = colorize_en | lv_port_gpu_cf_is_alpha(src_cf_val);
return true;
}
static void lv_port_gpu_blit_internal(const lv_area_t * dest_area, const lv_color_t * src_buf,
const lv_area_t * src_area, d2_u32 flags)
{
const lv_area_t * img_area = src_area;
lv_area_t img_area_scaled;
lv_coord_t w, h, img_w, img_h;
d2_s32 pitch;
int bpp = lv_port_gpu_cf_bpp(src_cf_val);
D2_EXEC(d2_cliprect(_d2_handle, dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2));
pitch = w = lv_area_get_width(src_area);
h = lv_area_get_height(src_area);
if(img_dsc.zoom != LV_IMG_ZOOM_NONE) {
img_area_scaled.x1 = src_area->x1 + ((((int32_t)0 - img_dsc.pivot.x) * img_dsc.zoom) >> 8) + img_dsc.pivot.x;
img_area_scaled.x2 = src_area->x1 + ((((int32_t)w - img_dsc.pivot.x) * img_dsc.zoom) >> 8) + img_dsc.pivot.x;
img_area_scaled.y1 = src_area->y1 + ((((int32_t)0 - img_dsc.pivot.y) * img_dsc.zoom) >> 8) + img_dsc.pivot.y;
img_area_scaled.y2 = src_area->y1 + ((((int32_t)h - img_dsc.pivot.y) * img_dsc.zoom) >> 8) + img_dsc.pivot.y;
img_area = &img_area_scaled;
}
img_w = lv_area_get_width(img_area);
img_h = lv_area_get_height(img_area);
if(0 < bpp && bpp < 8) {
pitch = (w + (8 - bpp)) & (~(8 - bpp));
}
if(img_dsc.angle == 0) {
D2_EXEC(d2_setblitsrc(_d2_handle, (void *) src_buf, pitch, w, h, src_cf_val));
D2_EXEC(d2_blitcopy(_d2_handle, w, h, 0, 0,
D2_FIX4(img_w), D2_FIX4(img_h), D2_FIX4(img_area->x1), D2_FIX4(img_area->y1), flags));
}
else {
int x, y, x1, y1, x2, y2, x3, y3, x4, y4, dxu, dxv, dyu, dyv, xx, xy, yx, yy;
int pivot_scaled_x, pivot_scaled_y;
int tex_offset = (flags & d2_bf_filter) ? -32767 : 0;
d2_u8 amode, cmode = d2_to_copy;
float angle = ((float)img_dsc.angle / 10) * M_PI / 180;
float cos_angle = cosf(angle);
float sin_angle = sinf(angle);
d2_u8 fillmode_backup;
/* setup texture params */
fillmode_backup = d2_getfillmode(_d2_handle);
D2_EXEC(d2_setfillmode(_d2_handle, d2_fm_texture));
D2_EXEC(d2_settexture(_d2_handle, (void *) src_buf, pitch, w, h, src_cf_val));
D2_EXEC(d2_settexturemode(_d2_handle, flags & (d2_bf_filter | d2_bf_wrap)));
amode = flags & d2_bf_usealpha ? d2_to_copy : d2_to_one;
cmode = flags & d2_bf_colorize2 ? d2_to_blend : d2_to_copy;
D2_EXEC(d2_settextureoperation(_d2_handle, amode, cmode, cmode, cmode));
if(flags & d2_bf_colorize2) {
d2_color cl = d2_getcolor(_d2_handle, 0), ch = d2_getcolor(_d2_handle, 1);
D2_EXEC(d2_settexopparam(_d2_handle, d2_cc_red, (uint8_t)(cl >> 16UL),
(uint8_t)(ch >> 16UL)));
D2_EXEC(d2_settexopparam(_d2_handle, d2_cc_green, (uint8_t)(cl >> 8UL),
(uint8_t)(ch >> 8UL)));
D2_EXEC(d2_settexopparam(_d2_handle, d2_cc_blue, (uint8_t)(cl >> 0UL),
(uint8_t)(ch >> 0UL)));
}
x = D2_FIX4(img_area->x1);
y = D2_FIX4(img_area->y1);
/* define quad points */
x1 = D2_FIX4(0);
y1 = D2_FIX4(0);
x2 = D2_FIX4(img_w);
y2 = D2_FIX4(0);
x3 = D2_FIX4(img_w);
y3 = D2_FIX4(img_h);
x4 = D2_FIX4(0);
y4 = D2_FIX4(img_h);
/* rotate points for quad */
pivot_scaled_x = (img_dsc.pivot.x * img_dsc.zoom) >> 4;
pivot_scaled_y = (img_dsc.pivot.y * img_dsc.zoom) >> 4;
lv_port_gpu_rotate_point(&x1, &y1, cos_angle, sin_angle, pivot_scaled_x, pivot_scaled_y);
lv_port_gpu_rotate_point(&x2, &y2, cos_angle, sin_angle, pivot_scaled_x, pivot_scaled_y);
lv_port_gpu_rotate_point(&x3, &y3, cos_angle, sin_angle, pivot_scaled_x, pivot_scaled_y);
lv_port_gpu_rotate_point(&x4, &y4, cos_angle, sin_angle, pivot_scaled_x, pivot_scaled_y);
/* compute texture increments */
xx = (int)(cos_angle * 65536.0f);
xy = (int)(sin_angle * 65536.0f);
yx = (int)(-sin_angle * 65536.0f);
yy = (int)(cos_angle * 65536.0f);
dxu = ((D2_FIX16(w) / D2_FIX4(img_w)) * xx) >> 12;
dxv = ((D2_FIX16(w) / D2_FIX4(img_w)) * xy) >> 12;
dyu = ((D2_FIX16(h) / D2_FIX4(img_h)) * yx) >> 12;
dyv = ((D2_FIX16(h) / D2_FIX4(img_h)) * yy) >> 12;
/* map texture exactly to rotated quad, so texel center is always (0/0) top-left */
D2_EXEC(d2_settexelcenter(_d2_handle, 0, 0));
D2_EXEC(d2_settexturemapping(_d2_handle, (d2_point)(x + x1), (d2_point)(y + y1),
tex_offset, tex_offset, dxu, dxv, dyu, dyv));
int minx = MAX(dest_area->x1, D2_INT4(x + MIN(x1, MIN(x2, MIN(x3, x4)))));
int maxx = MIN(dest_area->x2, D2_INT4(x + MAX(x1, MAX(x2, MAX(x3, x4)))));
int slice = (flags & d2_bf_filter) ? 6 : 8;
/* Perform render operation in slices to acheive better performance */
for(int posx = minx; posx < maxx; posx += slice) {
D2_EXEC(d2_cliprect(_d2_handle, posx, dest_area->y1, MIN(posx + slice - 1, maxx), dest_area->y2));
D2_EXEC(d2_renderquad(_d2_handle, (d2_point)(x + x1), (d2_point)(y + y1),
(d2_point)(x + x2), (d2_point)(y + y2),
(d2_point)(x + x3), (d2_point)(y + y3),
(d2_point)(x + x4), (d2_point)(y + y4), 0));
}
D2_EXEC(d2_setfillmode(_d2_handle, fillmode_backup));
}
}
void lv_port_ra_gpu_blit(lv_color_t * dst, const lv_area_t * dst_area, lv_coord_t dest_stride,
const lv_color_t * src, const lv_area_t * src_area, lv_opa_t opa)
{
d2_u32 flags = 0;
const d2_color * clut = NULL;
int clut_len = 0;
invalidate_cache();
clut_len = lv_port_gpu_handle_indexed_color(&src, &clut, src_cf_val);
lv_port_gpu_start_render();
D2_EXEC(d2_framebuffer(_d2_handle, d1_maptovidmem(_d2_handle, dst), MAX(dest_stride, 2),
MAX(dst_area->x2 + 1, 2), MAX(dst_area->y2 + 1, 2), dst_cf_val));
flags |= alpha_enabled ? d2_bf_usealpha : 0;
D2_EXEC(d2_setalpha(_d2_handle, opa > LV_OPA_MAX ? LV_OPA_COVER : opa));
if(clut) {
D2_EXEC(d2_writetexclut_direct(_d2_handle, clut, 0, clut_len));
}
flags |= color_key_enabled ? d2_bf_usealpha : 0;
flags |= (colorize_enabled || img_dsc.recolor_opa != LV_OPA_TRANSP) ? d2_bf_colorize2 : 0;
if(colorize_enabled) {
D2_EXEC(d2_setcolor(_d2_handle, 0, lv_port_gpu_color_lv_to_d2(img_dsc.recolor)));
D2_EXEC(d2_setcolor(_d2_handle, 1, lv_port_gpu_color_lv_to_d2(img_dsc.recolor)));
}
else if(img_dsc.recolor_opa != LV_OPA_TRANSP) {
d2_color cl = 0, ch = 0;
lv_port_gpu_get_recolor_consts(&cl, &ch);
D2_EXEC(d2_setcolor(_d2_handle, 0, cl));
D2_EXEC(d2_setcolor(_d2_handle, 1, ch));
}
flags |= ((img_dsc.angle || img_dsc.zoom != LV_IMG_ZOOM_NONE) && img_dsc.antialias) ? d2_bf_filter : 0;
if(blend_enabled) {
D2_EXEC(d2_setblendmode(_d2_handle, d2_bm_alpha,
img_dsc.blend_mode != LV_BLEND_MODE_NORMAL ? d2_bm_one : d2_bm_one_minus_alpha));
D2_EXEC(d2_setalphablendmode(_d2_handle, d2_bm_one, d2_bm_one_minus_alpha));
}
else {
D2_EXEC(d2_setblendmode(_d2_handle, d2_bm_one, d2_bm_zero));
D2_EXEC(d2_setalphablendmode(_d2_handle, d2_bm_one, d2_bm_zero));
}
lv_port_gpu_blit_internal(dst_area, src, src_area, flags);
lv_port_gpu_execute_render();
}
void lv_draw_ra6m3_2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
lv_area_t blend_area;
if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
bool done = false;
if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) {
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_color_t * dest_buf = draw_ctx->buf;
const lv_color_t * src_buf = dsc->src_buf;
if(src_buf) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
lv_area_t src_area;
src_area.x1 = blend_area.x1 - (dsc->blend_area->x1 - draw_ctx->buf_area->x1);
src_area.y1 = blend_area.y1 - (dsc->blend_area->y1 - draw_ctx->buf_area->y1);
src_area.x2 = src_area.x1 + lv_area_get_width(dsc->blend_area) - 1;
src_area.y2 = src_area.y1 + lv_area_get_height(dsc->blend_area) - 1;
lv_port_ra_gpu_blit(dest_buf, &blend_area, dest_stride, src_buf, &src_area, dsc->opa);
done = true;
}
else if(dsc->opa >= LV_OPA_MAX) {
lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
lv_port_gpu_fill(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa);
done = true;
}
}
if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
}
static void lv_port_gpu_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
{
/*TODO basic ARGB8888 image can be handles here*/
lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
}
void lv_draw_ra6m3_2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_ra6m3_dma2d_ctx_t * ra_2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
ra_2d_draw_ctx->blend = lv_draw_ra6m3_2d_blend;
ra_2d_draw_ctx->base_draw.draw_img_decoded = lv_port_gpu_img_decoded;
ra_2d_draw_ctx->base_draw.wait_for_finish = lv_port_gpu_wait;
ra_2d_draw_ctx->base_draw.draw_letter = lv_draw_gpu_letter;
//ra_2d_draw_ctx->base_draw.buffer_copy = lv_draw_ra6m3_2d_buffer_copy;
}
void lv_draw_stm32_dma2d_ctx_deinit(lv_disp_t * disp, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(disp);
LV_UNUSED(draw_ctx);
}
static void invalidate_cache(void)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver);
}
#ifdef LOG_ERRORS
static void lv_port_gpu_log_error(d2_s32 status, const char * func, int line)
{
if(status) {
log_error_list[error_list_index].error = status;
log_error_list[error_list_index].func = func;
log_error_list[error_list_index].line = line;
LV_LOG_ERROR("%s\r\n", d2_geterrorstring(_d2_handle));
LV_LOG_ERROR("%d:\t%d - %s : %d\r\n", error_count,
log_error_list[error_list_index].error,
log_error_list[error_list_index].func,
log_error_list[error_list_index].line);
error_count++;
error_list_index++;
if(error_list_index >= ERROR_LIST_SIZE) {
error_list_index = 0;
}
}
}
#endif
#endif /* LV_USE_GPU_RA6M3_G2D */

View File

@@ -0,0 +1,56 @@
/**
* @file lv_gpu_d2_ra6m3.h
*
*/
#ifndef LV_GPU_D2_RA6M3_H
#define LV_GPU_D2_RA6M3_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#if LV_USE_GPU_RA6M3_G2D
#include "../../core/lv_disp.h"
#include "../sw/lv_draw_sw.h"
/**********************
* DEFINE
**********************/
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define M_PI 3.1415926
/**********************
* TYPEDEFS
**********************/
typedef lv_draw_sw_ctx_t lv_draw_ra6m3_dma2d_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_ra6m3_g2d_init(void);
void lv_port_gpu_init(void);
void lv_port_gpu_flush(void);
void lv_port_gpu_blit(int32_t x, int32_t y, lv_color_t * dst, const lv_area_t * fill_area);
void lv_draw_ra6m3_2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
void lv_draw_ra6m3_2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_ra6m3_2d_ctx_deinit(lv_disp_drv_t * disp, lv_draw_ctx_t * draw_ctx);
#endif /*LV_USE_GPU_GD32_IPA*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif

View File

@@ -0,0 +1,28 @@
# SDL_Renderer Based Drawing Functions
In LVGL, drawing was performed by CPU. To improve drawing performance on platforms with GPU,
we should perform drawing operations on GPU if possible.
This implementation has moved most bitmap blending and drawing procedures to utilize SDL_Renderer,
which takes advantages of hardware acceleration APIs like DirectX or OpenGL.
This implementation can be also considered as a reference implementation, for contributors wants to
develop accelerated drawing functions with other APIs such as OpenGL/OpenGL ES.
## Caveats
`lv_draw_arc`, `lv_draw_line` is not enabled, due to incomplete implementation. So lines and arcs will
have significant impact to drawing performances.
Performance of this implementation still has room to improve. Or we should use more powerful APIs
such as OpenGL.
## Notices for files
### `lv_draw_sdl_stack_blur.c`
Contains modified code from [android-stackblur](https://github.com/kikoso/android-stackblur) project.
Apache License 2.0
### `lv_draw_sdl_lru.c`/`lv_draw_sdl_lru.h`
Contains modified code from [C-LRU-Cache](https://github.com/willcannings/C-LRU-Cache) project. No license defined.

View File

@@ -0,0 +1,103 @@
/**
* @file lv_draw_sdl.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_layer.h"
/*********************
* DEFINES
*********************/
void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
uint16_t point_cnt);
void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
{
_lv_draw_sdl_utils_init();
lv_memset_00(draw_ctx, sizeof(lv_draw_sdl_ctx_t));
draw_ctx->draw_rect = lv_draw_sdl_draw_rect;
draw_ctx->draw_img = lv_draw_sdl_img_core;
draw_ctx->draw_letter = lv_draw_sdl_draw_letter;
draw_ctx->draw_line = lv_draw_sdl_draw_line;
draw_ctx->draw_arc = lv_draw_sdl_draw_arc;
draw_ctx->draw_polygon = lv_draw_sdl_polygon;
draw_ctx->draw_bg = lv_draw_sdl_draw_bg;
draw_ctx->layer_init = lv_draw_sdl_layer_init;
draw_ctx->layer_blend = lv_draw_sdl_layer_blend;
draw_ctx->layer_destroy = lv_draw_sdl_layer_destroy;
draw_ctx->layer_instance_size = sizeof(lv_draw_sdl_layer_ctx_t);
lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
draw_ctx_sdl->renderer = ((lv_draw_sdl_drv_param_t *) disp_drv->user_data)->renderer;
draw_ctx_sdl->internals = lv_mem_alloc(sizeof(lv_draw_sdl_context_internals_t));
lv_memset_00(draw_ctx_sdl->internals, sizeof(lv_draw_sdl_context_internals_t));
lv_draw_sdl_texture_cache_init(draw_ctx_sdl);
}
void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_draw_sdl_texture_cache_deinit(draw_ctx_sdl);
lv_mem_free(draw_ctx_sdl->internals);
_lv_draw_sdl_utils_deinit();
}
SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver)
{
SDL_Texture * texture = SDL_CreateTexture(renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET, hor, ver);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
return texture;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,96 @@
/**
* @file lv_draw_sdl.h
*
*/
#ifndef LV_DRAW_SDL_H
#define LV_DRAW_SDL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "../../core/lv_disp.h"
/*********************
* DEFINES
*********************/
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_ARGB8888
#else
#define LV_DRAW_SDL_TEXTURE_FORMAT SDL_PIXELFORMAT_RGBA8888
#endif
/**********************
* TYPEDEFS
**********************/
struct lv_draw_sdl_context_internals_t;
typedef struct {
/**
* Render for display driver
*/
SDL_Renderer * renderer;
void * user_data;
} lv_draw_sdl_drv_param_t;
typedef struct {
lv_draw_ctx_t base_draw;
SDL_Renderer * renderer;
struct lv_draw_sdl_context_internals_t * internals;
} lv_draw_sdl_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
/**
* @brief Free caches
*
*/
void lv_draw_sdl_deinit_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
SDL_Texture * lv_draw_sdl_create_screen_texture(SDL_Renderer * renderer, lv_coord_t hor, lv_coord_t ver);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_H*/

View File

@@ -0,0 +1,19 @@
CSRCS += lv_draw_sdl.c
CSRCS += lv_draw_sdl_arc.c
CSRCS += lv_draw_sdl_bg.c
CSRCS += lv_draw_sdl_composite.c
CSRCS += lv_draw_sdl_img.c
CSRCS += lv_draw_sdl_label.c
CSRCS += lv_draw_sdl_line.c
CSRCS += lv_draw_sdl_mask.c
CSRCS += lv_draw_sdl_polygon.c
CSRCS += lv_draw_sdl_rect.c
CSRCS += lv_draw_sdl_stack_blur.c
CSRCS += lv_draw_sdl_texture_cache.c
CSRCS += lv_draw_sdl_utils.c
CSRCS += lv_draw_sdl_layer.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl"

View File

@@ -0,0 +1,245 @@
/**
* @file lv_draw_sdl_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
const int16_t * caps);
static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
lv_area_t * out);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle)
{
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_area_t area_out;
area_out.x1 = center->x - radius;
area_out.y1 = center->y - radius;
area_out.x2 = center->x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center->y + radius - 1;
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, &area_out, draw_ctx->clip_area)) {
return;
}
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
int16_t mask_ids[3] = {LV_MASK_ID_INV, LV_MASK_ID_INV, LV_MASK_ID_INV}, mask_ids_count = 1;
int16_t cap_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
mask_ids[0] = lv_draw_mask_add(&mask_out_param, NULL);
lv_draw_mask_radius_param_t mask_in_param;
if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
mask_ids[1] = lv_draw_mask_add(&mask_in_param, NULL);
mask_ids_count++;
}
lv_draw_mask_angle_param_t mask_angle_param;
if((start_angle - end_angle) % 360) {
lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
mask_ids[2] = lv_draw_mask_add(&mask_angle_param, NULL);
mask_ids_count++;
}
lv_draw_mask_radius_param_t cap_start_param, cap_end_param;
if(mask_ids_count == 3 && dsc->rounded) {
lv_area_t start_area, end_area;
get_cap_area((int16_t) start_angle, dsc->width, radius, center, &start_area);
get_cap_area((int16_t) end_angle, dsc->width, radius, center, &end_area);
lv_draw_mask_radius_init(&cap_start_param, &start_area, dsc->width / 2, false);
cap_ids[0] = lv_draw_mask_add(&cap_start_param, NULL);
lv_draw_mask_radius_init(&cap_end_param, &end_area, dsc->width / 2, false);
cap_ids[1] = lv_draw_mask_add(&cap_end_param, NULL);
}
lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
bool texture_cached = false;
SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h,
&texture_cached);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
dump_masks(texture, &draw_area, mask_ids, mask_ids_count, cap_ids[0] != LV_MASK_ID_INV ? cap_ids : NULL);
lv_draw_mask_remove_id(mask_ids[0]);
lv_draw_mask_free_param(&mask_out_param);
if(mask_ids_count > 1) {
lv_draw_mask_remove_id(mask_ids[1]);
lv_draw_mask_free_param(&mask_in_param);
}
if(mask_ids_count > 2) {
lv_draw_mask_remove_id(mask_ids[2]);
lv_draw_mask_free_param(&mask_angle_param);
}
if(cap_ids[0] != LV_MASK_ID_INV) {
lv_draw_mask_remove_id(cap_ids[0]);
lv_draw_mask_remove_id(cap_ids[1]);
lv_draw_mask_free_param(&cap_start_param);
lv_draw_mask_free_param(&cap_end_param);
}
SDL_Rect srcrect = {0, 0, w, h}, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
SDL_Color color;
lv_color_to_sdl_color(&dsc->color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, dsc->opa);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
if(!texture_cached) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(texture);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
const int16_t * caps)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
*pixel = line_buf[x];
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
if(caps) {
for(int i = 0; i < 2; i++) {
lv_memset_ff(line_buf, rect.w);
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, &caps[i], 1);
if(res == LV_DRAW_MASK_RES_TRANSP) {
/* Ignore */
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
uint16_t old_opa = line_buf[x] + *pixel;
*pixel = LV_MIN(old_opa, 0xFF);
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
lv_area_t * out)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin((int16_t)(90 - angle))) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
out->x1 = cir_x - thick_half + thick_corr;
out->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
out->x1 = cir_x - thick_half;
out->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
out->y1 = cir_y - thick_half + thick_corr;
out->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
out->y1 = cir_y - thick_half;
out->y2 = cir_y + thick_half - thick_corr;
}
lv_area_move(out, center->x, center->y);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,106 @@
/**
* @file lv_draw_sdl_bg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../lv_draw_rect.h"
#include "../lv_draw_img.h"
#include "../lv_draw_label.h"
#include "../lv_draw_mask.h"
#include "../../core/lv_refr.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
{
const lv_area_t * clip = draw_ctx->clip_area;
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
/* Coords will be translated so coords will start at (0,0) */
lv_area_t t_area;
bool has_content = _lv_area_intersect(&t_area, coords, clip);
/* Shadows and outlines will also draw in extended area */
if(has_content) {
if(dsc->bg_img_src) {
draw_bg_img(ctx, coords, &t_area, dsc);
}
else {
draw_bg_color(ctx, coords, &t_area, dsc);
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void draw_bg_color(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
SDL_Color bg_color;
lv_color_to_sdl_color(&dsc->bg_color, &bg_color);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(ctx->renderer, bg_color.r, bg_color.g, bg_color.b, dsc->bg_opa);
SDL_Rect rect;
lv_area_to_sdl_rect(draw_area, &rect);
SDL_RenderFillRect(ctx->renderer, &rect);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
}
static void draw_bg_img(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords, const lv_area_t * draw_area,
const lv_draw_rect_dsc_t * dsc)
{
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
SDL_Rect rect;
lv_area_to_sdl_rect(draw_area, &rect);
SDL_RenderFillRect(ctx->renderer, &rect);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
lv_draw_rect((lv_draw_ctx_t *) ctx, dsc, coords);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,279 @@
/**
* @file lv_draw_sdl_composite.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_gc.h"
#include "../../core/lv_refr.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_priv.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_draw_sdl_composite_texture_id_t type;
} composite_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type);
static lv_coord_t next_pow_of_2(lv_coord_t num);
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
lv_area_t * clip_out, lv_area_t * apply_area)
{
lv_area_t full_coords = *coords_in;
/* Normalize full_coords */
if(full_coords.x1 > full_coords.x2) {
lv_coord_t x2 = full_coords.x2;
full_coords.x2 = full_coords.x1;
full_coords.x1 = x2;
}
if(full_coords.y1 > full_coords.y2) {
lv_coord_t y2 = full_coords.y2;
full_coords.y2 = full_coords.y1;
full_coords.y1 = y2;
}
if(extension) {
full_coords.x1 -= extension->x1;
full_coords.x2 += extension->x2;
full_coords.y1 -= extension->y1;
full_coords.y2 += extension->y2;
}
if(!_lv_area_intersect(apply_area, &full_coords, clip_in)) return false;
bool has_mask = lv_draw_mask_is_any(apply_area);
const bool draw_mask = has_mask && LV_GPU_SDL_CUSTOM_BLEND_MODE;
const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL;
if(draw_mask || draw_blend) {
lv_draw_sdl_context_internals_t * internals = ctx->internals;
LV_ASSERT(internals->mask == NULL && internals->composition == NULL && internals->target_backup == NULL);
lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area);
internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h,
&internals->composition_cached);
/* Don't need to worry about integral overflow */
lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1;
/* Offset draw area to start with (0,0) of coords */
lv_area_move(coords_out, ofs_x, ofs_y);
lv_area_move(clip_out, ofs_x, ofs_y);
internals->target_backup = SDL_GetRenderTarget(ctx->renderer);
SDL_SetRenderTarget(ctx->renderer, internals->composition);
SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0);
/* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_RenderFillRect(ctx->renderer, NULL);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
internals->mask = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0, w, h,
&internals->composition_cached);
dump_masks(internals->mask, apply_area);
#endif
}
else if(has_mask) {
/* Fallback mask handling. This will at least make bars looks less bad */
for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
_lv_draw_mask_common_dsc_t * comm_param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
if(comm_param == NULL) continue;
switch(comm_param->type) {
case LV_DRAW_MASK_TYPE_RADIUS: {
const lv_draw_mask_radius_param_t * param = (const lv_draw_mask_radius_param_t *) comm_param;
if(param->cfg.outer) break;
_lv_area_intersect(clip_out, apply_area, &param->cfg.rect);
break;
}
default:
break;
}
}
}
return has_mask;
}
void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode)
{
lv_draw_sdl_context_internals_t * internals = ctx->internals;
SDL_Rect src_rect = {0, 0, lv_area_get_width(apply_area), lv_area_get_height(apply_area)};
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
if(internals->mask) {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(internals->mask, mode);
SDL_RenderCopy(ctx->renderer, internals->mask, &src_rect, &src_rect);
}
#endif
/* Shapes are drawn on composite layer when mask or blend mode is present */
if(internals->composition) {
SDL_Rect dst_rect;
lv_area_to_sdl_rect(apply_area, &dst_rect);
SDL_SetRenderTarget(ctx->renderer, internals->target_backup);
switch(blend_mode) {
case LV_BLEND_MODE_NORMAL:
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
break;
case LV_BLEND_MODE_ADDITIVE:
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_ADD);
break;
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
case LV_BLEND_MODE_SUBTRACTIVE: {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE,
SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ONE,
SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
SDL_SetTextureBlendMode(internals->composition, mode);
break;
}
case LV_BLEND_MODE_MULTIPLY: {
SDL_BlendMode mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_COLOR,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ZERO,
SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(internals->composition, mode);
break;
}
#endif
default:
LV_LOG_WARN("Doesn't support blend mode %d", blend_mode);
SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND);
/* Unsupported yet */
break;
}
SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect);
if(!internals->composition_cached) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(internals->composition);
}
}
internals->mask = internals->composition = internals->target_backup = NULL;
}
SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
lv_coord_t w, lv_coord_t h, bool * texture_in_cache)
{
lv_point_t * tex_size = NULL;
composite_key_t mask_key = mask_key_create(id);
SDL_Texture * result = lv_draw_sdl_texture_cache_get_with_userdata(ctx, &mask_key, sizeof(composite_key_t), NULL,
(void **) &tex_size);
if(result == NULL || tex_size->x < w || tex_size->y < h) {
lv_coord_t size = next_pow_of_2(LV_MAX(w, h));
int access = SDL_TEXTUREACCESS_STREAMING;
if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0) {
access = SDL_TEXTUREACCESS_TARGET;
}
else if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) {
access = SDL_TEXTUREACCESS_TARGET;
}
result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size);
tex_size = lv_mem_alloc(sizeof(lv_point_t));
tex_size->x = tex_size->y = size;
bool in_cache = lv_draw_sdl_texture_cache_put_advanced(ctx, &mask_key, sizeof(composite_key_t), result,
tex_size, lv_mem_free, 0);
if(!in_cache) {
lv_mem_free(tex_size);
}
if(texture_in_cache != NULL) {
*texture_in_cache = in_cache;
}
}
else if(texture_in_cache != NULL) {
*texture_in_cache = true;
}
return result;
}
/**********************
* STATIC FUNCTIONS
**********************/
static composite_key_t mask_key_create(lv_draw_sdl_composite_texture_id_t type)
{
composite_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_MASK;
key.type = type;
return key;
}
static lv_coord_t next_pow_of_2(lv_coord_t num)
{
lv_coord_t n = 128;
while(n < num && n < 16384) {
n = n << 1;
}
return n;
}
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
const size_t idx = y * pitch + x * 4;
pixels[idx] = line_buf[x];
pixels[idx + 1] = pixels[idx + 2] = pixels[idx + 3] = 0xFF;
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,84 @@
/**
* @file lv_draw_sdl_composite.h
*
*/
#ifndef LV_DRAW_SDL_COMPOSITE_H
#define LV_DRAW_SDL_COMPOSITE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum lv_draw_sdl_composite_texture_id_t {
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM0,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET1,
LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0,
} lv_draw_sdl_composite_texture_id_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Begin drawing with mask. Render target will be switched to a temporary texture,
* and drawing coordinates may get clipped or translated
* @param ctx Drawing context
* @param coords_in Original coordinates
* @param clip_in Original clip area
* @param extension Useful for shadows or outlines, can be NULL
* @param coords_out Translated coords
* @param clip_out Translated clip area
* @param apply_area Area of actual composited texture will be drawn
* @return true if there are any mask needs to be drawn, false otherwise
*/
bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coords_in, const lv_area_t * clip_in,
const lv_area_t * extension, lv_blend_mode_t blend_mode, lv_area_t * coords_out,
lv_area_t * clip_out, lv_area_t * apply_area);
void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_area, lv_blend_mode_t blend_mode);
/**
*
* @param ctx Drawing context
* @param id Texture ID
* @param w Maximum width needed
* @param h Maximum height needed
* @param texture_in_cache output to query if the texture is in cache. If it's not in cache, you'll need to destroy it
* by yourself
* @return Obtained texture
*/
SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id,
lv_coord_t w, lv_coord_t h, bool * texture_in_cache);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_COMPOSITE_H*/

View File

@@ -0,0 +1,499 @@
/**
* @file lv_draw_sdl_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../lv_draw_img.h"
#include "../lv_img_cache.h"
#include "../lv_draw_mask.h"
#include "../../misc/lv_lru.h"
#include "../../misc/lv_gc.h"
#include "lv_draw_sdl_img.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_rect.h"
#include "lv_draw_sdl_layer.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
const SDL_Texture * texture;
lv_coord_t w, h, radius;
} lv_draw_img_rounded_key_t;
enum {
ROUNDED_IMG_PART_LEFT = 0,
ROUNDED_IMG_PART_HCENTER = 1,
ROUNDED_IMG_PART_RIGHT = 2,
ROUNDED_IMG_PART_TOP = 3,
ROUNDED_IMG_PART_VCENTER = 4,
ROUNDED_IMG_PART_BOTTOM = 5,
};
enum {
ROUNDED_IMG_CORNER_TOP_LEFT = 0,
ROUNDED_IMG_CORNER_TOP_RIGHT = 1,
ROUNDED_IMG_CORNER_BOTTOM_RIGHT = 2,
ROUNDED_IMG_CORNER_BOTTOM_LEFT = 3,
};
/**********************
* STATIC PROTOTYPES
**********************/
static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc);
static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius);
static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip);
static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
lv_coord_t radius);
static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius,
bool * in_cache);
static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
lv_coord_t radius);
static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc);
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src)
{
const lv_area_t * clip = draw_ctx->clip_area;
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
size_t key_size;
lv_draw_sdl_cache_key_head_img_t * key = lv_draw_sdl_texture_img_key_create(src, draw_dsc->frame_id, &key_size);
bool texture_found = false;
lv_draw_sdl_img_header_t * header = NULL;
SDL_Texture * texture = lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_size, &texture_found,
(void **) &header);
bool texture_in_cache = false;
if(!texture_found) {
lv_draw_sdl_img_load_texture(ctx, key, key_size, src, draw_dsc->frame_id, &texture, &header,
&texture_in_cache);
}
else {
texture_in_cache = true;
}
SDL_free(key);
if(!texture || !header) {
return LV_RES_INV;
}
lv_area_t zoomed_cords;
_lv_img_buf_get_transformed_area(&zoomed_cords, lv_area_get_width(coords), lv_area_get_height(coords), 0,
draw_dsc->zoom, &draw_dsc->pivot);
lv_area_move(&zoomed_cords, coords->x1, coords->y1);
/* When in > 0, draw simple radius */
lv_coord_t radius = 0;
/* Coords will be translated so coords will start at (0,0) */
lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area;
bool has_composite = false;
if(!check_mask_simple_radius(&t_coords, &radius)) {
has_composite = lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode,
&t_coords, &t_clip, &apply_area);
}
lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip);
SDL_Rect clip_rect, coords_rect;
lv_area_to_sdl_rect(&t_clip, &clip_rect);
lv_area_to_sdl_rect(&t_coords, &coords_rect);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
if(radius > 0) {
draw_img_rounded(ctx, texture, header, draw_dsc, &t_coords, &t_clip, radius);
}
else {
draw_img_simple(ctx, texture, header, draw_dsc, &t_coords, &t_clip);
}
lv_draw_sdl_composite_end(ctx, &apply_area, draw_dsc->blend_mode);
if(!texture_in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
if(!header->managed) {
SDL_DestroyTexture(texture);
}
lv_mem_free(header);
}
return LV_RES_OK;
}
static void calc_draw_part(SDL_Texture * texture, const lv_draw_sdl_img_header_t * header, const lv_area_t * coords,
const lv_area_t * clip, SDL_Rect * clipped_src, SDL_Rect * clipped_dst)
{
double x = 0, y = 0, w, h;
if(SDL_RectEmpty(&header->rect)) {
Uint32 format = 0;
int access = 0, tw, th;
SDL_QueryTexture(texture, &format, &access, &tw, &th);
w = tw;
h = th;
}
else {
x = header->rect.x;
y = header->rect.y;
w = header->rect.w;
h = header->rect.h;
}
if(clip) {
lv_area_t clipped_area;
_lv_area_intersect(&clipped_area, coords, clip);
lv_area_to_sdl_rect(&clipped_area, clipped_dst);
}
else {
lv_area_to_sdl_rect(coords, clipped_dst);
}
lv_coord_t coords_w = lv_area_get_width(coords), coords_h = lv_area_get_height(coords);
clipped_src->x = (int)(x + (clipped_dst->x - coords->x1) * w / coords_w);
clipped_src->y = (int)(y + (clipped_dst->y - coords->y1) * h / coords_h);
clipped_src->w = (int)(w - (coords_w - clipped_dst->w) * w / coords_w);
clipped_src->h = (int)(h - (coords_h - clipped_dst->h) * h / coords_h);
}
bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
const void * src, int32_t frame_id, SDL_Texture ** texture,
lv_draw_sdl_img_header_t ** header, bool * texture_in_cache)
{
_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, lv_color_white(), frame_id);
lv_draw_sdl_cache_flag_t tex_flags = 0;
SDL_Rect rect;
SDL_memset(&rect, 0, sizeof(SDL_Rect));
if(cdsc) {
lv_img_decoder_dsc_t * dsc = &cdsc->dec_dsc;
if(dsc->user_data && SDL_memcmp(dsc->user_data, LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD, 8) == 0) {
lv_draw_sdl_dec_dsc_userdata_t * ptr = (lv_draw_sdl_dec_dsc_userdata_t *) dsc->user_data;
*texture = ptr->texture;
rect = ptr->rect;
if(ptr->texture_managed) {
tex_flags |= LV_DRAW_SDL_CACHE_FLAG_MANAGED;
}
ptr->texture_referenced = true;
}
else {
*texture = upload_img_texture(ctx->renderer, dsc);
}
#if LV_IMG_CACHE_DEF_SIZE == 0
lv_img_decoder_close(dsc);
#endif
}
if(texture && cdsc) {
*header = lv_mem_alloc(sizeof(lv_draw_sdl_img_header_t));
SDL_memcpy(&(*header)->base, &cdsc->dec_dsc.header, sizeof(lv_img_header_t));
(*header)->rect = rect;
(*header)->managed = (tex_flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) != 0;
*texture_in_cache = lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_size, *texture, *header, SDL_free,
tex_flags);
return true;
}
else {
*texture_in_cache = lv_draw_sdl_texture_cache_put(ctx, key, key_size, NULL);
return false;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static SDL_Texture * upload_img_texture(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
{
if(!dsc->img_data) {
return upload_img_texture_fallback(renderer, dsc);
}
bool chroma_keyed = dsc->header.cf == (uint32_t) LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
int h = (int) dsc->header.h;
int w = (int) dsc->header.w;
void * data = (void *) dsc->img_data;
Uint32 rmask = 0x00FF0000;
Uint32 gmask = 0x0000FF00;
Uint32 bmask = 0x000000FF;
Uint32 amask = 0xFF000000;
if(chroma_keyed) {
amask = 0x00;
}
SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
rmask, gmask, bmask, amask);
SDL_SetColorKey(surface, chroma_keyed, lv_color_to32(LV_COLOR_CHROMA_KEY));
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return texture;
}
static SDL_Texture * upload_img_texture_fallback(SDL_Renderer * renderer, lv_img_decoder_dsc_t * dsc)
{
lv_coord_t h = (lv_coord_t) dsc->header.h;
lv_coord_t w = (lv_coord_t) dsc->header.w;
uint8_t * data = lv_mem_buf_get(w * h * sizeof(lv_color_t));
for(lv_coord_t y = 0; y < h; y++) {
lv_img_decoder_read_line(dsc, 0, y, w, &data[y * w * sizeof(lv_color_t)]);
}
Uint32 rmask = 0x00FF0000;
Uint32 gmask = 0x0000FF00;
Uint32 bmask = 0x000000FF;
Uint32 amask = 0xFF000000;
SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(data, w, h, LV_COLOR_DEPTH, w * LV_COLOR_DEPTH / 8,
rmask, gmask, bmask, amask);
SDL_SetColorKey(surface, SDL_TRUE, lv_color_to32(LV_COLOR_CHROMA_KEY));
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
lv_mem_buf_release(data);
return texture;
}
/**
* Check if there is only one radius mask
* @param radius Set to radius value if the only mask is a radius mask
* @return true if the only mask is a radius mask
*/
static bool check_mask_simple_radius(const lv_area_t * coords, lv_coord_t * radius)
{
if(lv_draw_mask_get_cnt() != 1) return false;
for(uint8_t i = 0; i < _LV_MASK_MAX_NUM; i++) {
_lv_draw_mask_common_dsc_t * param = LV_GC_ROOT(_lv_draw_mask_list[i]).param;
if(param->type == LV_DRAW_MASK_TYPE_RADIUS) {
lv_draw_mask_radius_param_t * rparam = (lv_draw_mask_radius_param_t *) param;
if(rparam->cfg.outer) return false;
if(!_lv_area_is_equal(&rparam->cfg.rect, coords)) return false;
*radius = rparam->cfg.radius;
return true;
}
}
return false;
}
static void draw_img_simple(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip)
{
apply_recolor_opa(texture, draw_dsc);
SDL_Point pivot = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
/*Image needs to be rotated, so we have to use clip rect which is slower*/
if(draw_dsc->angle != 0) {
/* No radius, set clip here */
SDL_Rect clip_rect;
lv_area_to_sdl_rect(clip, &clip_rect);
SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
}
SDL_Rect src_rect, dst_rect;
calc_draw_part(texture, header, coords, clip, &src_rect, &dst_rect);
SDL_RenderCopyEx(ctx->renderer, texture, &src_rect, &dst_rect, draw_dsc->angle, &pivot, SDL_FLIP_NONE);
if(draw_dsc->angle != 0) {
SDL_RenderSetClipRect(ctx->renderer, NULL);
}
}
static void draw_img_rounded(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture, const lv_draw_sdl_img_header_t * header,
const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const lv_area_t * clip,
lv_coord_t radius)
{
const int w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_coord_t real_radius = LV_MIN3(radius, w, h);
bool frag_in_cache = false;
SDL_Texture * frag = img_rounded_frag_obtain(ctx, texture, header, w, h, real_radius, &frag_in_cache);
apply_recolor_opa(frag, draw_dsc);
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, frag, real_radius, coords, clip, true);
apply_recolor_opa(texture, draw_dsc);
SDL_Rect src_rect, dst_rect;
/* Draw 3 parts */
lv_area_t clip_tmp, part;
calc_draw_part(texture, header, coords, NULL, &src_rect, &dst_rect);
for(int i = w > h ? ROUNDED_IMG_PART_LEFT : ROUNDED_IMG_PART_TOP, j = i + 3; i <= j; i++) {
switch(i) {
case ROUNDED_IMG_PART_LEFT:
lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x1 + radius - 1, coords->y2 - radius);
break;
case ROUNDED_IMG_PART_HCENTER:
lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y2);
break;
case ROUNDED_IMG_PART_RIGHT:
lv_area_set(&part, coords->x2 - radius + 1, coords->y1 + radius, coords->x2, coords->y2 - radius);
break;
case ROUNDED_IMG_PART_TOP:
lv_area_set(&part, coords->x1 + radius, coords->y1, coords->x2 - radius, coords->y1 + radius - 1);
break;
case ROUNDED_IMG_PART_VCENTER:
lv_area_set(&part, coords->x1 + radius, coords->y2 - radius + 1, coords->x2 - radius, coords->y2);
break;
case ROUNDED_IMG_PART_BOTTOM:
lv_area_set(&part, coords->x1, coords->y1 + radius, coords->x2, coords->y2 - radius);
break;
default:
break;
}
if(!_lv_area_intersect(&clip_tmp, &part, clip)) continue;
SDL_Rect clip_rect;
lv_area_to_sdl_rect(&clip_tmp, &clip_rect);
SDL_RenderSetClipRect(ctx->renderer, &clip_rect);
SDL_RenderCopy(ctx->renderer, texture, &src_rect, &dst_rect);
}
SDL_RenderSetClipRect(ctx->renderer, NULL);
if(!frag_in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(frag);
}
}
static void apply_recolor_opa(SDL_Texture * texture, const lv_draw_img_dsc_t * draw_dsc)
{
if(draw_dsc->recolor_opa > LV_OPA_TRANSP) {
/* Draw with mixed recolor */
lv_color_t recolor = lv_color_mix(draw_dsc->recolor, lv_color_white(), draw_dsc->recolor_opa);
SDL_SetTextureColorMod(texture, recolor.ch.red, recolor.ch.green, recolor.ch.blue);
}
else {
/* Draw with no recolor */
SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
}
SDL_SetTextureAlphaMod(texture, draw_dsc->opa);
}
static SDL_Texture * img_rounded_frag_obtain(lv_draw_sdl_ctx_t * ctx, SDL_Texture * texture,
const lv_draw_sdl_img_header_t * header, int w, int h, lv_coord_t radius,
bool * in_cache)
{
lv_draw_img_rounded_key_t key = rounded_key_create(texture, w, h, radius);
bool mask_frag_in_cache = false;
SDL_Texture * mask_frag = lv_draw_sdl_rect_bg_frag_obtain(ctx, radius, &mask_frag_in_cache);
SDL_Texture * img_frag = lv_draw_sdl_texture_cache_get(ctx, &key, sizeof(key), NULL);
if(img_frag == NULL) {
const lv_coord_t full_frag_size = radius * 2 + 3;
img_frag = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
full_frag_size, full_frag_size);
SDL_assert(img_frag);
SDL_SetTextureBlendMode(img_frag, SDL_BLENDMODE_BLEND);
SDL_Texture * old_target = SDL_GetRenderTarget(ctx->renderer);
SDL_SetRenderTarget(ctx->renderer, img_frag);
SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 0);
/* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_NONE);
SDL_RenderFillRect(ctx->renderer, NULL);
SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_BLEND);
lv_area_t coords = {0, 0, w - 1, h - 1};
lv_area_t frag_coords = {0, 0, full_frag_size - 1, full_frag_size - 1};
lv_draw_sdl_rect_bg_frag_draw_corners(ctx, mask_frag, radius, &frag_coords, NULL, false);
SDL_SetTextureAlphaMod(texture, 0xFF);
SDL_SetTextureColorMod(texture, 0xFF, 0xFF, 0xFF);
#if LV_GPU_SDL_CUSTOM_BLEND_MODE
SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ZERO,
SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_DST_ALPHA,
SDL_BLENDFACTOR_ZERO, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, blend_mode);
#else
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
#endif
SDL_Rect srcrect, cliprect, dstrect = {0, 0, radius, radius};
cliprect.w = cliprect.h = radius;
for(int i = 0; i <= ROUNDED_IMG_CORNER_BOTTOM_LEFT; i++) {
switch(i) {
case ROUNDED_IMG_CORNER_TOP_LEFT:
cliprect.x = 0;
cliprect.y = 0;
lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_LEFT, 0, 0);
break;
case ROUNDED_IMG_CORNER_TOP_RIGHT:
cliprect.x = full_frag_size - radius;
cliprect.y = 0;
lv_area_align(&frag_coords, &coords, LV_ALIGN_TOP_RIGHT, 0, 0);
break;
case ROUNDED_IMG_CORNER_BOTTOM_RIGHT:
cliprect.x = full_frag_size - radius;
cliprect.y = full_frag_size - radius;
lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
break;
case ROUNDED_IMG_CORNER_BOTTOM_LEFT:
cliprect.x = 0;
cliprect.y = full_frag_size - radius;
lv_area_align(&frag_coords, &coords, LV_ALIGN_BOTTOM_LEFT, 0, 0);
break;
default:
break;
}
calc_draw_part(texture, header, &coords, NULL, &srcrect, &dstrect);
SDL_RenderSetClipRect(ctx->renderer, &cliprect);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
}
SDL_RenderSetClipRect(ctx->renderer, NULL);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(ctx->renderer, old_target);
*in_cache = lv_draw_sdl_texture_cache_put(ctx, &key, sizeof(key), img_frag);
}
else {
*in_cache = true;
}
if(!mask_frag_in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(mask_frag);
}
return img_frag;
}
static lv_draw_img_rounded_key_t rounded_key_create(const SDL_Texture * texture, lv_coord_t w, lv_coord_t h,
lv_coord_t radius)
{
lv_draw_img_rounded_key_t key;
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS;
key.texture = texture;
key.w = w;
key.h = h;
key.radius = radius;
return key;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,86 @@
/**
* @file lv_draw_sdl_img.h
*
*/
#ifndef LV_DRAW_SDL_IMG_H
#define LV_DRAW_SDL_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_img_header_t {
lv_img_header_t base;
SDL_Rect rect;
bool managed;
} lv_draw_sdl_img_header_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**
*
* @param ctx Drawing context
* @param key Texture cache key
* @param key_size Size of texture cache key
* @param src Image source pointer
* @param frame_id Frame ID for animated image
* @param texture Texture for render
* @param header Header also holds sdl image info
* @param texture_in_cache Whether the texture has been put in the cache. Please note for managed texture,
* this will be false too. So you'll need to check header.managed too.
* @return Whether the image has been loaded successfully
*/
bool lv_draw_sdl_img_load_texture(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_cache_key_head_img_t * key, size_t key_size,
const void * src, int32_t frame_id, SDL_Texture ** texture,
lv_draw_sdl_img_header_t ** header, bool * texture_in_cache);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_IMG_H*/

View File

@@ -0,0 +1,189 @@
/**
* @file lv_draw_sdl_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw_label.h"
#include "../../misc/lv_utils.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_layer.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
const lv_font_t * font_p;
uint32_t letter;
} lv_font_glyph_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter)
{
const lv_area_t * clip_area = draw_ctx->clip_area;
const lv_font_t * font_p = dsc->font;
lv_opa_t opa = dsc->opa;
lv_color_t color = dsc->color;
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("lv_draw_letter: font is NULL");
return;
}
lv_font_glyph_dsc_t g;
bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
if(g_ret == false) {
/*Add warning if the dsc is not found
*but do not print warning for non printable ASCII chars (e.g. '\n')*/
if(letter >= 0x20 &&
letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter);
/* draw placeholder */
lv_area_t glyph_coords;
lv_draw_rect_dsc_t glyph_dsc;
lv_coord_t begin_x = pos_p->x + g.ofs_x;
lv_coord_t begin_y = pos_p->y + g.ofs_y;
lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h);
lv_draw_rect_dsc_init(&glyph_dsc);
glyph_dsc.bg_opa = LV_OPA_MIN;
glyph_dsc.outline_opa = LV_OPA_MIN;
glyph_dsc.shadow_opa = LV_OPA_MIN;
glyph_dsc.bg_img_opa = LV_OPA_MIN;
glyph_dsc.border_color = dsc->color;
glyph_dsc.border_width = 1;
draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords);
}
return;
}
/*Don't draw anything if the character is empty. E.g. space*/
if((g.box_h == 0) || (g.box_w == 0)) return;
int32_t pos_x = pos_p->x + g.ofs_x;
int32_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
const lv_area_t letter_area = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1};
lv_area_t draw_area;
/*If the letter is completely out of mask don't draw it*/
if(!_lv_area_intersect(&draw_area, &letter_area, clip_area)) {
return;
}
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
SDL_Renderer * renderer = ctx->renderer;
lv_font_glyph_key_t glyph_key = font_key_glyph_create(font_p, letter);
bool glyph_found = false;
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(ctx, &glyph_key, sizeof(glyph_key), &glyph_found);
bool in_cache = false;
if(!glyph_found) {
if(g.resolved_font) {
font_p = g.resolved_font;
}
const uint8_t * bmp = lv_font_get_glyph_bitmap(font_p, letter);
uint8_t * buf = lv_mem_alloc(g.box_w * g.box_h);
lv_sdl_to_8bpp(buf, bmp, g.box_w, g.box_h, g.box_w, g.bpp);
SDL_Surface * mask = lv_sdl_create_opa_surface(buf, g.box_w, g.box_h, g.box_w);
texture = SDL_CreateTextureFromSurface(renderer, mask);
SDL_FreeSurface(mask);
lv_mem_free(buf);
in_cache = lv_draw_sdl_texture_cache_put(ctx, &glyph_key, sizeof(glyph_key), texture);
}
else {
in_cache = true;
}
if(!texture) {
return;
}
lv_area_t t_letter = letter_area, t_clip = *clip_area, apply_area;
bool has_composite = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter,
&t_clip, &apply_area);
lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_letter, &t_clip);
/*If the letter is completely out of mask don't draw it*/
if(!_lv_area_intersect(&draw_area, &t_letter, &t_clip)) {
if(!in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(texture);
}
return;
}
SDL_Rect srcrect, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
srcrect.x = draw_area.x1 - t_letter.x1;
srcrect.y = draw_area.y1 - t_letter.y1;
srcrect.w = dstrect.w;
srcrect.h = dstrect.h;
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, opa);
SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue);
SDL_RenderCopy(renderer, texture, &srcrect, &dstrect);
lv_draw_sdl_composite_end(ctx, &apply_area, dsc->blend_mode);
if(!in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(texture);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter)
{
lv_font_glyph_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH;
key.font_p = font_p;
key.letter = letter;
return key;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,147 @@
/**
* @file lv_draw_sdl_refr.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../core/lv_refr.h"
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_priv.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_layer.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags)
{
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
SDL_Renderer * renderer = ctx->renderer;
lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx;
transform_ctx->flags = flags;
transform_ctx->orig_target = SDL_GetRenderTarget(renderer);
lv_coord_t target_w = lv_area_get_width(&layer_ctx->area_full);
lv_coord_t target_h = lv_area_get_height(&layer_ctx->area_full);
enum lv_draw_sdl_composite_texture_id_t texture_id = LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0 +
ctx->internals->transform_count;
transform_ctx->target = lv_draw_sdl_composite_texture_obtain(ctx, texture_id, target_w, target_h,
&transform_ctx->target_in_cache);
transform_ctx->target_rect.x = 0;
transform_ctx->target_rect.y = 0;
transform_ctx->target_rect.w = target_w;
transform_ctx->target_rect.h = target_h;
layer_ctx->max_row_with_alpha = target_h;
layer_ctx->max_row_with_no_alpha = target_h;
SDL_SetTextureBlendMode(transform_ctx->target, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(renderer, transform_ctx->target);
/* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderFillRect(renderer, NULL);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
/* Set proper drawing context for transform layer */
ctx->internals->transform_count += 1;
draw_ctx->buf_area = &layer_ctx->area_full;
draw_ctx->clip_area = &layer_ctx->area_full;
return layer_ctx;
}
void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
const lv_draw_img_dsc_t * draw_dsc)
{
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx;
SDL_Renderer * renderer = ctx->renderer;
SDL_Rect trans_rect;
if(transform_ctx->flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
lv_area_zoom_to_sdl_rect(&layer_ctx->area_act, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot);
}
else {
lv_area_zoom_to_sdl_rect(&layer_ctx->area_full, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot);
}
SDL_SetRenderTarget(renderer, transform_ctx->orig_target);
/*Render off-screen texture, transformed*/
SDL_Rect clip_rect;
lv_area_to_sdl_rect(layer_ctx->original.clip_area, &clip_rect);
SDL_Point center = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y};
SDL_RenderSetClipRect(renderer, &clip_rect);
SDL_SetTextureAlphaMod(transform_ctx->target, draw_dsc->opa);
SDL_RenderCopyEx(renderer, transform_ctx->target, &transform_ctx->target_rect, &trans_rect,
draw_dsc->angle, &center, SDL_FLIP_NONE);
SDL_RenderSetClipRect(renderer, NULL);
}
void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx)
{
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx;
if(!transform_ctx->target_in_cache && transform_ctx->target != NULL) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(transform_ctx->target);
}
ctx->internals->transform_count -= 1;
}
void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area,
lv_area_t * coords, lv_area_t * clip)
{
if(ctx->internals->transform_count == 0) {
return;
}
lv_area_t * area = ctx->base_draw.buf_area;
lv_area_move(coords, -area->x1, -area->y1);
lv_area_move(clip, -area->x1, -area->y1);
if(has_composite) {
lv_area_move(apply_area, -area->x1, -area->y1);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,56 @@
/**
* @file lv_draw_sdl_refr.h
*
*/
#ifndef LV_TEMPL_H
#define LV_TEMPL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_sdl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_draw_sdl_layer_ctx_t {
lv_draw_layer_ctx_t base;
SDL_Texture * orig_target;
SDL_Texture * target;
SDL_Rect target_rect;
bool target_in_cache;
lv_draw_layer_flags_t flags;
} lv_draw_sdl_layer_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * transform_ctx,
const lv_draw_img_dsc_t * draw_dsc);
void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx);
void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area,
lv_area_t * coords, lv_area_t * clip);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEMPL_H*/

View File

@@ -0,0 +1,157 @@
/**
* @file lv_draw_sdl_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
#include "lv_draw_sdl_mask.h"
/*********************
* DEFINES
*********************/
#define ROUND_START 0x01
#define ROUND_END 0x02
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_coord_t length;
lv_coord_t width;
uint8_t round;
} lv_draw_line_key_t;
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length);
static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc,
lv_coord_t length);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_draw_line(lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2, bool * in_cache)
{
lv_draw_sdl_ctx_t * sdl_ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
SDL_Renderer * renderer = sdl_ctx->renderer;
lv_coord_t x1 = point1->x, x2 = point2->x, y1 = point1->y, y2 = point2->y;
double length = SDL_sqrt(SDL_pow(x2 - x1, 2) + SDL_pow(y2 - y1, 2));
if(length - (long) length > 0.5) {
length = (long) length + 1;
}
double angle = SDL_atan2(y2 - y1, x2 - x1) * 180 / M_PI;
lv_draw_line_key_t key = line_key_create(dsc, (lv_coord_t) length);
SDL_Texture * texture = lv_draw_sdl_texture_cache_get(sdl_ctx, &key, sizeof(key), NULL);
if(!texture) {
texture = line_texture_create(sdl_ctx, dsc, (lv_coord_t) length);
*in_cache = lv_draw_sdl_texture_cache_put(sdl_ctx, &key, sizeof(key), texture);
}
lv_area_t coords = {x1, y1, x2, y2};
const lv_area_t * clip = draw_ctx->clip_area;
SDL_Rect coords_r, clip_r;
lv_area_to_sdl_rect(&coords, &coords_r);
lv_area_to_sdl_rect(clip, &clip_r);
lv_area_t t_coords = coords, t_clip = *clip, apply_area;
lv_area_t extension = {dsc->width / 2, dsc->width / 2, dsc->width / 2, dsc->width / 2};
lv_draw_sdl_composite_begin(sdl_ctx, &coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip,
&apply_area);
SDL_Color color;
lv_color_to_sdl_color(&dsc->color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, dsc->opa);
SDL_Rect srcrect = {0, 0, (int) length + dsc->width + 2, dsc->width + 2},
dstrect = {t_coords.x1 - 1 - dsc->width / 2, t_coords.y1 - 1, srcrect.w, srcrect.h};
SDL_Point center = {1 + dsc->width / 2, 1 + dsc->width / 2};
SDL_Rect clip_rect;
lv_area_to_sdl_rect(&t_clip, &clip_rect);
if(!SDL_RectEquals(&clip_rect, &dstrect) || angle != 0) {
SDL_RenderSetClipRect(renderer, &clip_rect);
}
SDL_RenderCopyEx(renderer, texture, &srcrect, &dstrect, angle, &center, 0);
SDL_RenderSetClipRect(renderer, NULL);
lv_draw_sdl_composite_end(sdl_ctx, &apply_area, dsc->blend_mode);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_draw_line_key_t line_key_create(const lv_draw_line_dsc_t * dsc, lv_coord_t length)
{
lv_draw_line_key_t key;
lv_memset_00(&key, sizeof(lv_draw_line_key_t));
key.magic = LV_GPU_CACHE_KEY_MAGIC_LINE;
key.length = length;
key.width = dsc->width;
key.round = (dsc->round_start ? ROUND_START : 0) | (dsc->round_end ? ROUND_END : 0);
return key;
}
static SDL_Texture * line_texture_create(lv_draw_sdl_ctx_t * sdl_ctx, const lv_draw_line_dsc_t * dsc, lv_coord_t length)
{
SDL_Texture * texture = SDL_CreateTexture(sdl_ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, SDL_TEXTUREACCESS_TARGET,
length + dsc->width + 2, dsc->width + 2);
SDL_Texture * target = SDL_GetRenderTarget(sdl_ctx->renderer);
SDL_SetRenderTarget(sdl_ctx->renderer, texture);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0x0);
/* SDL_RenderClear is not working properly, so we overwrite the target with solid color */
SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_NONE);
SDL_RenderFillRect(sdl_ctx->renderer, NULL);
SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_Rect line_rect = {1 + dsc->width / 2, 1, length, dsc->width};
SDL_RenderFillRect(sdl_ctx->renderer, &line_rect);
if(dsc->round_start || dsc->round_end) {
lv_draw_mask_radius_param_t param;
lv_area_t round_area = {0, 0, dsc->width - 1, dsc->width - 1};
lv_draw_mask_radius_init(&param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_id = lv_draw_mask_add(&param, NULL);
SDL_Texture * round_texture = lv_draw_sdl_mask_dump_texture(sdl_ctx->renderer, &round_area, &mask_id, 1);
lv_draw_mask_remove_id(mask_id);
SDL_Rect round_src = {0, 0, dsc->width, dsc->width};
SDL_Rect round_dst = {line_rect.x - dsc->width / 2, 1, dsc->width, dsc->width};
SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
round_dst.x = line_rect.w + dsc->width / 2;
SDL_RenderCopy(sdl_ctx->renderer, round_texture, &round_src, &round_dst);
SDL_DestroyTexture(round_texture);
}
SDL_SetRenderTarget(sdl_ctx->renderer, target);
return texture;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,84 @@
/**
* @file lv_draw_sdl_mask.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_gc.h"
#include "lv_draw_sdl_mask.h"
#include "lv_draw_sdl_utils.h"
/*********************
* DEFINES
*********************/
#ifndef HAVE_SDL_CUSTOM_BLEND_MODE
#define HAVE_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count)
{
SDL_assert(coords->x2 >= coords->x1);
SDL_assert(coords->y2 >= coords->y1);
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_opa_t * mask_buf = lv_mem_buf_get(w * h);
for(lv_coord_t y = 0; y < h; y++) {
lv_opa_t * line_buf = &mask_buf[y * w];
lv_memset_ff(line_buf, w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) w;
lv_draw_mask_res_t res;
if(ids) {
res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
}
else {
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
}
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(line_buf, w);
}
}
return mask_buf;
}
SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
int16_t ids_count)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
lv_opa_t * mask_buf = lv_draw_sdl_mask_dump_opa(coords, ids, ids_count);
SDL_Surface * surface = lv_sdl_create_opa_surface(mask_buf, w, h, w);
lv_mem_buf_release(mask_buf);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return texture;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,51 @@
/**
* @file lv_draw_sdl_mask.h
*
*/
#ifndef LV_DRAW_SDL_MASK_H
#define LV_DRAW_SDL_MASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_opa_t * lv_draw_sdl_mask_dump_opa(const lv_area_t * coords, const int16_t * ids, int16_t ids_count);
SDL_Texture * lv_draw_sdl_mask_dump_texture(SDL_Renderer * renderer, const lv_area_t * coords, const int16_t * ids,
int16_t ids_count);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_MASK_H*/

View File

@@ -0,0 +1,139 @@
/**
* @file lv_draw_sdl_polygon.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_utils.h"
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_composite.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_polygon(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points,
uint16_t point_cnt)
{
if(point_cnt < 3) return;
if(points == NULL) return;
lv_draw_mask_polygon_param_t polygon_param;
lv_draw_mask_polygon_init(&polygon_param, points, point_cnt);
if(polygon_param.cfg.point_cnt < 3) {
lv_draw_mask_free_param(&polygon_param);
return;
}
lv_area_t poly_coords = {.x1 = LV_COORD_MAX, .y1 = LV_COORD_MAX, .x2 = LV_COORD_MIN, .y2 = LV_COORD_MIN};
uint16_t i;
for(i = 0; i < point_cnt; i++) {
poly_coords.x1 = LV_MIN(poly_coords.x1, polygon_param.cfg.points[i].x);
poly_coords.y1 = LV_MIN(poly_coords.y1, polygon_param.cfg.points[i].y);
poly_coords.x2 = LV_MAX(poly_coords.x2, polygon_param.cfg.points[i].x);
poly_coords.y2 = LV_MAX(poly_coords.y2, polygon_param.cfg.points[i].y);
}
bool is_common;
lv_area_t draw_area;
is_common = _lv_area_intersect(&draw_area, &poly_coords, draw_ctx->clip_area);
if(!is_common) {
lv_draw_mask_free_param(&polygon_param);
return;
}
lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
int16_t mask_id = lv_draw_mask_add(&polygon_param, NULL);
lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
bool texture_in_cache = false;
SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h,
&texture_in_cache);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
dump_masks(texture, &draw_area);
lv_draw_mask_remove_id(mask_id);
lv_draw_mask_free_param(&polygon_param);
SDL_Rect srcrect = {0, 0, w, h}, dstrect;
lv_area_to_sdl_rect(&draw_area, &dstrect);
SDL_Color color;
lv_color_to_sdl_color(&draw_dsc->bg_color, &color);
SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture, draw_dsc->bg_opa);
SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
if(!texture_in_cache) {
LV_LOG_WARN("Texture is not cached, this will impact performance.");
SDL_DestroyTexture(texture);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void dump_masks(SDL_Texture * texture, const lv_area_t * coords)
{
lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
SDL_assert(w > 0 && h > 0);
SDL_Rect rect = {0, 0, w, h};
uint8_t * pixels;
int pitch;
if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
for(lv_coord_t y = 0; y < rect.h; y++) {
lv_memset_ff(line_buf, rect.w);
lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
lv_draw_mask_res_t res;
res = lv_draw_mask_apply(line_buf, abs_x, abs_y, len);
if(res == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(&pixels[y * pitch], 4 * rect.w);
}
else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
}
else {
for(int x = 0; x < rect.w; x++) {
uint8_t * pixel = &pixels[y * pitch + x * 4];
*pixel = line_buf[x];
pixel[1] = pixel[2] = pixel[3] = 0xFF;
}
}
}
lv_mem_buf_release(line_buf);
SDL_UnlockTexture(texture);
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,73 @@
/**
* @file lv_draw_sdl_priv.h
*
*/
#ifndef LV_DRAW_SDL_PRIV_H
#define LV_DRAW_SDL_PRIV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "../../misc/lv_lru.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_context_internals_t {
lv_lru_t * texture_cache;
SDL_Texture * mask;
SDL_Texture * composition;
bool composition_cached;
SDL_Texture * target_backup;
uint8_t transform_count;
} lv_draw_sdl_context_internals_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_PRIV_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
/**
* @file lv_draw_sdl_rect.h
*
*/
#ifndef LV_DRAW_SDL_RECT_H
#define LV_DRAW_SDL_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "../lv_draw.h"
#include "lv_draw_sdl_texture_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct lv_draw_sdl_rect_header_t {
lv_img_header_t base;
SDL_Rect rect;
} lv_draw_sdl_rect_header_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**
*
* @param ctx Drawing context
* @param radius Corner radius of the rectangle
* @param in_cache Whether the texture has been put in the cache
* @return Background fragment texture
*/
SDL_Texture * lv_draw_sdl_rect_bg_frag_obtain(lv_draw_sdl_ctx_t * ctx, lv_coord_t radius, bool * in_cache);
/**
*
* @param ctx Drawing context
* @param grad Gradient info
* @param w Width of the rectangle
* @param h Height of the rectangle
* @param radius Corner radius of the rectangle
* @param in_cache Whether the texture has been put in the cache
* @return Gradient fragment texture
*/
SDL_Texture * lv_draw_sdl_rect_grad_frag_obtain(lv_draw_sdl_ctx_t * ctx, const lv_grad_dsc_t * grad, lv_coord_t w,
lv_coord_t h, lv_coord_t radius, bool * in_cache);
/**
*
* @param ctx Drawing context
* @param grad Gradient info
* @param in_cache Whether the texture has been put in the cache
* @return Gradient strip texture
*/
SDL_Texture * lv_draw_sdl_rect_grad_strip_obtain(lv_draw_sdl_ctx_t * ctx, const lv_grad_dsc_t * grad, bool * in_cache);
void lv_draw_sdl_rect_bg_frag_draw_corners(lv_draw_sdl_ctx_t * ctx, SDL_Texture * frag, lv_coord_t frag_size,
const lv_area_t * coords, const lv_area_t * clip, bool full);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_RECT_H*/

View File

@@ -0,0 +1,249 @@
/**
* @file lv_draw_sdl_stack_blur.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sdl_stack_blur.h"
#if LV_USE_GPU_SDL
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void
stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, int step);
/**********************
* STATIC VARIABLES
**********************/
// Based heavily on http://vitiy.info/Code/stackblur.cpp
// See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
static unsigned short const stackblur_mul[255] = {
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
};
static unsigned char const stackblur_shr[255] = {
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r)
{
stack_blur_job(buf, w, h, r, 1, 0, 1);
stack_blur_job(buf, w, h, r, 1, 0, 2);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void stack_blur_job(lv_opa_t * src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core,
int step)
{
if(radius < 2 || radius > 254) {
/* Silently ignore bad radius */
return;
}
unsigned int x, y, xp, yp, i;
unsigned int sp;
unsigned int stack_start;
unsigned char * stack_ptr;
lv_opa_t * src_ptr;
lv_opa_t * dst_ptr;
unsigned long sum_r;
unsigned long sum_in_r;
unsigned long sum_out_r;
unsigned int wm = w - 1;
unsigned int hm = h - 1;
unsigned int stride = w;
unsigned int div = (radius * 2) + 1;
unsigned int mul_sum = stackblur_mul[radius];
unsigned char shr_sum = stackblur_shr[radius];
unsigned char stack[254 * 2 + 1];
if(step == 1) {
unsigned int minY = core * h / cores;
unsigned int maxY = (core + 1) * h / cores;
for(y = minY; y < maxY; y++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + stride * y; // start of line (0,y)
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= wm) src_ptr += 1;
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
xp = radius;
if(xp > wm) xp = wm;
src_ptr = src + (xp + y * w); // img.pix_ptr(xp, y);
dst_ptr = src + y * stride; // img.pix_ptr(0, y);
for(x = 0; x < w; x++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += 1;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(xp < wm) {
src_ptr += 1;
++xp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
// step 2
if(step == 2) {
unsigned int minX = core * w / cores;
unsigned int maxX = (core + 1) * w / cores;
for(x = minX; x < maxX; x++) {
sum_r =
sum_in_r =
sum_out_r = 0;
src_ptr = src + x; // x,0
for(i = 0; i <= radius; i++) {
stack_ptr = &stack[i];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (i + 1);
sum_out_r += src_ptr[0];
}
for(i = 1; i <= radius; i++) {
if(i <= hm) src_ptr += stride; // +stride
stack_ptr = &stack[i + radius];
stack_ptr[0] = src_ptr[0];
sum_r += src_ptr[0] * (radius + 1 - i);
sum_in_r += src_ptr[0];
}
sp = radius;
yp = radius;
if(yp > hm) yp = hm;
src_ptr = src + (x + yp * w); // img.pix_ptr(x, yp);
dst_ptr = src + x; // img.pix_ptr(x, 0);
for(y = 0; y < h; y++) {
dst_ptr[0] = LV_CLAMP((sum_r * mul_sum) >> shr_sum, 0, 255);
dst_ptr += stride;
sum_r -= sum_out_r;
stack_start = sp + div - radius;
if(stack_start >= div) stack_start -= div;
stack_ptr = &stack[stack_start];
sum_out_r -= stack_ptr[0];
if(yp < hm) {
src_ptr += stride; // stride
++yp;
}
stack_ptr[0] = src_ptr[0];
sum_in_r += src_ptr[0];
sum_r += sum_in_r;
++sp;
if(sp >= div) sp = 0;
stack_ptr = &stack[sp];
sum_out_r += stack_ptr[0];
sum_in_r -= stack_ptr[0];
}
}
}
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,46 @@
/**
* @file lv_draw_sdl_stack_blur.h
*
*/
#ifndef LV_DRAW_SDL_STACK_BLUR_H
#define LV_DRAW_SDL_STACK_BLUR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_stack_blur_grayscale(lv_opa_t * buf, uint16_t w, uint16_t h, uint16_t r);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_STACK_BLUR_H*/

View File

@@ -0,0 +1,176 @@
/**
* @file lv_draw_sdl_texture_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl_texture_cache.h"
#include "lv_draw_sdl_utils.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
SDL_Texture * texture;
void * userdata;
lv_lru_free_t * userdata_free;
lv_draw_sdl_cache_flag_t flags;
} draw_cache_value_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
} temp_texture_key_t;
typedef struct {
lv_coord_t width, height;
} temp_texture_userdata_t;
static void draw_cache_free_value(draw_cache_value_t *);
static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx)
{
ctx->internals->texture_cache = lv_lru_create(LV_GPU_SDL_LRU_SIZE, 65536,
(lv_lru_free_t *) draw_cache_free_value, NULL);
}
void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx)
{
lv_lru_del(ctx->internals->texture_cache);
}
SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found)
{
return lv_draw_sdl_texture_cache_get_with_userdata(ctx, key, key_length, found, NULL);
}
SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found, void ** userdata)
{
draw_cache_value_t * value = draw_cache_get_entry(ctx, key, key_length, found);
if(!value) return NULL;
if(userdata) {
*userdata = value->userdata;
}
return value->texture;
}
bool lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture)
{
return lv_draw_sdl_texture_cache_put_advanced(ctx, key, key_length, texture, NULL, NULL, 0);
}
bool lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
SDL_Texture * texture, void * userdata, void userdata_free(void *),
lv_draw_sdl_cache_flag_t flags)
{
lv_lru_t * lru = ctx->internals->texture_cache;
draw_cache_value_t * value = SDL_malloc(sizeof(draw_cache_value_t));
value->texture = texture;
value->userdata = userdata;
value->userdata_free = userdata_free;
value->flags = flags;
if(!texture) {
return lv_lru_set(lru, key, key_length, value, 1) == LV_LRU_OK;
}
if(flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) {
/* Managed texture doesn't count into cache size */
LV_LOG_INFO("cache texture %p", texture);
return lv_lru_set(lru, key, key_length, value, 1) == LV_LRU_OK;
}
Uint32 format;
int access, width, height;
if(SDL_QueryTexture(texture, &format, &access, &width, &height) != 0) {
return false;
}
LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format));
return lv_lru_set(lru, key, key_length, value, width * height * SDL_BITSPERPIXEL(format) / 8) == LV_LRU_OK;
}
lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id, size_t * size)
{
lv_draw_sdl_cache_key_head_img_t header;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&header, 0, sizeof(header));
header.magic = LV_GPU_CACHE_KEY_MAGIC_IMG;
header.type = lv_img_src_get_type(src);
header.frame_id = frame_id;
void * key;
size_t key_size;
if(header.type == LV_IMG_SRC_FILE || header.type == LV_IMG_SRC_SYMBOL) {
size_t srclen = SDL_strlen(src);
key_size = sizeof(header) + srclen;
key = SDL_malloc(key_size);
SDL_memcpy(key, &header, sizeof(header));
/*Copy string content as key value*/
SDL_memcpy(key + sizeof(header), src, srclen);
}
else {
key_size = sizeof(header) + sizeof(void *);
key = SDL_malloc(key_size);
SDL_memcpy(key, &header, sizeof(header));
/*Copy address number as key value*/
SDL_memcpy(key + sizeof(header), &src, sizeof(void *));
}
*size = key_size;
return (lv_draw_sdl_cache_key_head_img_t *) key;
}
static void draw_cache_free_value(draw_cache_value_t * value)
{
if(value->texture && !(value->flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED)) {
LV_LOG_INFO("destroy texture %p", value->texture);
SDL_DestroyTexture(value->texture);
}
if(value->userdata_free) {
value->userdata_free(value->userdata);
}
SDL_free(value);
}
static draw_cache_value_t * draw_cache_get_entry(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found)
{
lv_lru_t * lru = ctx->internals->texture_cache;
draw_cache_value_t * value = NULL;
lv_lru_get(lru, key, key_length, (void **) &value);
if(!value) {
if(found) {
*found = false;
}
return NULL;
}
if(found) {
*found = true;
}
return value;
}
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,109 @@
/**
* @file lv_draw_sdl_texture_cache.h
*
*/
#ifndef LV_DRAW_SDL_TEXTURE_CACHE_H
#define LV_DRAW_SDL_TEXTURE_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include LV_GPU_SDL_INCLUDE_PATH
#include "lv_draw_sdl.h"
#include "lv_draw_sdl_priv.h"
#include "../../draw/lv_img_decoder.h"
#include "../../misc/lv_area.h"
/*********************
* DEFINES
*********************/
#define LV_DRAW_SDL_DEC_DSC_TEXTURE_HEAD "@LVSDLTex"
/**********************
* TYPEDEFS
**********************/
typedef struct {
char head[8];
SDL_Texture * texture;
SDL_Rect rect;
bool texture_managed;
bool texture_referenced;
} lv_draw_sdl_dec_dsc_userdata_t;
typedef enum {
LV_GPU_CACHE_KEY_MAGIC_ARC = 0x01,
LV_GPU_CACHE_KEY_MAGIC_IMG = 0x11,
LV_GPU_CACHE_KEY_MAGIC_IMG_ROUNDED_CORNERS = 0x12,
LV_GPU_CACHE_KEY_MAGIC_LINE = 0x21,
LV_GPU_CACHE_KEY_MAGIC_RECT_BG = 0x31,
LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW = 0x32,
LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER = 0x33,
LV_GPU_CACHE_KEY_MAGIC_RECT_GRAD = 0x34,
LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH = 0x41,
LV_GPU_CACHE_KEY_MAGIC_MASK = 0x51,
} lv_sdl_cache_key_magic_t;
typedef enum {
LV_DRAW_SDL_CACHE_FLAG_NONE = 0,
LV_DRAW_SDL_CACHE_FLAG_MANAGED = 1,
} lv_draw_sdl_cache_flag_t;
typedef struct {
lv_sdl_cache_key_magic_t magic;
lv_img_src_t type;
int32_t frame_id;
} lv_draw_sdl_cache_key_head_img_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sdl_texture_cache_init(lv_draw_sdl_ctx_t * ctx);
void lv_draw_sdl_texture_cache_deinit(lv_draw_sdl_ctx_t * ctx);
/**
* Find cached texture by key. The texture can be destroyed during usage.
*/
SDL_Texture * lv_draw_sdl_texture_cache_get(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, bool * found);
SDL_Texture * lv_draw_sdl_texture_cache_get_with_userdata(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
bool * found, void ** userdata);
/**
* @return Whether the texture has been put in the cache
*/
bool lv_draw_sdl_texture_cache_put(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length, SDL_Texture * texture);
/**
* @return Whether the texture has been put in the cache
*/
bool lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void * key, size_t key_length,
SDL_Texture * texture, void * userdata, void userdata_free(void *),
lv_draw_sdl_cache_flag_t flags);
lv_draw_sdl_cache_key_head_img_t * lv_draw_sdl_texture_img_key_create(const void * src, int32_t frame_id,
size_t * size);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_TEXTURE_CACHE_H*/

View File

@@ -0,0 +1,183 @@
/**
* @file lv_draw_sdl_utils.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl_utils.h"
#include "../lv_draw.h"
#include "../lv_draw_label.h"
#include "../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
extern const uint8_t _lv_bpp1_opa_table[2];
extern const uint8_t _lv_bpp2_opa_table[4];
extern const uint8_t _lv_bpp4_opa_table[16];
extern const uint8_t _lv_bpp8_opa_table[256];
static int utils_init_count = 0;
static SDL_Palette * lv_sdl_palette_grayscale8 = NULL;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_draw_sdl_utils_init()
{
utils_init_count++;
if(utils_init_count > 1) {
return;
}
lv_sdl_palette_grayscale8 = lv_sdl_alloc_palette_for_bpp(_lv_bpp8_opa_table, 8);
}
void _lv_draw_sdl_utils_deinit()
{
if(utils_init_count == 0) {
return;
}
utils_init_count--;
if(utils_init_count == 0) {
SDL_FreePalette(lv_sdl_palette_grayscale8);
lv_sdl_palette_grayscale8 = NULL;
}
}
void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out)
{
out->x = in->x1;
out->y = in->y1;
out->w = in->x2 - in->x1 + 1;
out->h = in->y2 - in->y1 + 1;
}
void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out)
{
#if LV_COLOR_DEPTH == 32
out->a = in->ch.alpha;
out->r = in->ch.red;
out->g = in->ch.green;
out->b = in->ch.blue;
#else
uint32_t color32 = lv_color_to32(*in);
lv_color32_t * color32_t = (lv_color32_t *) &color32;
out->a = color32_t->ch.alpha;
out->r = color32_t->ch.red;
out->g = color32_t->ch.green;
out->b = color32_t->ch.blue;
#endif
}
void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot)
{
if(zoom == LV_IMG_ZOOM_NONE) {
lv_area_to_sdl_rect(in, out);
return;
}
lv_area_t tmp;
_lv_img_buf_get_transformed_area(&tmp, lv_area_get_width(in), lv_area_get_height(in), 0, zoom, pivot);
lv_area_move(&tmp, in->x1, in->y1);
lv_area_to_sdl_rect(&tmp, out);
}
SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp)
{
SDL_assert(bpp >= 1 && bpp <= 8);
int color_cnt = 1 << bpp;
SDL_Palette * result = SDL_AllocPalette(color_cnt);
SDL_Color palette[256];
for(int i = 0; i < color_cnt; i++) {
palette[i].r = palette[i].g = palette[i].b = 0xFF;
palette[i].a = mapping ? mapping[i] : i;
}
SDL_SetPaletteColors(result, palette, 0, color_cnt);
return result;
}
SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride)
{
SDL_Surface * indexed = SDL_CreateRGBSurfaceFrom(opa, width, height, 8, stride, 0, 0, 0, 0);
SDL_SetSurfacePalette(indexed, lv_sdl_palette_grayscale8);
SDL_Surface * converted = SDL_ConvertSurfaceFormat(indexed, LV_DRAW_SDL_TEXTURE_FORMAT, 0);
SDL_FreeSurface(indexed);
return converted;
}
SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
lv_coord_t height, lv_coord_t stride)
{
SDL_Surface * indexed = lv_sdl_create_opa_surface(pixels, width, height, stride);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, indexed);
SDL_FreeSurface(indexed);
return texture;
}
void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp)
{
int src_len = width * height;
int cur = 0;
int curbit;
uint8_t opa_mask;
const uint8_t * opa_table;
switch(bpp) {
case 1:
opa_mask = 0x1;
opa_table = _lv_bpp1_opa_table;
break;
case 2:
opa_mask = 0x4;
opa_table = _lv_bpp2_opa_table;
break;
case 4:
opa_mask = 0xF;
opa_table = _lv_bpp4_opa_table;
break;
case 8:
opa_mask = 0xFF;
opa_table = _lv_bpp8_opa_table;
break;
default:
return;
}
/* Does this work well on big endian systems? */
while(cur < src_len) {
curbit = 8 - bpp;
uint8_t src_byte = src[cur * bpp / 8];
while(curbit >= 0 && cur < src_len) {
uint8_t src_bits = opa_mask & (src_byte >> curbit);
dest[(cur / width * stride) + (cur % width)] = opa_table[src_bits];
curbit -= bpp;
cur++;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_GPU_SDL*/

View File

@@ -0,0 +1,65 @@
/**
* @file lv_draw_sdl_utils.h
*
*/
#ifndef LV_DRAW_SDL_UTILS_H
#define LV_DRAW_SDL_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_GPU_SDL
#include "lv_draw_sdl.h"
#include "../../misc/lv_color.h"
#include "../../misc/lv_area.h"
#include LV_GPU_SDL_INCLUDE_PATH
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void _lv_draw_sdl_utils_init();
void _lv_draw_sdl_utils_deinit();
void lv_area_to_sdl_rect(const lv_area_t * in, SDL_Rect * out);
void lv_color_to_sdl_color(const lv_color_t * in, SDL_Color * out);
void lv_area_zoom_to_sdl_rect(const lv_area_t * in, SDL_Rect * out, uint16_t zoom, const lv_point_t * pivot);
SDL_Palette * lv_sdl_alloc_palette_for_bpp(const uint8_t * mapping, uint8_t bpp);
SDL_Surface * lv_sdl_create_opa_surface(lv_opa_t * opa, lv_coord_t width, lv_coord_t height, lv_coord_t stride);
SDL_Texture * lv_sdl_create_opa_texture(SDL_Renderer * renderer, lv_opa_t * pixels, lv_coord_t width,
lv_coord_t height, lv_coord_t stride);
void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height, int stride, uint8_t bpp);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_SDL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SDL_UTILS_H*/

View File

@@ -0,0 +1,6 @@
CSRCS += lv_gpu_stm32_dma2d.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d"

View File

@@ -0,0 +1,777 @@
/**
* @file lv_gpu_stm32_dma2d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gpu_stm32_dma2d.h"
#include "../../core/lv_refr.h"
#if LV_USE_GPU_STM32_DMA2D
/*********************
* DEFINES
*********************/
#if LV_COLOR_16_SWAP
// Note: DMA2D red/blue swap (RBS) works for all color modes
#define RBS_BIT 1U
#else
#define RBS_BIT 0U
#endif
#define CACHE_ROW_SIZE 32U // cache row size in Bytes
// For code/implementation discussion refer to https://github.com/lvgl/lvgl/issues/3714#issuecomment-1365187036
// astyle --options=lvgl/scripts/code-format.cfg --ignore-exclude-errors lvgl/src/draw/stm32_dma2d/*.c lvgl/src/draw/stm32_dma2d/*.h
#if LV_COLOR_DEPTH == 16
const dma2d_color_format_t LvglColorFormat = RGB565;
#elif LV_COLOR_DEPTH == 32
const dma2d_color_format_t LvglColorFormat = ARGB8888;
#else
#error "Cannot use DMA2D with LV_COLOR_DEPTH other than 16 or 32"
#endif
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
static void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * img_dsc,
const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t color_format);
static dma2d_color_format_t lv_color_format_to_dma2d_color_format(lv_img_cf_t color_format);
static lv_point_t lv_area_get_offset(const lv_area_t * area1, const lv_area_t * area2);
LV_STM32_DMA2D_STATIC void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx);
LV_STM32_DMA2D_STATIC lv_res_t lv_draw_stm32_dma2d_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * img_dsc,
const lv_area_t * src_area, const void * src);
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_fill(const lv_color_t * dst_buf, lv_coord_t dst_stride,
const lv_area_t * draw_area, lv_color_t color, lv_opa_t opa);
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_map(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, const void * src_buf, lv_coord_t src_stride, const lv_point_t * src_offset, lv_opa_t opa,
dma2d_color_format_t src_color_format, bool ignore_src_alpha);
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_paint(const lv_color_t * dst_buf, lv_coord_t dst_stride,
const lv_area_t * draw_area, const lv_opa_t * mask_buf, lv_coord_t mask_stride, const lv_point_t * mask_offset,
lv_color_t color, lv_opa_t opa);
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_copy_buffer(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, const lv_color_t * src_buf, lv_coord_t src_stride, const lv_point_t * src_offset);
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_await_dma_transfer_finish(lv_disp_drv_t * disp_drv);
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_start_dma_transfer(void);
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_invalidate_cache(uint32_t address, lv_coord_t offset,
lv_coord_t width, lv_coord_t height, uint8_t pixel_size);
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_clean_cache(uint32_t address, lv_coord_t offset, lv_coord_t width,
lv_coord_t height, uint8_t pixel_size);
LV_STM32_DMA2D_STATIC bool _lv_gpu_stm32_dwt_init(void);
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dwt_reset(void);
LV_STM32_DMA2D_STATIC uint32_t _lv_gpu_stm32_dwt_get_us(void);
static bool isDma2dInProgess = false; // indicates whether DMA2D transfer *initiated here* is in progress
/**
* Turn on the peripheral and set output color mode, this only needs to be done once
*/
void lv_draw_stm32_dma2d_init(void)
{
// Enable DMA2D clock
#if defined(STM32F4) || defined(STM32F7)
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN; // enable DMA2D
#elif defined(STM32H7)
RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
#else
# warning "LVGL can't enable the clock of DMA2D"
#endif
// Wait for hardware access to complete
__asm volatile("DSB\n");
// Delay after setting peripheral clock
volatile uint32_t temp = RCC->AHB1ENR;
LV_UNUSED(temp);
// AHB master timer configuration
DMA2D->AMTCR = 0; // AHB bus guaranteed dead time disabled
#if defined(LV_STM32_DMA2D_TEST)
_lv_gpu_stm32_dwt_init(); // init µs timer
#endif
}
void lv_draw_stm32_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
lv_draw_sw_init_ctx(drv, draw_ctx);
lv_draw_stm32_dma2d_ctx_t * dma2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
dma2d_draw_ctx->blend = lv_draw_stm32_dma2d_blend;
dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_stm32_dma2d_img_decoded;
//dma2d_draw_ctx->base_draw.draw_img = lv_draw_stm32_dma2d_img;
// Note: currently it does not make sense use lv_gpu_stm32_dma2d_wait_cb() since waiting starts right after the dma2d transfer
//dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_stm32_dma2d_wait_cb;
dma2d_draw_ctx->base_draw.buffer_copy = lv_draw_stm32_dma2d_buffer_copy;
}
void lv_draw_stm32_dma2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
LV_UNUSED(draw_ctx);
}
static void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
{
if(dsc->blend_mode != LV_BLEND_MODE_NORMAL) {
lv_draw_sw_blend_basic(draw_ctx, dsc);
return;
}
// Note: x1 must be zero. Otherwise, there is no way to correctly calculate dest_stride.
//LV_ASSERT_MSG(draw_ctx->buf_area->x1 == 0); // critical?
// Both draw buffer start address and buffer size *must* be 32-byte aligned since draw buffer cache is being invalidated.
//uint32_t drawBufferLength = lv_area_get_size(draw_ctx->buf_area) * sizeof(lv_color_t);
//LV_ASSERT_MSG(drawBufferLength % CACHE_ROW_SIZE == 0); // critical, but this is not the way to test it
//LV_ASSERT_MSG((uint32_t)draw_ctx->buf % CACHE_ROW_SIZE == 0, "draw_ctx.buf is not 32B aligned"); // critical?
if(dsc->src_buf) {
// For performance reasons, both source buffer start address and buffer size *should* be 32-byte aligned since source buffer cache is being cleaned.
//uint32_t srcBufferLength = lv_area_get_size(dsc->blend_area) * sizeof(lv_color_t);
//LV_ASSERT_MSG(srcBufferLength % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
//LV_ASSERT_MSG((uint32_t)dsc->src_buf % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
}
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, dsc->blend_area, draw_ctx->clip_area)) return;
// + draw_ctx->buf_area has the entire draw buffer location
// + draw_ctx->clip_area has the current draw buffer location
// + dsc->blend_area has the location of the area intended to be painted - image etc.
// + draw_area has the area actually being painted
// All coordinates are relative to the screen.
const lv_opa_t * mask = dsc->mask_buf;
if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return;
else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
if(mask != NULL) {
// For performance reasons, both mask buffer start address and buffer size *should* be 32-byte aligned since mask buffer cache is being cleaned.
//uint32_t srcBufferLength = lv_area_get_size(dsc->mask_area) * sizeof(lv_opa_t);
//LV_ASSERT_MSG(srcBufferLength % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
//LV_ASSERT_MSG((uint32_t)mask % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
lv_coord_t mask_stride = lv_area_get_width(dsc->mask_area);
lv_point_t mask_offset = lv_area_get_offset(dsc->mask_area, &draw_area); // mask offset in relation to draw_area
if(dsc->src_buf == NULL) { // 93.5%
lv_area_move(&draw_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
_lv_draw_stm32_dma2d_blend_paint(draw_ctx->buf, dest_stride, &draw_area, mask, mask_stride, &mask_offset, dsc->color,
dsc->opa);
}
else { // 0.2%
// note: (x)RGB dsc->src_buf does not carry alpha channel bytes,
// alpha channel bytes are carried in dsc->mask_buf
#if LV_COLOR_DEPTH == 32
lv_coord_t src_stride = lv_area_get_width(dsc->blend_area);
lv_point_t src_offset = lv_area_get_offset(dsc->blend_area, &draw_area); // source image offset in relation to draw_area
lv_coord_t draw_width = lv_area_get_width(&draw_area);
lv_coord_t draw_height = lv_area_get_height(&draw_area);
// merge mask alpha bytes with src RGB bytes
// TODO: optimize by reading 4 or 8 mask bytes at a time
mask += (mask_stride * mask_offset.y) + mask_offset.x;
lv_color_t * src_buf = (lv_color_t *)dsc->src_buf;
src_buf += (src_stride * src_offset.y) + src_offset.x;
uint16_t mask_buffer_offset = mask_stride - draw_width;
uint16_t src_buffer_offset = src_stride - draw_width;
while(draw_height > 0) {
draw_height--;
for(uint16_t x = 0; x < draw_width; x++) {
(*src_buf).ch.alpha = *mask;
src_buf++;
mask++;
}
mask += mask_buffer_offset;
src_buf += src_buffer_offset;
}
lv_area_move(&draw_area, -draw_ctx->buf_area->x1,
-draw_ctx->buf_area->y1); // translate the screen draw area to the origin of the buffer area
_lv_draw_stm32_dma2d_blend_map(draw_ctx->buf, dest_stride, &draw_area, dsc->src_buf, src_stride, &src_offset, dsc->opa,
ARGB8888, false);
#else
// Note: 16-bit bitmap hardware blending with mask and background is possible, but requires a temp 24 or 32-bit buffer to combine bitmap with mask first.
lv_draw_sw_blend_basic(draw_ctx, dsc); // (e.g. Shop Items)
// clean cache after software drawing - this does not help since this is not the only place where buffer is written without dma2d
// lv_coord_t draw_width = lv_area_get_width(&draw_area);
// lv_coord_t draw_height = lv_area_get_height(&draw_area);
// uint32_t dest_address = (uint32_t)(draw_ctx->buf + (dest_stride * draw_area.y1) + draw_area.x1);
// _lv_gpu_stm32_dma2d_clean_cache(dest_address, dest_stride - draw_width, draw_width, draw_height, sizeof(lv_color_t));
#endif
}
}
else {
if(dsc->src_buf == NULL) { // 6.1%
lv_area_move(&draw_area, -draw_ctx->buf_area->x1,
-draw_ctx->buf_area->y1); // translate the screen draw area to the origin of the buffer area
_lv_draw_stm32_dma2d_blend_fill(draw_ctx->buf, dest_stride, &draw_area, dsc->color, dsc->opa);
}
else { // 0.2%
lv_coord_t src_stride = lv_area_get_width(dsc->blend_area);
lv_point_t src_offset = lv_area_get_offset(dsc->blend_area, &draw_area); // source image offset in relation to draw_area
lv_area_move(&draw_area, -draw_ctx->buf_area->x1,
-draw_ctx->buf_area->y1); // translate the screen draw area to the origin of the buffer area
_lv_draw_stm32_dma2d_blend_map(draw_ctx->buf, dest_stride, &draw_area, dsc->src_buf, src_stride, &src_offset, dsc->opa,
LvglColorFormat, true);
}
}
}
// Does dest_area = intersect(draw_ctx->clip_area, src_area) ?
// See: https://github.com/lvgl/lvgl/issues/3714#issuecomment-1331710788
static void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride,
const lv_area_t * dest_area, void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
// Both draw buffer start address and buffer size *must* be 32-byte aligned since draw buffer cache is being invalidated.
//uint32_t drawBufferLength = lv_area_get_size(draw_ctx->buf_area) * sizeof(lv_color_t);
//LV_ASSERT_MSG(drawBufferLength % CACHE_ROW_SIZE == 0); // critical, but this is not the way to test it
//LV_ASSERT_MSG((uint32_t)draw_ctx->buf % CACHE_ROW_SIZE == 0, "draw_ctx.buf is not 32B aligned"); // critical?
// FIXME:
// 1. Both src_buf and dest_buf pixel size *must* be known to use DMA2D.
// 2. Verify both buffers start addresses and lengths are 32-byte (cache row size) aligned.
LV_UNUSED(draw_ctx);
lv_point_t src_offset = lv_area_get_offset(src_area, dest_area);
// FIXME: use lv_area_move(dest_area, -dest_area->x1, -dest_area->y1) here ?
// TODO: It is assumed that dest_buf and src_buf buffers are of lv_color_t type. Verify it, this assumption may be incorrect.
_lv_draw_stm32_dma2d_blend_map((const lv_color_t *)dest_buf, dest_stride, dest_area, (const lv_color_t *)src_buf,
src_stride, &src_offset, 0xff, LvglColorFormat, true);
// TODO: Investigate if output buffer cache needs to be invalidated. It depends on what the destination buffer is and how it is used next - by dma2d or not.
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL); // TODO: is this line needed here?
}
static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * img_dsc,
const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t color_format)
{
if(draw_ctx->draw_img_decoded == NULL) return;
lv_area_t draw_area;
lv_area_copy(&draw_area, draw_ctx->clip_area);
bool mask_any = lv_draw_mask_is_any(&draw_area);
bool transform = img_dsc->angle != 0 || img_dsc->zoom != LV_IMG_ZOOM_NONE;
const dma2d_color_format_t bitmapColorFormat = lv_color_format_to_dma2d_color_format(color_format);
const bool ignoreBitmapAlpha = (color_format == LV_IMG_CF_RGBX8888);
if(!mask_any && !transform && bitmapColorFormat != UNSUPPORTED && img_dsc->recolor_opa == LV_OPA_TRANSP) {
// simple bitmap blending, optionally with supported color format conversion - handle directly by dma2d
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_coord_t src_stride = lv_area_get_width(coords);
lv_point_t src_offset = lv_area_get_offset(coords, &draw_area); // source image offset in relation to draw_area
lv_area_move(&draw_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
_lv_draw_stm32_dma2d_blend_map(draw_ctx->buf, dest_stride, &draw_area, src_buf, src_stride, &src_offset,
img_dsc->opa, bitmapColorFormat, ignoreBitmapAlpha);
}
else {
// all more complex cases which require additional image transformations
lv_draw_sw_img_decoded(draw_ctx, img_dsc, coords, src_buf, color_format);
}
}
static lv_point_t lv_area_get_offset(const lv_area_t * area1, const lv_area_t * area2)
{
lv_point_t offset = {x: area2->x1 - area1->x1, y: area2->y1 - area1->y1};
return offset;
}
static dma2d_color_format_t lv_color_format_to_dma2d_color_format(lv_img_cf_t color_format)
{
switch(color_format) {
case LV_IMG_CF_RGBA8888:
// note: LV_IMG_CF_RGBA8888 is actually ARGB8888
return ARGB8888;
case LV_IMG_CF_RGBX8888:
// note: LV_IMG_CF_RGBX8888 is actually XRGB8888
return ARGB8888;
case LV_IMG_CF_RGB565:
return RGB565;
case LV_IMG_CF_TRUE_COLOR:
return LvglColorFormat;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
#if LV_COLOR_DEPTH == 16
// bitmap color format is 24b ARGB8565 - dma2d unsupported
return UNSUPPORTED;
#elif LV_COLOR_DEPTH == 32
return ARGB8888;
#else
// unknown bitmap color format
return UNSUPPORTED;
#endif
default:
return UNSUPPORTED;
}
}
LV_STM32_DMA2D_STATIC lv_res_t lv_draw_stm32_dma2d_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * img_dsc,
const lv_area_t * src_area, const void * src)
{
//if(lv_img_src_get_type(src) != LV_IMG_SRC_VARIABLE) return LV_RES_INV;
return LV_RES_INV;
if(img_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
const lv_img_dsc_t * img = src;
const dma2d_color_format_t bitmapColorFormat = lv_color_format_to_dma2d_color_format(img->header.cf);
const bool ignoreBitmapAlpha = (img->header.cf == LV_IMG_CF_RGBX8888);
if(bitmapColorFormat == UNSUPPORTED || img_dsc->angle != 0 || img_dsc->zoom != LV_IMG_ZOOM_NONE) {
return LV_RES_INV; // sorry, dma2d can handle this
}
// FIXME: handle dsc.pivot, dsc.recolor, dsc.blend_mode
// FIXME: src pixel size *must* be known to use DMA2D
// FIXME: If image is drawn by SW, then output cache needs to be cleaned next. Currently it is not possible.
// Both draw buffer start address and buffer size *must* be 32-byte aligned since draw buffer cache is being invalidated.
//uint32_t drawBufferLength = lv_area_get_size(draw_ctx->buf_area) * sizeof(lv_color_t);
//LV_ASSERT_MSG(drawBufferLength % CACHE_ROW_SIZE == 0); // critical, but this is not the way to test it
//LV_ASSERT_MSG((uint32_t)draw_ctx->buf % CACHE_ROW_SIZE == 0, "draw_ctx.buf is not 32B aligned"); // critical?
// For performance reasons, both source buffer start address and buffer size *should* be 32-byte aligned since source buffer cache is being cleaned.
//uint32_t srcBufferLength = lv_area_get_size(src_area) * sizeof(lv_color_t); // TODO: verify src pixel size = sizeof(lv_color_t)
//LV_ASSERT_MSG(srcBufferLength % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
//LV_ASSERT_MSG((uint32_t)src % CACHE_ROW_SIZE == 0); // FIXME: assert fails (performance, non-critical)
lv_area_t draw_area;
if(!_lv_area_intersect(&draw_area, src_area, draw_ctx->clip_area)) return LV_RES_OK;
lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
lv_point_t src_offset = lv_area_get_offset(src_area, &draw_area); // source image offset in relation to draw_area
lv_area_move(&draw_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
_lv_draw_stm32_dma2d_blend_map(draw_ctx->buf, dest_stride, &draw_area, img->data, img->header.w,
&src_offset, img_dsc->opa, bitmapColorFormat, ignoreBitmapAlpha);
return LV_RES_OK;
}
LV_STM32_DMA2D_STATIC void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx)
{
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(disp->driver);
lv_draw_sw_wait_for_finish(draw_ctx);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* @brief Fills draw_area with specified color.
* @param color color to be painted, note: alpha is ignored
*/
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_fill(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, lv_color_t color, lv_opa_t opa)
{
LV_ASSERT_MSG(!isDma2dInProgess, "dma2d transfer has not finished"); // critical
lv_coord_t draw_width = lv_area_get_width(draw_area);
lv_coord_t draw_height = lv_area_get_height(draw_area);
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL);
if(opa >= LV_OPA_MAX) {
DMA2D->CR = 0x3UL << DMA2D_CR_MODE_Pos; // Register-to-memory (no FG nor BG, only output stage active)
DMA2D->OPFCCR = LvglColorFormat;
#if defined(DMA2D_OPFCCR_RBS_Pos)
DMA2D->OPFCCR |= (RBS_BIT << DMA2D_OPFCCR_RBS_Pos);
#endif
DMA2D->OMAR = (uint32_t)(dest_buf + (dest_stride * draw_area->y1) + draw_area->x1);
DMA2D->OOR = dest_stride - draw_width; // out buffer offset
// Note: unlike FGCOLR and BGCOLR, OCOLR bits must match DMA2D_OUTPUT_COLOR, alpha can be specified
#if RBS_BIT
// swap red/blue bits
DMA2D->OCOLR = (color.ch.blue << 11) | (color.ch.green_l << 5 | color.ch.green_h << 8) | (color.ch.red);
#else
DMA2D->OCOLR = color.full;
#endif
}
else {
DMA2D->CR = 0x2UL << DMA2D_CR_MODE_Pos; // Memory-to-memory with blending (FG and BG fetch with PFC and blending)
DMA2D->FGPFCCR = A8;
DMA2D->FGPFCCR |= (opa << DMA2D_FGPFCCR_ALPHA_Pos);
// Alpha Mode 1: Replace original foreground image alpha channel value by FGPFCCR.ALPHA
DMA2D->FGPFCCR |= (0x1UL << DMA2D_FGPFCCR_AM_Pos);
//DMA2D->FGPFCCR |= (RBS_BIT << DMA2D_FGPFCCR_RBS_Pos);
// Note: in Alpha Mode 1 FGMAR and FGOR are not used to supply foreground A8 bytes,
// those bytes are replaced by constant ALPHA defined in FGPFCCR
DMA2D->FGMAR = (uint32_t)dest_buf;
DMA2D->FGOR = dest_stride;
DMA2D->FGCOLR = lv_color_to32(color) & 0x00ffffff; // swap FGCOLR R/B bits if FGPFCCR.RBS (RBS_BIT) bit is set
DMA2D->BGPFCCR = LvglColorFormat;
#if defined(DMA2D_BGPFCCR_RBS_Pos)
DMA2D->BGPFCCR |= (RBS_BIT << DMA2D_BGPFCCR_RBS_Pos);
#endif
DMA2D->BGMAR = (uint32_t)(dest_buf + (dest_stride * draw_area->y1) + draw_area->x1);
DMA2D->BGOR = dest_stride - draw_width;
DMA2D->BGCOLR = 0; // used in A4 and A8 modes only
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->BGMAR, DMA2D->BGOR, draw_width, draw_height, sizeof(lv_color_t));
DMA2D->OPFCCR = LvglColorFormat;
#if defined(DMA2D_OPFCCR_RBS_Pos)
DMA2D->OPFCCR |= (RBS_BIT << DMA2D_OPFCCR_RBS_Pos);
#endif
DMA2D->OMAR = DMA2D->BGMAR;
DMA2D->OOR = DMA2D->BGOR;
DMA2D->OCOLR = 0;
}
// PL - pixel per lines (14 bit), NL - number of lines (16 bit)
DMA2D->NLR = (draw_width << DMA2D_NLR_PL_Pos) | (draw_height << DMA2D_NLR_NL_Pos);
_lv_gpu_stm32_dma2d_start_dma_transfer();
}
/**
* @brief Draws src (foreground) map on dst (background) map.
* @param src_offset src offset in relation to dst, useful when src is larger than draw_area
* @param opa constant opacity to be applied
* @param bitmapColorCode bitmap color type
* @param ignoreAlpha if TRUE, bitmap src alpha channel is ignored
*/
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_map(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, const void * src_buf, lv_coord_t src_stride, const lv_point_t * src_offset, lv_opa_t opa,
dma2d_color_format_t src_color_format, bool ignore_src_alpha)
{
LV_ASSERT_MSG(!isDma2dInProgess, "dma2d transfer has not finished"); // critical
if(opa <= LV_OPA_MIN || src_color_format == UNSUPPORTED) return;
lv_coord_t draw_width = lv_area_get_width(draw_area);
lv_coord_t draw_height = lv_area_get_height(draw_area);
bool bitmapHasOpacity = !ignore_src_alpha && (src_color_format == ARGB8888 || src_color_format == ARGB1555 ||
src_color_format == ARGB4444);
if(opa >= LV_OPA_MAX) opa = 0xff;
uint8_t srcBpp; // source bytes per pixel
switch(src_color_format) {
case ARGB8888:
srcBpp = 4;
break;
case RGB888:
srcBpp = 3;
break;
case RGB565:
case ARGB1555:
case ARGB4444:
srcBpp = 2;
break;
default:
LV_LOG_ERROR("unsupported color format");
return;
}
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL);
DMA2D->FGPFCCR = src_color_format;
if(opa == 0xff && !bitmapHasOpacity) {
// no need to blend
if(src_color_format == LvglColorFormat) {
// no need to convert pixel format (PFC) either
DMA2D->CR = 0x0UL;
}
else {
DMA2D->CR = 0x1UL << DMA2D_CR_MODE_Pos; // Memory-to-memory with PFC (FG fetch only with FG PFC active)
}
// Alpha Mode 0: No modification of the foreground image alpha channel value
}
else {
// blend
DMA2D->CR = 0x2UL << DMA2D_CR_MODE_Pos; // Memory-to-memory with blending (FG and BG fetch with PFC and blending)
DMA2D->FGPFCCR |= (opa << DMA2D_FGPFCCR_ALPHA_Pos);
if(bitmapHasOpacity) {
// Alpha Mode 2: Replace original foreground image alpha channel value by FGPFCCR.ALPHA multiplied with original alpha channel value
DMA2D->FGPFCCR |= (0x2UL << DMA2D_FGPFCCR_AM_Pos);
}
else {
// Alpha Mode 1: Replace original foreground image alpha channel value by FGPFCCR.ALPHA
DMA2D->FGPFCCR |= (0x1UL << DMA2D_FGPFCCR_AM_Pos);
}
}
#if defined(DMA2D_FGPFCCR_RBS_Pos)
DMA2D->FGPFCCR |= (RBS_BIT << DMA2D_FGPFCCR_RBS_Pos);
#endif
DMA2D->FGMAR = ((uint32_t)src_buf) + srcBpp * ((src_stride * src_offset->y) + src_offset->x);
DMA2D->FGOR = src_stride - draw_width;
DMA2D->FGCOLR = 0; // used in A4 and A8 modes only
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->FGMAR, DMA2D->FGOR, draw_width, draw_height, srcBpp);
DMA2D->OPFCCR = LvglColorFormat;
#if defined(DMA2D_OPFCCR_RBS_Pos)
DMA2D->OPFCCR |= (RBS_BIT << DMA2D_OPFCCR_RBS_Pos);
#endif
DMA2D->OMAR = (uint32_t)(dest_buf + (dest_stride * draw_area->y1) + draw_area->x1);
DMA2D->OOR = dest_stride - draw_width;
DMA2D->OCOLR = 0;
if(opa != 0xff || bitmapHasOpacity) {
// use background (BG*) registers
DMA2D->BGPFCCR = LvglColorFormat;
#if defined(DMA2D_BGPFCCR_RBS_Pos)
DMA2D->BGPFCCR |= (RBS_BIT << DMA2D_BGPFCCR_RBS_Pos);
#endif
DMA2D->BGMAR = DMA2D->OMAR;
DMA2D->BGOR = DMA2D->OOR;
DMA2D->BGCOLR = 0; // used in A4 and A8 modes only
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->BGMAR, DMA2D->BGOR, draw_width, draw_height, sizeof(lv_color_t));
}
// PL - pixel per lines (14 bit), NL - number of lines (16 bit)
DMA2D->NLR = (draw_width << DMA2D_NLR_PL_Pos) | (draw_height << DMA2D_NLR_NL_Pos);
_lv_gpu_stm32_dma2d_start_dma_transfer();
}
/**
* @brief Paints solid color with alpha mask with additional constant opacity. Useful e.g. for painting anti-aliased fonts.
* @param src_offset src offset in relation to dst, useful when src (alpha mask) is larger than draw_area
* @param color color to paint, note: alpha is ignored
* @param opa constant opacity to be applied
*/
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_blend_paint(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, const lv_opa_t * mask_buf, lv_coord_t mask_stride, const lv_point_t * mask_offset,
lv_color_t color, lv_opa_t opa)
{
LV_ASSERT_MSG(!isDma2dInProgess, "dma2d transfer has not finished"); // critical
lv_coord_t draw_width = lv_area_get_width(draw_area);
lv_coord_t draw_height = lv_area_get_height(draw_area);
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL);
DMA2D->CR = 0x2UL << DMA2D_CR_MODE_Pos; // Memory-to-memory with blending (FG and BG fetch with PFC and blending)
DMA2D->FGPFCCR = A8;
if(opa < LV_OPA_MAX) {
DMA2D->FGPFCCR |= (opa << DMA2D_FGPFCCR_ALPHA_Pos);
DMA2D->FGPFCCR |= (0x2UL <<
DMA2D_FGPFCCR_AM_Pos); // Alpha Mode: Replace original foreground image alpha channel value by FGPFCCR.ALPHA multiplied with original alpha channel value
}
//DMA2D->FGPFCCR |= (RBS_BIT << DMA2D_FGPFCCR_RBS_Pos);
DMA2D->FGMAR = (uint32_t)(mask_buf + (mask_stride * mask_offset->y) + mask_offset->x);
DMA2D->FGOR = mask_stride - draw_width;
DMA2D->FGCOLR = lv_color_to32(color) & 0x00ffffff; // swap FGCOLR R/B bits if FGPFCCR.RBS (RBS_BIT) bit is set
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->FGMAR, DMA2D->FGOR, draw_width, draw_height, sizeof(lv_opa_t));
DMA2D->BGPFCCR = LvglColorFormat;
#if defined(DMA2D_BGPFCCR_RBS_Pos)
DMA2D->BGPFCCR |= (RBS_BIT << DMA2D_BGPFCCR_RBS_Pos);
#endif
DMA2D->BGMAR = (uint32_t)(dest_buf + (dest_stride * draw_area->y1) + draw_area->x1);
DMA2D->BGOR = dest_stride - draw_width;
DMA2D->BGCOLR = 0; // used in A4 and A8 modes only
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->BGMAR, DMA2D->BGOR, draw_width, draw_height, sizeof(lv_color_t));
DMA2D->OPFCCR = LvglColorFormat;
#if defined(DMA2D_OPFCCR_RBS_Pos)
DMA2D->OPFCCR |= (RBS_BIT << DMA2D_OPFCCR_RBS_Pos);
#endif
DMA2D->OMAR = DMA2D->BGMAR;
DMA2D->OOR = DMA2D->BGOR;
DMA2D->OCOLR = 0;
// PL - pixel per lines (14 bit), NL - number of lines (16 bit)
DMA2D->NLR = (draw_width << DMA2D_NLR_PL_Pos) | (draw_height << DMA2D_NLR_NL_Pos);
_lv_gpu_stm32_dma2d_start_dma_transfer();
}
/**
* @brief Copies src (foreground) map to the dst (background) map.
* @param src_offset src offset in relation to dst, useful when src is larger than draw_area
*/
LV_STM32_DMA2D_STATIC void _lv_draw_stm32_dma2d_copy_buffer(const lv_color_t * dest_buf, lv_coord_t dest_stride,
const lv_area_t * draw_area, const lv_color_t * src_buf, lv_coord_t src_stride, const lv_point_t * src_offset)
{
LV_ASSERT_MSG(!isDma2dInProgess, "dma2d transfer has not finished"); // critical
lv_coord_t draw_width = lv_area_get_width(draw_area);
lv_coord_t draw_height = lv_area_get_height(draw_area);
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL);
DMA2D->CR = 0x0UL; // Memory-to-memory (FG fetch only)
DMA2D->FGPFCCR = LvglColorFormat;
#if defined(DMA2D_FGPFCCR_RBS_Pos)
DMA2D->FGPFCCR |= (RBS_BIT << DMA2D_FGPFCCR_RBS_Pos);
#endif
DMA2D->FGMAR = (uint32_t)(src_buf + (src_stride * src_offset->y) + src_offset->x);
DMA2D->FGOR = src_stride - draw_width;
DMA2D->FGCOLR = 0; // used in A4 and A8 modes only
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->FGMAR, DMA2D->FGOR, draw_width, draw_height, sizeof(lv_color_t));
// Note BG* registers do not need to be set up since BG is not used
DMA2D->OPFCCR = LvglColorFormat;
#if defined(DMA2D_OPFCCR_RBS_Pos)
DMA2D->OPFCCR |= (RBS_BIT << DMA2D_OPFCCR_RBS_Pos);
#endif
DMA2D->OMAR = (uint32_t)(dest_buf + (dest_stride * draw_area->y1) + draw_area->x1);
DMA2D->OOR = dest_stride - draw_width;
DMA2D->OCOLR = 0;
// PL - pixel per lines (14 bit), NL - number of lines (16 bit)
DMA2D->NLR = (draw_width << DMA2D_NLR_PL_Pos) | (draw_height << DMA2D_NLR_NL_Pos);
_lv_gpu_stm32_dma2d_start_dma_transfer();
}
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_start_dma_transfer(void)
{
LV_ASSERT_MSG(!isDma2dInProgess, "dma2d transfer has not finished");
isDma2dInProgess = true;
DMA2D->IFCR = 0x3FU; // trigger ISR flags reset
// Note: cleaning output buffer cache is needed only when buffer may be misaligned or adjacent area may have been drawn in sw-fashion, e.g. using lv_draw_sw_blend_basic()
#if LV_COLOR_DEPTH == 16
_lv_gpu_stm32_dma2d_clean_cache(DMA2D->OMAR, DMA2D->OOR, (DMA2D->NLR & DMA2D_NLR_PL_Msk) >> DMA2D_NLR_PL_Pos,
(DMA2D->NLR & DMA2D_NLR_NL_Msk) >> DMA2D_NLR_NL_Pos, sizeof(lv_color_t));
#endif
DMA2D->CR |= DMA2D_CR_START;
// Note: for some reason mask buffer gets damaged during transfer if waiting is postponed
_lv_gpu_stm32_dma2d_await_dma_transfer_finish(NULL); // FIXME: this line should not be needed here, but it is
}
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_await_dma_transfer_finish(lv_disp_drv_t * disp_drv)
{
if(disp_drv && disp_drv->wait_cb) {
while((DMA2D->CR & DMA2D_CR_START) != 0U) {
disp_drv->wait_cb(disp_drv);
}
}
else {
while((DMA2D->CR & DMA2D_CR_START) != 0U);
}
__IO uint32_t isrFlags = DMA2D->ISR;
if(isrFlags & DMA2D_ISR_CEIF) {
LV_LOG_ERROR("DMA2D config error");
}
if(isrFlags & DMA2D_ISR_TEIF) {
LV_LOG_ERROR("DMA2D transfer error");
}
DMA2D->IFCR = 0x3FU; // trigger ISR flags reset
if(isDma2dInProgess) {
// invalidate output buffer cached memory ONLY after DMA2D transfer
//_lv_gpu_stm32_dma2d_invalidate_cache(DMA2D->OMAR, DMA2D->OOR, (DMA2D->NLR & DMA2D_NLR_PL_Msk) >> DMA2D_NLR_PL_Pos, (DMA2D->NLR & DMA2D_NLR_NL_Msk) >> DMA2D_NLR_NL_Pos, sizeof(lv_color_t));
isDma2dInProgess = false;
}
}
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_invalidate_cache(uint32_t address, lv_coord_t offset, lv_coord_t width,
lv_coord_t height, uint8_t pixel_size)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if(((SCB->CCR) & SCB_CCR_DC_Msk) == 0) return; // L1 data cache is disabled
uint16_t stride = pixel_size * (width + offset); // in bytes
uint16_t ll = pixel_size * width; // line length in bytes
uint32_t n = 0; // address of the next cache row after the last invalidated row
lv_coord_t h = 0;
__DSB();
while(h < height) {
uint32_t a = address + (h * stride);
uint32_t e = a + ll; // end address, address of the first byte after the current line
a &= ~(CACHE_ROW_SIZE - 1U);
if(a < n) a = n; // prevent the previous last cache row from being invalidated again
while(a < e) {
SCB->DCIMVAC = a;
a += CACHE_ROW_SIZE;
}
n = a;
h++;
};
__DSB();
__ISB();
#endif
}
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dma2d_clean_cache(uint32_t address, lv_coord_t offset, lv_coord_t width,
lv_coord_t height, uint8_t pixel_size)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if(((SCB->CCR) & SCB_CCR_DC_Msk) == 0) return; // L1 data cache is disabled
uint16_t stride = pixel_size * (width + offset); // in bytes
uint16_t ll = pixel_size * width; // line length in bytes
uint32_t n = 0; // address of the next cache row after the last cleaned row
lv_coord_t h = 0;
__DSB();
while(h < height) {
uint32_t a = address + (h * stride);
uint32_t e = a + ll; // end address, address of the first byte after the current line
a &= ~(CACHE_ROW_SIZE - 1U);
if(a < n) a = n; // prevent the previous last cache row from being cleaned again
while(a < e) {
SCB->DCCMVAC = a;
a += CACHE_ROW_SIZE;
}
n = a;
h++;
};
__DSB();
__ISB();
#endif
}
// initialize µs timer
LV_STM32_DMA2D_STATIC bool _lv_gpu_stm32_dwt_init(void)
{
// disable TRC
CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
// enable TRC
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
#if defined(__CORTEX_M) && (__CORTEX_M == 7U)
DWT->LAR = 0xC5ACCE55;
#endif
// disable clock cycle counter
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
// enable clock cycle counter
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
// reset the clock cycle counter value
DWT->CYCCNT = 0;
// 3 NO OPERATION instructions
__ASM volatile("NOP");
__ASM volatile("NOP");
__ASM volatile("NOP");
// check if clock cycle counter has started
if(DWT->CYCCNT) {
return true; // clock cycle counter started
}
else {
return false; // clock cycle counter not started
}
}
// get elapsed µs since reset
LV_STM32_DMA2D_STATIC uint32_t _lv_gpu_stm32_dwt_get_us(void)
{
uint32_t us = (DWT->CYCCNT * 1000000) / HAL_RCC_GetHCLKFreq();
return us;
}
// reset µs timer
LV_STM32_DMA2D_STATIC void _lv_gpu_stm32_dwt_reset(void)
{
DWT->CYCCNT = 0;
}
#endif

View File

@@ -0,0 +1,67 @@
/**
* @file lv_gpu_stm32_dma2d.h
*
*/
#ifndef LV_GPU_STM32_DMA2D_H
#define LV_GPU_STM32_DMA2D_H
#ifdef __cplusplus
extern "C" {
#endif
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
#include "../sw/lv_draw_sw.h"
#if LV_USE_GPU_STM32_DMA2D
/*********************
* INCLUDES
*********************/
#include LV_GPU_DMA2D_CMSIS_INCLUDE
/*********************
* DEFINES
*********************/
#if defined(LV_STM32_DMA2D_TEST)
// removes "static" modifier for some internal methods in order to test them
#define LV_STM32_DMA2D_STATIC
#else
#define LV_STM32_DMA2D_STATIC static
#endif
/**********************
* TYPEDEFS
**********************/
enum dma2d_color_format {
ARGB8888 = 0x0,
RGB888 = 0x01,
RGB565 = 0x02,
ARGB1555 = 0x03,
ARGB4444 = 0x04,
A8 = 0x09,
UNSUPPORTED = 0xff,
};
typedef enum dma2d_color_format dma2d_color_format_t;
typedef lv_draw_sw_ctx_t lv_draw_stm32_dma2d_ctx_t;
struct _lv_disp_drv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_stm32_dma2d_init(void);
void lv_draw_stm32_dma2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_stm32_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GPU_STM32_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GPU_STM32_DMA2D_H*/

View File

@@ -0,0 +1,108 @@
/**
* @file lv_draw_sw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_draw.h"
#include "lv_draw_sw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_init_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx;
lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t));
draw_sw_ctx->base_draw.draw_arc = lv_draw_sw_arc;
draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect;
draw_sw_ctx->base_draw.draw_bg = lv_draw_sw_bg;
draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter;
draw_sw_ctx->base_draw.draw_img_decoded = lv_draw_sw_img_decoded;
draw_sw_ctx->base_draw.draw_line = lv_draw_sw_line;
draw_sw_ctx->base_draw.draw_polygon = lv_draw_sw_polygon;
#if LV_DRAW_COMPLEX
draw_sw_ctx->base_draw.draw_transform = lv_draw_sw_transform;
#endif
draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish;
draw_sw_ctx->base_draw.buffer_copy = lv_draw_sw_buffer_copy;
draw_sw_ctx->base_draw.layer_init = lv_draw_sw_layer_create;
draw_sw_ctx->base_draw.layer_adjust = lv_draw_sw_layer_adjust;
draw_sw_ctx->base_draw.layer_blend = lv_draw_sw_layer_blend;
draw_sw_ctx->base_draw.layer_destroy = lv_draw_sw_layer_destroy;
draw_sw_ctx->blend = lv_draw_sw_blend_basic;
draw_ctx->layer_instance_size = sizeof(lv_draw_sw_layer_ctx_t);
}
void lv_draw_sw_deinit_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(drv);
lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx;
lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t));
}
void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx)
{
LV_UNUSED(draw_ctx);
/*Nothing to wait for*/
}
void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area)
{
LV_UNUSED(draw_ctx);
lv_color_t * dest_bufc = dest_buf;
lv_color_t * src_bufc = src_buf;
/*Got the first pixel of each buffer*/
dest_bufc += dest_stride * dest_area->y1;
dest_bufc += dest_area->x1;
src_bufc += src_stride * src_area->y1;
src_bufc += src_area->x1;
uint32_t line_length = lv_area_get_width(dest_area) * sizeof(lv_color_t);
lv_coord_t y;
for(y = dest_area->y1; y <= dest_area->y2; y++) {
lv_memcpy(dest_bufc, src_bufc, line_length);
dest_bufc += dest_stride;
src_bufc += src_stride;
}
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,106 @@
/**
* @file lv_draw_sw.h
*
*/
#ifndef LV_DRAW_SW_H
#define LV_DRAW_SW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw_blend.h"
#include "../lv_draw.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
#include "../../hal/lv_hal_disp.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_disp_drv_t;
typedef struct {
lv_draw_ctx_t base_draw;
/** Fill an area of the destination buffer with a color*/
void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
} lv_draw_sw_ctx_t;
typedef struct {
lv_draw_layer_ctx_t base_draw;
uint32_t buf_size_bytes: 31;
uint32_t has_alpha : 1;
} lv_draw_sw_layer_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_sw_init_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_deinit_ctx(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx);
void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle);
void lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx,
const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const uint8_t * src_buf,
lv_img_cf_t cf);
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc,
const lv_point_t * point1, const lv_point_t * point2);
void lv_draw_sw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc,
const lv_point_t * points, uint16_t point_cnt);
void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx,
void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area,
void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area);
void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf,
lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride,
const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf);
struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags);
void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
const lv_draw_img_dsc_t * draw_dsc);
void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx);
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SW_H*/

View File

@@ -0,0 +1,17 @@
CSRCS += lv_draw_sw.c
CSRCS += lv_draw_sw_arc.c
CSRCS += lv_draw_sw_blend.c
CSRCS += lv_draw_sw_dither.c
CSRCS += lv_draw_sw_gradient.c
CSRCS += lv_draw_sw_img.c
CSRCS += lv_draw_sw_letter.c
CSRCS += lv_draw_sw_line.c
CSRCS += lv_draw_sw_polygon.c
CSRCS += lv_draw_sw_rect.c
CSRCS += lv_draw_sw_transform.c
CSRCS += lv_draw_sw_layer.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw"

View File

@@ -0,0 +1,537 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#include "../../misc/lv_math.h"
#include "../../misc/lv_log.h"
#include "../../misc/lv_mem.h"
#include "../lv_draw.h"
/*********************
* DEFINES
*********************/
#define SPLIT_RADIUS_LIMIT 10 /*With radius greater than this the arc will drawn in quarters. A quarter is drawn only if there is arc in it*/
#define SPLIT_ANGLE_GAP_LIMIT 60 /*With small gaps in the arc don't bother with splitting because there is nothing to skip.*/
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_point_t * center;
lv_coord_t radius;
uint16_t start_angle;
uint16_t end_angle;
uint16_t start_quarter;
uint16_t end_quarter;
lv_coord_t width;
lv_draw_rect_dsc_t * draw_dsc;
const lv_area_t * draw_area;
lv_draw_ctx_t * draw_ctx;
} quarter_draw_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q);
static void draw_quarter_1(quarter_draw_dsc_t * q);
static void draw_quarter_2(quarter_draw_dsc_t * q);
static void draw_quarter_3(quarter_draw_dsc_t * q);
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area);
#endif /*LV_DRAW_COMPLEX*/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, uint16_t radius,
uint16_t start_angle, uint16_t end_angle)
{
#if LV_DRAW_COMPLEX
if(dsc->opa <= LV_OPA_MIN) return;
if(dsc->width == 0) return;
if(start_angle == end_angle) return;
lv_coord_t width = dsc->width;
if(width > radius) width = radius;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
cir_dsc.blend_mode = dsc->blend_mode;
if(dsc->img_src) {
cir_dsc.bg_opa = LV_OPA_TRANSP;
cir_dsc.bg_img_src = dsc->img_src;
cir_dsc.bg_img_opa = dsc->opa;
}
else {
cir_dsc.bg_opa = dsc->opa;
cir_dsc.bg_color = dsc->color;
}
lv_area_t area_out;
area_out.x1 = center->x - radius;
area_out.y1 = center->y - radius;
area_out.x2 = center->x + radius - 1; /*-1 because the center already belongs to the left/bottom part*/
area_out.y2 = center->y + radius - 1;
lv_area_t area_in;
lv_area_copy(&area_in, &area_out);
area_in.x1 += dsc->width;
area_in.y1 += dsc->width;
area_in.x2 -= dsc->width;
area_in.y2 -= dsc->width;
/*Create inner the mask*/
int16_t mask_in_id = LV_MASK_ID_INV;
lv_draw_mask_radius_param_t mask_in_param;
bool mask_in_param_valid = false;
if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
mask_in_param_valid = true;
mask_in_id = lv_draw_mask_add(&mask_in_param, NULL);
}
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, NULL);
/*Draw a full ring*/
if(start_angle + 360 == end_angle || start_angle == end_angle + 360) {
cir_dsc.radius = LV_RADIUS_CIRCLE;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
lv_draw_mask_free_param(&mask_out_param);
if(mask_in_param_valid) {
lv_draw_mask_free_param(&mask_in_param);
}
return;
}
while(start_angle >= 360) start_angle -= 360;
while(end_angle >= 360) end_angle -= 360;
lv_draw_mask_angle_param_t mask_angle_param;
lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
int16_t mask_angle_id = lv_draw_mask_add(&mask_angle_param, NULL);
int32_t angle_gap;
if(end_angle > start_angle) {
angle_gap = 360 - (end_angle - start_angle);
}
else {
angle_gap = start_angle - end_angle;
}
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
if(angle_gap > SPLIT_ANGLE_GAP_LIMIT && radius > SPLIT_RADIUS_LIMIT) {
/*Handle each quarter individually and skip which is empty*/
quarter_draw_dsc_t q_dsc;
q_dsc.center = center;
q_dsc.radius = radius;
q_dsc.start_angle = start_angle;
q_dsc.end_angle = end_angle;
q_dsc.start_quarter = (start_angle / 90) & 0x3;
q_dsc.end_quarter = (end_angle / 90) & 0x3;
q_dsc.width = width;
q_dsc.draw_dsc = &cir_dsc;
q_dsc.draw_area = &area_out;
q_dsc.draw_ctx = draw_ctx;
draw_quarter_0(&q_dsc);
draw_quarter_1(&q_dsc);
draw_quarter_2(&q_dsc);
draw_quarter_3(&q_dsc);
}
else {
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
}
lv_draw_mask_free_param(&mask_angle_param);
lv_draw_mask_free_param(&mask_out_param);
if(mask_in_param_valid) {
lv_draw_mask_free_param(&mask_in_param);
}
lv_draw_mask_remove_id(mask_angle_id);
lv_draw_mask_remove_id(mask_out_id);
if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id);
if(dsc->rounded) {
lv_draw_mask_radius_param_t mask_end_param;
lv_area_t round_area;
get_rounded_area(start_angle, radius, width, &round_area);
round_area.x1 += center->x;
round_area.x2 += center->x;
round_area.y1 += center->y;
round_area.y2 += center->y;
lv_area_t clip_area2;
if(_lv_area_intersect(&clip_area2, clip_area_ori, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
draw_ctx->clip_area = &clip_area2;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
get_rounded_area(end_angle, radius, width, &round_area);
round_area.x1 += center->x;
round_area.x2 += center->x;
round_area.y1 += center->y;
round_area.y2 += center->y;
if(_lv_area_intersect(&clip_area2, clip_area_ori, &round_area)) {
lv_draw_mask_radius_init(&mask_end_param, &round_area, LV_RADIUS_CIRCLE, false);
int16_t mask_end_id = lv_draw_mask_add(&mask_end_param, NULL);
draw_ctx->clip_area = &clip_area2;
lv_draw_rect(draw_ctx, &cir_dsc, &area_out);
lv_draw_mask_remove_id(mask_end_id);
lv_draw_mask_free_param(&mask_end_param);
}
draw_ctx->clip_area = clip_area_ori;
}
#else
LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
LV_UNUSED(center);
LV_UNUSED(radius);
LV_UNUSED(start_angle);
LV_UNUSED(end_angle);
LV_UNUSED(draw_ctx);
LV_UNUSED(dsc);
#endif /*LV_DRAW_COMPLEX*/
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_DRAW_COMPLEX
static void draw_quarter_0(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 0 && q->end_quarter == 0 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 0 || q->end_quarter == 0) {
/*Start and/or end arcs here*/
if(q->start_quarter == 0) {
quarter_area.x1 = q->center->x;
quarter_area.y2 = q->center->y + q->radius;
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 0) {
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y1 = q->center->y;
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 0 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 1) ||
(q->start_quarter == 3 && q->end_quarter == 2) ||
(q->start_quarter == 3 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y;
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_1(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 1 && q->end_quarter == 1 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 1 || q->end_quarter == 1) {
/*Start and/or end arcs here*/
if(q->start_quarter == 1) {
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y;
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 1) {
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y + q->radius;
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 1 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 2) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 3 && q->end_quarter == 2)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y;
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y + q->radius;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_2(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 2 && q->end_quarter == 2 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * q->radius) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 2 || q->end_quarter == 2) {
/*Start and/or end arcs here*/
if(q->start_quarter == 2) {
quarter_area.x2 = q->center->x - 1;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 2) {
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y2 = q->center->y - 1;
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 2 && q->end_angle < q->start_angle) ||
(q->start_quarter == 0 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 3) ||
(q->start_quarter == 1 && q->end_quarter == 0)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x - q->radius;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x - 1;
quarter_area.y2 = q->center->y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void draw_quarter_3(quarter_draw_dsc_t * q)
{
const lv_area_t * clip_area_ori = q->draw_ctx->clip_area;
lv_area_t quarter_area;
if(q->start_quarter == 3 && q->end_quarter == 3 && q->start_angle < q->end_angle) {
/*Small arc here*/
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
else if(q->start_quarter == 3 || q->end_quarter == 3) {
/*Start and/or end arcs here*/
if(q->start_quarter == 3) {
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y - 1;
quarter_area.x1 = q->center->x + ((lv_trigo_sin(q->start_angle + 90) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
quarter_area.y1 = q->center->y + ((lv_trigo_sin(q->start_angle) * (q->radius)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
if(q->end_quarter == 3) {
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x + ((lv_trigo_sin(q->end_angle + 90) * (q->radius)) >> LV_TRIGO_SHIFT);
quarter_area.y2 = q->center->y + ((lv_trigo_sin(q->end_angle) * (q->radius - q->width)) >> LV_TRIGO_SHIFT);
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
}
else if((q->start_quarter == q->end_quarter && q->start_quarter != 3 && q->end_angle < q->start_angle) ||
(q->start_quarter == 2 && q->end_quarter == 0) ||
(q->start_quarter == 1 && q->end_quarter == 0) ||
(q->start_quarter == 2 && q->end_quarter == 1)) {
/*Arc crosses here*/
quarter_area.x1 = q->center->x;
quarter_area.y1 = q->center->y - q->radius;
quarter_area.x2 = q->center->x + q->radius;
quarter_area.y2 = q->center->y - 1;
bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori);
if(ok) {
q->draw_ctx->clip_area = &quarter_area;
lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area);
}
}
q->draw_ctx->clip_area = clip_area_ori;
}
static void get_rounded_area(int16_t angle, lv_coord_t radius, uint8_t thickness, lv_area_t * res_area)
{
const uint8_t ps = 8;
const uint8_t pa = 127;
int32_t thick_half = thickness / 2;
uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
int32_t cir_x;
int32_t cir_y;
cir_x = ((radius - thick_half) * lv_trigo_sin(90 - angle)) >> (LV_TRIGO_SHIFT - ps);
cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
/*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
if(cir_x > 0) {
cir_x = (cir_x - pa) >> ps;
res_area->x1 = cir_x - thick_half + thick_corr;
res_area->x2 = cir_x + thick_half;
}
else {
cir_x = (cir_x + pa) >> ps;
res_area->x1 = cir_x - thick_half;
res_area->x2 = cir_x + thick_half - thick_corr;
}
if(cir_y > 0) {
cir_y = (cir_y - pa) >> ps;
res_area->y1 = cir_y - thick_half + thick_corr;
res_area->y2 = cir_y + thick_half;
}
else {
cir_y = (cir_y + pa) >> ps;
res_area->y1 = cir_y - thick_half;
res_area->y2 = cir_y + thick_half - thick_corr;
}
}
#endif /*LV_DRAW_COMPLEX*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
/**
* @file lv_draw_sw_blend.h
*
*/
#ifndef LV_DRAW_SW_BLEND_H
#define LV_DRAW_SW_BLEND_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../misc/lv_area.h"
#include "../../misc/lv_style.h"
#include "../lv_draw_mask.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
const lv_area_t * blend_area; /**< The area with absolute coordinates to draw on `draw_ctx->buf`
* will be clipped to `draw_ctx->clip_area` */
const lv_color_t * src_buf; /**< Pointer to an image to blend. If set `fill_color` is ignored */
lv_color_t color; /**< Fill color*/
lv_opa_t * mask_buf; /**< NULL if ignored, or an alpha mask to apply on `blend_area`*/
lv_draw_mask_res_t mask_res; /**< The result of the previous mask operation */
const lv_area_t * mask_area; /**< The area of `mask_buf` with absolute coordinates*/
lv_opa_t opa; /**< The overall opacity*/
lv_blend_mode_t blend_mode; /**< E.g. LV_BLEND_MODE_ADDITIVE*/
} lv_draw_sw_blend_dsc_t;
struct _lv_draw_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Call the blend function of the `draw_ctx`.
* @param draw_ctx pointer to a draw context
* @param dsc pointer to an initialized blend descriptor
*/
void lv_draw_sw_blend(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
/**
* The basic blend function used with software rendering.
* @param draw_ctx pointer to a draw context
* @param dsc pointer to an initialized blend descriptor
*/
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_basic(struct _lv_draw_ctx_t * draw_ctx,
const lv_draw_sw_blend_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_SW_BLEND_H*/

View File

@@ -0,0 +1,213 @@
/**
* @file lv_draw_sw_dither.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw_dither.h"
#include "lv_draw_sw_gradient.h"
#include "../../misc/lv_color.h"
/**********************
* STATIC FUNCTIONS
**********************/
#if _DITHER_GRADIENT
void LV_ATTRIBUTE_FAST_MEM lv_dither_none(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(x);
LV_UNUSED(y);
if(grad == NULL || grad->filled) return;
for(lv_coord_t i = 0; i < w; i++) {
grad->map[i] = lv_color_hex(grad->hmap[i].full);
}
grad->filled = 1;
}
static const uint8_t dither_ordered_threshold_matrix[8 * 8] = {
0, 48, 12, 60, 3, 51, 15, 63,
32, 16, 44, 28, 35, 19, 47, 31,
8, 56, 4, 52, 11, 59, 7, 55,
40, 24, 36, 20, 43, 27, 39, 23,
2, 50, 14, 62, 1, 49, 13, 61,
34, 18, 46, 30, 33, 17, 45, 29,
10, 58, 6, 54, 9, 57, 5, 53,
42, 26, 38, 22, 41, 25, 37, 21
}; /* Shift by 6 to normalize */
void LV_ATTRIBUTE_FAST_MEM lv_dither_ordered_hor(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(x);
/* For vertical dithering, the error is spread on the next column (and not next line).
Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently.
The algorithm below is based on few assumptions:
1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n
2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here)
3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one.
Then we compute a complete row of ordered dither and store it in out. */
/*The apply the algorithm for this patch*/
for(lv_coord_t j = 0; j < w; j++) {
int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j) & 7)] - 32;
lv_color32_t tmp = grad->hmap[LV_CLAMP(0, j - 4, grad->size)];
lv_color32_t t;
t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255);
t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255);
t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255);
grad->map[j] = lv_color_hex(t.full);
}
}
void LV_ATTRIBUTE_FAST_MEM lv_dither_ordered_ver(lv_grad_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w)
{
/* For vertical dithering, the error is spread on the next column (and not next line).
Since the renderer is scanline based, it's not obvious what could be used to perform the rendering efficiently.
The algorithm below is based on few assumptions:
1. An error diffusion algorithm (like Floyd Steinberg) here would be hard to implement since it means that a pixel on column n depends on the pixel on row n
2. Instead an ordered dithering algorithm shift the value a bit, but the influence only spread from the matrix size (used 8x8 here)
3. It means that a pixel i,j only depends on the value of a pixel i-7, j-7 to i,j and no other one.
Then we compute a complete row of ordered dither and store it in out. */
/*Extract patch for working with, selected pseudo randomly*/
lv_color32_t tmp = grad->hmap[LV_CLAMP(0, y - 4, grad->size)];
/*The apply the algorithm for this patch*/
for(lv_coord_t j = 0; j < 8; j++) {
int8_t factor = dither_ordered_threshold_matrix[(y & 7) * 8 + ((j + x) & 7)] - 32;
lv_color32_t t;
t.ch.red = LV_CLAMP(0, tmp.ch.red + factor, 255);
t.ch.green = LV_CLAMP(0, tmp.ch.green + factor, 255);
t.ch.blue = LV_CLAMP(0, tmp.ch.blue + factor, 255);
grad->map[j] = lv_color_hex(t.full);
}
/*Finally fill the line*/
lv_coord_t j = 8;
for(; j < w - 8; j += 8) {
lv_memcpy(grad->map + j, grad->map, 8 * sizeof(*grad->map));
}
/* Prevent overwriting */
for(; j < w; j++) {
grad->map[j] = grad->map[j & 7];
}
}
#if LV_DITHER_ERROR_DIFFUSION == 1
void LV_ATTRIBUTE_FAST_MEM lv_dither_err_diff_hor(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w)
{
LV_UNUSED(xs);
LV_UNUSED(y);
LV_UNUSED(w);
/* Implement Floyd Steinberg algorithm, see https://surma.dev/things/ditherpunk/
Coefs are: x 7
3 5 1
/ 16
Can be implemented as: x (x<<3 - x)
(x<<2 - x) (x<<2+x) x
*/
int coef[4] = {0, 0, 0, 0};
#define FS_COMPUTE_ERROR(e) { coef[0] = (e<<3) - e; coef[1] = (e<<2) - e; coef[2] = (e<<2) + e; coef[3] = e; }
#define FS_COMPONENTS(A, OP, B, C) A.ch.red = LV_CLAMP(0, A.ch.red OP B.r OP C.r, 255); A.ch.green = LV_CLAMP(0, A.ch.green OP B.g OP C.g, 255); A.ch.blue = LV_CLAMP(0, A.ch.blue OP B.b OP C.b, 255);
#define FS_QUANT_ERROR(e, t, q) { lv_color32_t u; u.full = lv_color_to32(q); e.r = (int8_t)(t.ch.red - u.ch.red); e.g = (int8_t)(t.ch.green - u.ch.green); e.b = (int8_t)(t.ch.blue - u.ch.blue); }
lv_scolor24_t next_px_err = {0}, next_l = {0}, error;
/*First last pixel are not dithered */
grad->map[0] = lv_color_hex(grad->hmap[0].full);
for(lv_coord_t x = 1; x < grad->size - 1; x++) {
lv_color32_t t = grad->hmap[x];
lv_color_t q;
/*Add error term*/
FS_COMPONENTS(t, +, next_px_err, next_l);
next_l = grad->error_acc[x + 1];
/*Quantify*/
q = lv_color_hex(t.full);
/*Then compute error*/
FS_QUANT_ERROR(error, t, q);
/*Dither the error*/
FS_COMPUTE_ERROR(error.r);
next_px_err.r = coef[0] >> 4;
grad->error_acc[x - 1].r += coef[1] >> 4;
grad->error_acc[x].r += coef[2] >> 4;
grad->error_acc[x + 1].r = coef[3] >> 4;
FS_COMPUTE_ERROR(error.g);
next_px_err.g = coef[0] >> 4;
grad->error_acc[x - 1].g += coef[1] >> 4;
grad->error_acc[x].g += coef[2] >> 4;
grad->error_acc[x + 1].g = coef[3] >> 4;
FS_COMPUTE_ERROR(error.b);
next_px_err.b = coef[0] >> 4;
grad->error_acc[x - 1].b += coef[1] >> 4;
grad->error_acc[x].b += coef[2] >> 4;
grad->error_acc[x + 1].b = coef[3] >> 4;
grad->map[x] = q;
}
grad->map[grad->size - 1] = lv_color_hex(grad->hmap[grad->size - 1].full);
}
void LV_ATTRIBUTE_FAST_MEM lv_dither_err_diff_ver(lv_grad_t * grad, lv_coord_t xs, lv_coord_t y, lv_coord_t w)
{
/* Try to implement error diffusion on a vertical gradient and an horizontal map using those tricks:
Since the given hi-resolution gradient (in src) is vertical, the Floyd Steinberg algorithm pass need to be rotated,
so we'll get this instead (from top to bottom):
A B C
1 [ ][ ][ ]
2 [ ][ ][ ] Pixel A2 will spread its error on pixel A3 with coefficient 7,
3 [ ][ ][ ] Pixel A2 will spread its error on pixel B1 with coefficient 3, B2 with coef 5 and B3 with coef 1
When taking into account an arbitrary pixel P(i,j), its added error diffusion term is:
e(i,j) = 1/16 * [ e(i-1,j) * 5 + e(i-1,j+1) * 3 + e(i-1,j-1) * 1 + e(i,j-1) * 7]
This means that the error term depends on pixel W, NW, N and SW.
If we consider that we are generating the error diffused gradient map from top to bottom, we can remember the previous
line (N, NW) in the term above. Also, we remember the (W) term too since we are computing the gradient map from left to right.
However, the SW term is painful for us, we can't support it (since to get it, we need its own SW term and so on).
Let's remove it and re-dispatch the error factor accordingly so they stays normalized:
e(i,j) ~= 1/16 * [ e(i-1,j) * 6 + e(i-1,j-1) * 1 + e(i,j-1) * 9]
That's the idea of this pseudo Floyd Steinberg dithering */
#define FS_APPLY(d, s, c) d.r = (int8_t)(s.r * c) >> 4; d.g = (int8_t)(s.g * c) >> 4; d.b = (int8_t)(s.b * c) >> 4;
#define FS_COMPONENTS3(A, OP, B, b, C, c, D, d) \
A.ch.red = LV_CLAMP(0, A.ch.red OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \
A.ch.green = LV_CLAMP(0, A.ch.green OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255); \
A.ch.blue = LV_CLAMP(0, A.ch.blue OP ((B.r * b OP C.r * c OP D.r * d) >> 4), 255);
lv_scolor24_t next_px_err, prev_l = grad->error_acc[0];
/*Compute the error term for the current pixel (first pixel is never dithered)*/
if(xs == 0) {
grad->map[0] = lv_color_hex(grad->hmap[y].full);
FS_QUANT_ERROR(next_px_err, grad->hmap[y], grad->map[0]);
}
else {
lv_color_t tmp = lv_color_hex(grad->hmap[y].full);
lv_color32_t t = grad->hmap[y];
FS_QUANT_ERROR(next_px_err, grad->hmap[y], tmp);
FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[0], 9);
grad->map[0] = lv_color_hex(t.full);
}
for(lv_coord_t x = 1; x < w; x++) {
lv_color32_t t = grad->hmap[y];
lv_color_t q;
/*Add the current error term*/
FS_COMPONENTS3(t, +, next_px_err, 6, prev_l, 1, grad->error_acc[x], 9);
prev_l = grad->error_acc[x];
/*Quantize and compute error term*/
q = lv_color_hex(t.full);
FS_QUANT_ERROR(next_px_err, t, q);
/*Store error for next line computation*/
grad->error_acc[x] = next_px_err;
grad->map[x] = q;
}
}
#endif
#endif

View File

@@ -0,0 +1,71 @@
/**
* @file lv_draw_sw_dither.h
*
*/
#ifndef LV_DRAW_SW_DITHER_H
#define LV_DRAW_SW_DITHER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../core/lv_obj_pos.h"
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH < 32 && LV_DITHER_GRADIENT == 1
#define _DITHER_GRADIENT 1
#else
#define _DITHER_GRADIENT 0
#endif
/**********************
* TYPEDEFS
**********************/
#if _DITHER_GRADIENT
/*A signed error color component*/
typedef struct {
int8_t r, g, b;
} lv_scolor24_t;
struct _lv_gradient_cache_t;
typedef void (*lv_dither_func_t)(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y, lv_coord_t w);
#endif
/**********************
* PROTOTYPES
**********************/
#if LV_DRAW_COMPLEX
#if _DITHER_GRADIENT
void /* LV_ATTRIBUTE_FAST_MEM */ lv_dither_none(struct _lv_gradient_cache_t * grad, lv_coord_t x, lv_coord_t y,
lv_coord_t w);
void /* LV_ATTRIBUTE_FAST_MEM */ lv_dither_ordered_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
void /* LV_ATTRIBUTE_FAST_MEM */ lv_dither_ordered_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
#if LV_DITHER_ERROR_DIFFUSION == 1
void /* LV_ATTRIBUTE_FAST_MEM */ lv_dither_err_diff_hor(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
void /* LV_ATTRIBUTE_FAST_MEM */ lv_dither_err_diff_ver(struct _lv_gradient_cache_t * grad, const lv_coord_t xs,
const lv_coord_t y, const lv_coord_t w);
#endif /* LV_DITHER_ERROR_DIFFUSION */
#endif /* _DITHER_GRADIENT */
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif

View File

@@ -0,0 +1,346 @@
/**
* @file lv_draw_sw_gradient.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw_gradient.h"
#include "../../misc/lv_gc.h"
#include "../../misc/lv_types.h"
/*********************
* DEFINES
*********************/
#if _DITHER_GRADIENT
#define GRAD_CM(r,g,b) LV_COLOR_MAKE32(r,g,b)
#define GRAD_CONV(t, x) t.full = lv_color_to32(x)
#else
#define GRAD_CM(r,g,b) LV_COLOR_MAKE(r,g,b)
#define GRAD_CONV(t, x) t = x
#endif
#undef ALIGN
#if defined(LV_ARCH_64)
#define ALIGN(X) (((X) + 7) & ~7)
#else
#define ALIGN(X) (((X) + 3) & ~3)
#endif
#if LV_GRAD_CACHE_DEF_SIZE != 0 && LV_GRAD_CACHE_DEF_SIZE < 256
#error "LV_GRAD_CACHE_DEF_SIZE is too small"
#endif
/**********************
* STATIC PROTOTYPES
**********************/
static lv_grad_t * next_in_cache(lv_grad_t * item);
typedef lv_res_t (*op_cache_t)(lv_grad_t * c, void * ctx);
static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_grad_t ** out);
static size_t get_cache_item_size(lv_grad_t * c);
static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h);
static lv_res_t find_oldest_item_life(lv_grad_t * c, void * ctx);
static lv_res_t kill_oldest_item(lv_grad_t * c, void * ctx);
static lv_res_t find_item(lv_grad_t * c, void * ctx);
static void free_item(lv_grad_t * c);
static uint32_t compute_key(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h);
/**********************
* STATIC VARIABLE
**********************/
static size_t grad_cache_size = 0;
static uint8_t * grad_cache_end = 0;
/**********************
* STATIC FUNCTIONS
**********************/
union void_cast {
const void * ptr;
const uint32_t value;
};
static uint32_t compute_key(const lv_grad_dsc_t * g, lv_coord_t size, lv_coord_t w)
{
union void_cast v;
v.ptr = g;
return (v.value ^ size ^ (w >> 1)); /*Yes, this is correct, it's like a hash that changes if the width changes*/
}
static size_t get_cache_item_size(lv_grad_t * c)
{
size_t s = ALIGN(sizeof(*c)) + ALIGN(c->alloc_size * sizeof(lv_color_t));
#if _DITHER_GRADIENT
s += ALIGN(c->size * sizeof(lv_color32_t));
#if LV_DITHER_ERROR_DIFFUSION == 1
s += ALIGN(c->w * sizeof(lv_scolor24_t));
#endif
#endif
return s;
}
static lv_grad_t * next_in_cache(lv_grad_t * item)
{
if(grad_cache_size == 0) return NULL;
if(item == NULL)
return (lv_grad_t *)LV_GC_ROOT(_lv_grad_cache_mem);
size_t s = get_cache_item_size(item);
/*Compute the size for this cache item*/
if((uint8_t *)item + s >= grad_cache_end) return NULL;
else return (lv_grad_t *)((uint8_t *)item + s);
}
static lv_res_t iterate_cache(op_cache_t func, void * ctx, lv_grad_t ** out)
{
lv_grad_t * first = next_in_cache(NULL);
while(first != NULL && first->life) {
if((*func)(first, ctx) == LV_RES_OK) {
if(out != NULL) *out = first;
return LV_RES_OK;
}
first = next_in_cache(first);
}
return LV_RES_INV;
}
static lv_res_t find_oldest_item_life(lv_grad_t * c, void * ctx)
{
uint32_t * min_life = (uint32_t *)ctx;
if(c->life < *min_life) *min_life = c->life;
return LV_RES_INV;
}
static void free_item(lv_grad_t * c)
{
size_t size = get_cache_item_size(c);
size_t next_items_size = (size_t)(grad_cache_end - (uint8_t *)c) - size;
grad_cache_end -= size;
if(next_items_size) {
uint8_t * old = (uint8_t *)c;
lv_memcpy(c, ((uint8_t *)c) + size, next_items_size);
/* Then need to fix all internal pointers too */
while((uint8_t *)c != grad_cache_end) {
c->map = (lv_color_t *)(((uint8_t *)c->map) - size);
#if _DITHER_GRADIENT
c->hmap = (lv_color32_t *)(((uint8_t *)c->hmap) - size);
#if LV_DITHER_ERROR_DIFFUSION == 1
c->error_acc = (lv_scolor24_t *)(((uint8_t *)c->error_acc) - size);
#endif
#endif
c = (lv_grad_t *)(((uint8_t *)c) + get_cache_item_size(c));
}
lv_memset_00(old + next_items_size, size);
}
}
static lv_res_t kill_oldest_item(lv_grad_t * c, void * ctx)
{
uint32_t * min_life = (uint32_t *)ctx;
if(c->life == *min_life) {
/*Found, let's kill it*/
free_item(c);
return LV_RES_OK;
}
return LV_RES_INV;
}
static lv_res_t find_item(lv_grad_t * c, void * ctx)
{
uint32_t * k = (uint32_t *)ctx;
if(c->key == *k) return LV_RES_OK;
return LV_RES_INV;
}
static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h)
{
lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h;
lv_coord_t map_size = LV_MAX(w, h); /* The map is being used horizontally (width) unless
no dithering is selected where it's used vertically */
size_t req_size = ALIGN(sizeof(lv_grad_t)) + ALIGN(map_size * sizeof(lv_color_t));
#if _DITHER_GRADIENT
req_size += ALIGN(size * sizeof(lv_color32_t));
#if LV_DITHER_ERROR_DIFFUSION == 1
req_size += ALIGN(w * sizeof(lv_scolor24_t));
#endif
#endif
size_t act_size = (size_t)(grad_cache_end - LV_GC_ROOT(_lv_grad_cache_mem));
lv_grad_t * item = NULL;
if(req_size + act_size < grad_cache_size) {
item = (lv_grad_t *)grad_cache_end;
item->not_cached = 0;
}
else {
/*Need to evict items from cache until we find enough space to allocate this one */
if(req_size <= grad_cache_size) {
while(act_size + req_size > grad_cache_size) {
uint32_t oldest_life = UINT32_MAX;
iterate_cache(&find_oldest_item_life, &oldest_life, NULL);
iterate_cache(&kill_oldest_item, &oldest_life, NULL);
act_size = (size_t)(grad_cache_end - LV_GC_ROOT(_lv_grad_cache_mem));
}
item = (lv_grad_t *)grad_cache_end;
item->not_cached = 0;
}
else {
/*The cache is too small. Allocate the item manually and free it later.*/
item = lv_mem_alloc(req_size);
LV_ASSERT_MALLOC(item);
if(item == NULL) return NULL;
item->not_cached = 1;
}
}
item->key = compute_key(g, size, w);
item->life = 1;
item->filled = 0;
item->alloc_size = map_size;
item->size = size;
if(item->not_cached) {
uint8_t * p = (uint8_t *)item;
item->map = (lv_color_t *)(p + ALIGN(sizeof(*item)));
#if _DITHER_GRADIENT
item->hmap = (lv_color32_t *)(p + ALIGN(sizeof(*item)) + ALIGN(map_size * sizeof(lv_color_t)));
#if LV_DITHER_ERROR_DIFFUSION == 1
item->error_acc = (lv_scolor24_t *)(p + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_grad_color_t)) +
ALIGN(map_size * sizeof(lv_color_t)));
item->w = w;
#endif
#endif
}
else {
item->map = (lv_color_t *)(grad_cache_end + ALIGN(sizeof(*item)));
#if _DITHER_GRADIENT
item->hmap = (lv_color32_t *)(grad_cache_end + ALIGN(sizeof(*item)) + ALIGN(map_size * sizeof(lv_color_t)));
#if LV_DITHER_ERROR_DIFFUSION == 1
item->error_acc = (lv_scolor24_t *)(grad_cache_end + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_grad_color_t)) +
ALIGN(map_size * sizeof(lv_color_t)));
item->w = w;
#endif
#endif
grad_cache_end += req_size;
}
return item;
}
/**********************
* FUNCTIONS
**********************/
void lv_gradient_free_cache(void)
{
lv_mem_free(LV_GC_ROOT(_lv_grad_cache_mem));
LV_GC_ROOT(_lv_grad_cache_mem) = grad_cache_end = NULL;
grad_cache_size = 0;
}
void lv_gradient_set_cache_size(size_t max_bytes)
{
lv_mem_free(LV_GC_ROOT(_lv_grad_cache_mem));
grad_cache_end = LV_GC_ROOT(_lv_grad_cache_mem) = lv_mem_alloc(max_bytes);
LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_grad_cache_mem));
lv_memset_00(LV_GC_ROOT(_lv_grad_cache_mem), max_bytes);
grad_cache_size = max_bytes;
}
lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * g, lv_coord_t w, lv_coord_t h)
{
/* No gradient, no cache */
if(g->dir == LV_GRAD_DIR_NONE) return NULL;
/* Step 0: Check if the cache exist (else create it) */
static bool inited = false;
if(!inited) {
lv_gradient_set_cache_size(LV_GRAD_CACHE_DEF_SIZE);
inited = true;
}
/* Step 1: Search cache for the given key */
lv_coord_t size = g->dir == LV_GRAD_DIR_HOR ? w : h;
uint32_t key = compute_key(g, size, w);
lv_grad_t * item = NULL;
if(iterate_cache(&find_item, &key, &item) == LV_RES_OK) {
item->life++; /* Don't forget to bump the counter */
return item;
}
/* Step 2: Need to allocate an item for it */
item = allocate_item(g, w, h);
if(item == NULL) {
LV_LOG_WARN("Faild to allcoate item for teh gradient");
return item;
}
/* Step 3: Fill it with the gradient, as expected */
#if _DITHER_GRADIENT
for(lv_coord_t i = 0; i < item->size; i++) {
item->hmap[i] = lv_gradient_calculate(g, item->size, i);
}
#if LV_DITHER_ERROR_DIFFUSION == 1
lv_memset_00(item->error_acc, w * sizeof(lv_scolor24_t));
#endif
#else
for(lv_coord_t i = 0; i < item->size; i++) {
item->map[i] = lv_gradient_calculate(g, item->size, i);
}
#endif
return item;
}
lv_grad_color_t LV_ATTRIBUTE_FAST_MEM lv_gradient_calculate(const lv_grad_dsc_t * dsc, lv_coord_t range,
lv_coord_t frac)
{
lv_grad_color_t tmp;
lv_color32_t one, two;
/*Clip out-of-bounds first*/
int32_t min = (dsc->stops[0].frac * range) >> 8;
if(frac <= min) {
GRAD_CONV(tmp, dsc->stops[0].color);
return tmp;
}
int32_t max = (dsc->stops[dsc->stops_count - 1].frac * range) >> 8;
if(frac >= max) {
GRAD_CONV(tmp, dsc->stops[dsc->stops_count - 1].color);
return tmp;
}
/*Find the 2 closest stop now*/
int32_t d = 0;
for(uint8_t i = 1; i < dsc->stops_count; i++) {
int32_t cur = (dsc->stops[i].frac * range) >> 8;
if(frac <= cur) {
one.full = lv_color_to32(dsc->stops[i - 1].color);
two.full = lv_color_to32(dsc->stops[i].color);
min = (dsc->stops[i - 1].frac * range) >> 8;
max = (dsc->stops[i].frac * range) >> 8;
d = max - min;
break;
}
}
LV_ASSERT(d != 0);
/*Then interpolate*/
frac -= min;
lv_opa_t mix = (frac * 255) / d;
lv_opa_t imix = 255 - mix;
lv_grad_color_t r = GRAD_CM(LV_UDIV255(two.ch.red * mix + one.ch.red * imix),
LV_UDIV255(two.ch.green * mix + one.ch.green * imix),
LV_UDIV255(two.ch.blue * mix + one.ch.blue * imix));
return r;
}
void lv_gradient_cleanup(lv_grad_t * grad)
{
if(grad->not_cached) {
lv_mem_free(grad);
}
}

View File

@@ -0,0 +1,97 @@
/**
* @file lv_draw_sw_gradient.h
*
*/
#ifndef LV_DRAW_SW_GRADIENT_H
#define LV_DRAW_SW_GRADIENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_color.h"
#include "../../misc/lv_style.h"
#include "lv_draw_sw_dither.h"
/*********************
* DEFINES
*********************/
#if LV_GRADIENT_MAX_STOPS < 2
#error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS
#endif
/**********************
* TYPEDEFS
**********************/
#if _DITHER_GRADIENT
typedef lv_color32_t lv_grad_color_t;
#else
typedef lv_color_t lv_grad_color_t;
#endif
/** To avoid recomputing gradient for each draw operation,
* it's possible to cache the computation in this structure instance.
* Whenever possible, this structure is reused instead of recomputing the gradient map */
typedef struct _lv_gradient_cache_t {
uint32_t key; /**< A discriminating key that's built from the drawing operation.
* If the key does not match, the cache item is not used */
uint32_t life : 30; /**< A life counter that's incremented on usage. Higher counter is
* less likely to be evicted from the cache */
uint32_t filled : 1; /**< Used to skip dithering in it if already done */
uint32_t not_cached: 1; /**< The cache was too small so this item is not managed by the cache*/
lv_color_t * map; /**< The computed gradient low bitdepth color map, points into the
* cache's buffer, no free needed */
lv_coord_t alloc_size; /**< The map allocated size in colors */
lv_coord_t size; /**< The computed gradient color map size, in colors */
#if _DITHER_GRADIENT
lv_color32_t * hmap; /**< If dithering, we need to store the current, high bitdepth gradient
* map too, points to the cache's buffer, no free needed */
#if LV_DITHER_ERROR_DIFFUSION == 1
lv_scolor24_t * error_acc; /**< Error diffusion dithering algorithm requires storing the last error
* drawn, points to the cache's buffer, no free needed */
lv_coord_t w; /**< The error array width in pixels */
#endif
#endif
} lv_grad_t;
/**********************
* PROTOTYPES
**********************/
/** Compute the color in the given gradient and fraction
* Gradient are specified in a virtual [0-255] range, so this function scales the virtual range to the given range
* @param dsc The gradient descriptor to use
* @param range The range to use in computation.
* @param frac The current part used in the range. frac is in [0; range]
*/
lv_grad_color_t /* LV_ATTRIBUTE_FAST_MEM */ lv_gradient_calculate(const lv_grad_dsc_t * dsc, lv_coord_t range,
lv_coord_t frac);
/**
* Set the gradient cache size
* @param max_bytes Max cahce size
*/
void lv_gradient_set_cache_size(size_t max_bytes);
/** Free the gradient cache */
void lv_gradient_free_cache(void);
/** Get a gradient cache from the given parameters */
lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * gradient, lv_coord_t w, lv_coord_t h);
/**
* Clean up the gradient item after it was get with `lv_grad_get_from_cache`.
* @param grad pointer to a gradient
*/
void lv_gradient_cleanup(lv_grad_t * grad);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_GRADIENT_H*/

View File

@@ -0,0 +1,299 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#include "../lv_img_cache.h"
#include "../../hal/lv_hal_disp.h"
#include "../../misc/lv_log.h"
#include "../../core/lv_refr.h"
#include "../../misc/lv_mem.h"
#include "../../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define MAX_BUF_SIZE (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing())
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h,
lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx,
const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const uint8_t * src_buf,
lv_img_cf_t cf)
{
/*Use the clip area as draw area*/
lv_area_t draw_area;
lv_area_copy(&draw_area, draw_ctx->clip_area);
bool mask_any = lv_draw_mask_is_any(&draw_area);
bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false;
lv_area_t blend_area;
lv_draw_sw_blend_dsc_t blend_dsc;
lv_memset_00(&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.blend_area = &blend_area;
/*The simplest case just copy the pixels into the draw_buf*/
if(!mask_any && !transform && cf == LV_IMG_CF_TRUE_COLOR && draw_dsc->recolor_opa == LV_OPA_TRANSP) {
blend_dsc.src_buf = (const lv_color_t *)src_buf;
blend_dsc.blend_area = coords;
lv_draw_sw_blend(draw_ctx, &blend_dsc);
}
else if(!mask_any && !transform && cf == LV_IMG_CF_ALPHA_8BIT) {
lv_area_t clipped_coords;
if(!_lv_area_intersect(&clipped_coords, coords, draw_ctx->clip_area)) return;
blend_dsc.mask_buf = (lv_opa_t *)src_buf;
blend_dsc.mask_area = coords;
blend_dsc.src_buf = NULL;
blend_dsc.color = draw_dsc->recolor;
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
blend_dsc.blend_area = coords;
lv_draw_sw_blend(draw_ctx, &blend_dsc);
}
#if LV_COLOR_DEPTH == 16
else if(!mask_any && !transform && cf == LV_IMG_CF_RGB565A8 && draw_dsc->recolor_opa == LV_OPA_TRANSP) {
lv_coord_t src_w = lv_area_get_width(coords);
lv_coord_t src_h = lv_area_get_height(coords);
blend_dsc.src_buf = (const lv_color_t *)src_buf;
blend_dsc.mask_buf = (lv_opa_t *)src_buf;
blend_dsc.mask_buf += sizeof(lv_color_t) * src_w * src_h;
blend_dsc.blend_area = coords;
blend_dsc.mask_area = coords;
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
lv_draw_sw_blend(draw_ctx, &blend_dsc);
}
#endif
/*In the other cases every pixel need to be checked one-by-one*/
else {
blend_area.x1 = draw_ctx->clip_area->x1;
blend_area.x2 = draw_ctx->clip_area->x2;
blend_area.y1 = draw_ctx->clip_area->y1;
blend_area.y2 = draw_ctx->clip_area->y2;
lv_coord_t src_w = lv_area_get_width(coords);
lv_coord_t src_h = lv_area_get_height(coords);
lv_coord_t blend_h = lv_area_get_height(&blend_area);
lv_coord_t blend_w = lv_area_get_width(&blend_area);
uint32_t max_buf_size = MAX_BUF_SIZE;
uint32_t blend_size = lv_area_get_size(&blend_area);
uint32_t buf_h;
uint32_t buf_w = blend_w;
if(blend_size <= max_buf_size) {
buf_h = blend_h;
}
else {
/*Round to full lines*/
buf_h = max_buf_size / blend_w;
}
/*Create buffers and masks*/
uint32_t buf_size = buf_w * buf_h;
lv_color_t * rgb_buf = lv_mem_buf_get(buf_size * sizeof(lv_color_t));
lv_opa_t * mask_buf = lv_mem_buf_get(buf_size);
blend_dsc.mask_buf = mask_buf;
blend_dsc.mask_area = &blend_area;
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
blend_dsc.src_buf = rgb_buf;
lv_coord_t y_last = blend_area.y2;
blend_area.y2 = blend_area.y1 + buf_h - 1;
lv_draw_mask_res_t mask_res_def = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle ||
draw_dsc->zoom != LV_IMG_ZOOM_NONE) ?
LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER;
blend_dsc.mask_res = mask_res_def;
while(blend_area.y1 <= y_last) {
/*Apply transformations if any or separate the channels*/
lv_area_t transform_area;
lv_area_copy(&transform_area, &blend_area);
lv_area_move(&transform_area, -coords->x1, -coords->y1);
if(transform) {
lv_draw_transform(draw_ctx, &transform_area, src_buf, src_w, src_h, src_w,
draw_dsc, cf, rgb_buf, mask_buf);
}
else {
convert_cb(&transform_area, src_buf, src_w, src_h, src_w, draw_dsc, cf, rgb_buf, mask_buf);
}
/*Apply recolor*/
if(draw_dsc->recolor_opa > LV_OPA_MIN) {
uint16_t premult_v[3];
lv_opa_t recolor_opa = draw_dsc->recolor_opa;
lv_color_t recolor = draw_dsc->recolor;
lv_color_premult(recolor, recolor_opa, premult_v);
recolor_opa = 255 - recolor_opa;
uint32_t i;
for(i = 0; i < buf_size; i++) {
rgb_buf[i] = lv_color_mix_premult(premult_v, rgb_buf[i], recolor_opa);
}
}
#if LV_DRAW_COMPLEX
/*Apply the masks if any*/
if(mask_any) {
lv_coord_t y;
lv_opa_t * mask_buf_tmp = mask_buf;
for(y = blend_area.y1; y <= blend_area.y2; y++) {
lv_draw_mask_res_t mask_res_line;
mask_res_line = lv_draw_mask_apply(mask_buf_tmp, blend_area.x1, y, blend_w);
if(mask_res_line == LV_DRAW_MASK_RES_TRANSP) {
lv_memset_00(mask_buf_tmp, blend_w);
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
}
else if(mask_res_line == LV_DRAW_MASK_RES_CHANGED) {
blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
}
mask_buf_tmp += blend_w;
}
}
#endif
/*Blend*/
lv_draw_sw_blend(draw_ctx, &blend_dsc);
/*Go the the next lines*/
blend_area.y1 = blend_area.y2 + 1;
blend_area.y2 = blend_area.y1 + buf_h - 1;
if(blend_area.y2 > y_last) blend_area.y2 = y_last;
}
lv_mem_buf_release(mask_buf);
lv_mem_buf_release(rgb_buf);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/* Separate the image channels to RGB and Alpha to match LV_COLOR_DEPTH settings*/
static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h,
lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf)
{
LV_UNUSED(draw_dsc);
LV_UNUSED(src_h);
LV_UNUSED(src_w);
const uint8_t * src_tmp8 = (const uint8_t *)src_buf;
lv_coord_t y;
lv_coord_t x;
if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint32_t px_cnt = lv_area_get_size(dest_area);
lv_memset_ff(abuf, px_cnt);
src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t);
uint32_t dest_w = lv_area_get_width(dest_area);
uint32_t dest_w_byte = dest_w * sizeof(lv_color_t);
lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t);
lv_color_t * cbuf_tmp = cbuf;
for(y = dest_area->y1; y <= dest_area->y2; y++) {
lv_memcpy(cbuf_tmp, src_tmp8, dest_w_byte);
src_tmp8 += src_stride_byte;
cbuf_tmp += dest_w;
}
/*Make "holes" for with Chroma keying*/
if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint32_t i;
lv_color_t chk = LV_COLOR_CHROMA_KEY;
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
uint8_t * cbuf_uint = (uint8_t *)cbuf;
uint8_t chk_v = chk.full;
#elif LV_COLOR_DEPTH == 16
uint16_t * cbuf_uint = (uint16_t *)cbuf;
uint16_t chk_v = chk.full;
#elif LV_COLOR_DEPTH == 32
uint32_t * cbuf_uint = (uint32_t *)cbuf;
uint32_t chk_v = chk.full;
#endif
for(i = 0; i < px_cnt; i++) {
if(chk_v == cbuf_uint[i]) abuf[i] = 0x00;
}
}
}
else if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
src_tmp8 += (src_stride * dest_area->y1 * LV_IMG_PX_SIZE_ALPHA_BYTE) + dest_area->x1 * LV_IMG_PX_SIZE_ALPHA_BYTE;
lv_coord_t src_new_line_step_px = (src_stride - lv_area_get_width(dest_area));
lv_coord_t src_new_line_step_byte = src_new_line_step_px * LV_IMG_PX_SIZE_ALPHA_BYTE;
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_coord_t dest_w = lv_area_get_width(dest_area);
for(y = 0; y < dest_h; y++) {
for(x = 0; x < dest_w; x++) {
abuf[x] = src_tmp8[LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
cbuf[x].full = *src_tmp8;
#elif LV_COLOR_DEPTH == 16
cbuf[x].full = *src_tmp8 + ((*(src_tmp8 + 1)) << 8);
#elif LV_COLOR_DEPTH == 32
cbuf[x] = *((lv_color_t *) src_tmp8);
cbuf[x].ch.alpha = 0xff;
#endif
src_tmp8 += LV_IMG_PX_SIZE_ALPHA_BYTE;
}
cbuf += dest_w;
abuf += dest_w;
src_tmp8 += src_new_line_step_byte;
}
}
else if(cf == LV_IMG_CF_RGB565A8) {
src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t);
lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t);
lv_coord_t dest_h = lv_area_get_height(dest_area);
lv_coord_t dest_w = lv_area_get_width(dest_area);
for(y = 0; y < dest_h; y++) {
lv_memcpy(cbuf, src_tmp8, dest_w * sizeof(lv_color_t));
cbuf += dest_w;
src_tmp8 += src_stride_byte;
}
src_tmp8 = (const uint8_t *)src_buf;
src_tmp8 += sizeof(lv_color_t) * src_w * src_h;
src_tmp8 += src_stride * dest_area->y1 + dest_area->x1;
for(y = 0; y < dest_h; y++) {
lv_memcpy(abuf, src_tmp8, dest_w);
abuf += dest_w;
src_tmp8 += src_stride;
}
}
}

View File

@@ -0,0 +1,150 @@
/**
* @file lv_draw_sw_layer.h
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_sw.h"
#include "../../hal/lv_hal_disp.h"
#include "../../misc/lv_area.h"
#include "../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags)
{
if(LV_COLOR_SCREEN_TRANSP == 0 && (flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA)) {
LV_LOG_WARN("Rendering this widget needs LV_COLOR_SCREEN_TRANSP 1");
return NULL;
}
lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx;
uint32_t px_size = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_BUF_SIZE;
uint32_t full_size = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size;
if(layer_sw_ctx->buf_size_bytes > full_size) layer_sw_ctx->buf_size_bytes = full_size;
layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes);
if(layer_sw_ctx->base_draw.buf == NULL) {
LV_LOG_WARN("Cannot allocate %"LV_PRIu32" bytes for layer buffer. Allocating %"LV_PRIu32" bytes instead. (Reduced performance)",
(uint32_t)layer_sw_ctx->buf_size_bytes, (uint32_t)LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE * px_size);
layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE;
layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes);
if(layer_sw_ctx->base_draw.buf == NULL) {
return NULL;
}
}
layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full;
layer_sw_ctx->base_draw.area_act.y2 = layer_sw_ctx->base_draw.area_full.y1;
lv_coord_t w = lv_area_get_width(&layer_sw_ctx->base_draw.area_act);
layer_sw_ctx->base_draw.max_row_with_alpha = layer_sw_ctx->buf_size_bytes / w / LV_IMG_PX_SIZE_ALPHA_BYTE;
layer_sw_ctx->base_draw.max_row_with_no_alpha = layer_sw_ctx->buf_size_bytes / w / sizeof(lv_color_t);
}
else {
layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full;
layer_sw_ctx->buf_size_bytes = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size;
layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes);
lv_memset_00(layer_sw_ctx->base_draw.buf, layer_sw_ctx->buf_size_bytes);
layer_sw_ctx->has_alpha = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0;
if(layer_sw_ctx->base_draw.buf == NULL) {
return NULL;
}
draw_ctx->buf = layer_sw_ctx->base_draw.buf;
draw_ctx->buf_area = &layer_sw_ctx->base_draw.area_act;
draw_ctx->clip_area = &layer_sw_ctx->base_draw.area_act;
lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
disp_refr->driver->screen_transp = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0;
}
return layer_ctx;
}
void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
lv_draw_layer_flags_t flags)
{
lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx;
lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) {
lv_memset_00(layer_ctx->buf, layer_sw_ctx->buf_size_bytes);
layer_sw_ctx->has_alpha = 1;
disp_refr->driver->screen_transp = 1;
}
else {
layer_sw_ctx->has_alpha = 0;
disp_refr->driver->screen_transp = 0;
}
draw_ctx->buf = layer_ctx->buf;
draw_ctx->buf_area = &layer_ctx->area_act;
draw_ctx->clip_area = &layer_ctx->area_act;
}
void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx,
const lv_draw_img_dsc_t * draw_dsc)
{
lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx;
lv_img_dsc_t img;
img.data = draw_ctx->buf;
img.header.always_zero = 0;
img.header.w = lv_area_get_width(draw_ctx->buf_area);
img.header.h = lv_area_get_height(draw_ctx->buf_area);
img.header.cf = layer_sw_ctx->has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
/*Restore the original draw_ctx*/
draw_ctx->buf = layer_ctx->original.buf;
draw_ctx->buf_area = layer_ctx->original.buf_area;
draw_ctx->clip_area = layer_ctx->original.clip_area;
lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing();
disp_refr->driver->screen_transp = layer_ctx->original.screen_transp;
/*Blend the layer*/
lv_draw_img(draw_ctx, draw_dsc, &layer_ctx->area_act, &img);
lv_draw_wait_for_finish(draw_ctx);
lv_img_cache_invalidate_src(&img);
}
void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx)
{
LV_UNUSED(draw_ctx);
lv_mem_free(layer_ctx->buf);
}
/**********************
* STATIC FUNCTIONS
**********************/

Some files were not shown because too many files have changed in this diff Show More