356 lines
11 KiB
C
356 lines
11 KiB
C
/**
|
|
* @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
|
|
**********************/
|
|
LV_ATTRIBUTE_FAST_MEM static lv_res_t 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;
|
|
if(draw_ctx->draw_img) {
|
|
res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
|
|
}
|
|
else {
|
|
res = decode_and_draw(draw_ctx, dsc, coords, src);
|
|
}
|
|
|
|
if(res == LV_RES_INV) {
|
|
LV_LOG_WARN("Image draw error");
|
|
show_error(draw_ctx, coords, "No\ndata");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 byte shows the type of the image source*/
|
|
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
|
|
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
|
|
}
|
|
else if(u8_p[0] >= 0x80) {
|
|
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
|
|
**********************/
|
|
|
|
LV_ATTRIBUTE_FAST_MEM static lv_res_t 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_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
|
|
else cf = LV_IMG_CF_TRUE_COLOR;
|
|
|
|
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
|
|
}
|