LVGL+linux无法显示png格式图片问题

项目场景:

lvgl在linux开发板下显示png图片不成功问题


问题描述

在linux环境下移植lvgl后测试显示png解码图片发现不会正常显示,其中"/app/out/BACK.png"为开发板中图片的存放路径

 char *path = "/app/out/BACK.png";
    lv_obj_t *backimg;
    lv_obj_t *lable;
    lv_png_init();
    backimg = lv_img_create(lv_scr_act());
    lv_img_set_src(backimg, path);
    lv_obj_align_to(backimg,NULL,LV_ALIGN_CENTER,0,0);

原因分析:

进入lv_img_set_src函数当中,发现会进入到函数lv_img_decoder_get_info当中

 lv_img_header_t header;
    lv_img_decoder_get_info(src, &header);

在这个函数中会去遍历查找之前lv_png_init函数初始化过的解码器,遍历到后则会调用info_cb对应的解码器函数,这里是之前初始化的decoder_info函数

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) {
            //printf("find info_cb\n");
            res = d->info_cb(d, src, header);
            if(res == LV_RES_OK) break;
        }
    }
void lv_png_init(void)
{
    lv_img_decoder_t * dec = lv_img_decoder_create();
    lv_img_decoder_set_info_cb(dec, decoder_info);
    lv_img_decoder_set_open_cb(dec, decoder_open);
    lv_img_decoder_set_close_cb(dec, decoder_close);
}

进入到decoder_info函数中,打印res结果发现res结果为LV_FS_RES_UNKNOWN

if(src_type == LV_IMG_SRC_FILE) {
        const char * fn = src;
        if(!strcmp(&fn[strlen(fn) - 3], "png")) {              /*Check the extension*/

            /* Read the width and height from the file. They have a constant location:
             * [16..23]: width
             * [24..27]: height
             */
            uint32_t size[2];
            lv_fs_file_t f;
            lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD);printf("fun:png decoder_info,path=%s, res=%d\n",fn,res);
            if(res != LV_FS_RES_OK) return LV_RES_INV;
            lv_fs_seek(&f, 16, LV_FS_SEEK_SET);
            uint32_t rn;
            lv_fs_read(&f, &size, 8, &rn);
            if(rn != 8) return LV_RES_INV;
            lv_fs_close(&f);
            /*Save the data in the header*/
            header->always_zero = 0;
            header->cf = LV_IMG_CF_RAW_ALPHA;
            /*The width and height are stored in Big endian format so convert them to little endian*/
            header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
            header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);

            return LV_RES_OK;
        }
    }

继续进入lv_fs_open函数中查看,这里首先会去获取路径字符串的第一个字符用以匹配,我的letter设置如下,所以匹配的字符应该是“/”

 char letter = path[0];
    lv_fs_drv_t * drv = lv_fs_get_drv(letter);
lv_fs_drv_t * lv_fs_get_drv(char letter)
{
    lv_fs_drv_t ** drv;
    //printf("letter:%c\n",letter);
    _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
        if((*drv)->letter == letter) {
            return *drv;
        }
    }

    return NULL;
}
#define LV_USE_FS_POSIX 1
#if LV_USE_FS_POSIX
    #define LV_FS_POSIX_LETTER '/'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
    #define LV_FS_POSIX_PATH ""         /*Set the working directory. File/directory paths will be appended to it.*/
    #define LV_FS_POSIX_CACHE_SIZE  0   /*>0 to cache this number of bytes in lv_fs_read()*/
#endif

匹配成功后会进入到open_cb函数中,这里对应的就是真正的open函数,注意这里get_real_path函数是个大坑

const char * real_path = lv_fs_get_real_path(path);
    //printf("real path:%s\n",real_path);
    void * file_d = drv->open_cb(drv, real_path, mode);

    if(file_d == NULL || file_d == (void *)(-1)) {
        return LV_FS_RES_UNKNOWN;
    }

这里的drv->open_cb事先被注册过了,因为我所使用的文件系统是fs_posix,所以对应fs_posix的的open函数,具体的函数在lv_fs_posix.c文件当中。关于这个函数是什么时候注册的这里说明一下
首先前面提到的LV_USE_FS_POSIX宏打开后在lv_extra.c文件中会进行fs_posix的初始化工作,并在初始化中注册到lv_fs这一层

#if LV_USE_FS_POSIX != '\0'
    lv_fs_posix_init();
#endif
void lv_fs_posix_init(void)
{
    /*---------------------------------------------------
     * Register the file system interface in LVGL
     *--------------------------------------------------*/

    /*Add a simple drive to open images*/
    static lv_fs_drv_t fs_drv; /*A driver descriptor*/
    lv_fs_drv_init(&fs_drv);

    /*Set up fields...*/
    fs_drv.letter = LV_FS_POSIX_LETTER;
    fs_drv.cache_size = LV_FS_POSIX_CACHE_SIZE;

    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.write_cb = fs_write;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;

    fs_drv.dir_close_cb = fs_dir_close;
    fs_drv.dir_open_cb = fs_dir_open;
    fs_drv.dir_read_cb = fs_dir_read;

    lv_fs_drv_register(&fs_drv);
}

所以drv->open_cb函数对应这里面的fs_open函数。
进去后发现最后调用的确实是open函数

static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
    LV_UNUSED(drv);
    //printf("fs_open,path=%s,mode=%d\n",path,mode);
    uint32_t flags = 0;
    if(mode == LV_FS_MODE_WR) flags = O_WRONLY;
    else if(mode == LV_FS_MODE_RD) flags = O_RDONLY;
    else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR;

    /*Make the path relative to the current directory (the projects root folder)*/
    char buf[256];
    sprintf(buf, LV_FS_POSIX_PATH "%s", path);

    int f = open(buf, flags);//printf("open result:%d\n",f);
    if(f < 0) return NULL;

    return (void *)(lv_uintptr_t)f;
}

打印open函数的结果发现为负值(打开失败),然后检查文件路径是否正常,一检查果然发现问题了,在open函数中打印路径发现为“app/out/BACK.png”,明显和原来相比少了第一个字符,那到底是哪里出了问题导致少了字符呢?
继续往回翻找发现前面lv_fs_open函数中有一个get_real_path函数,这个函数是干嘛的?往里面一看发现不对劲了

const char * real_path = lv_fs_get_real_path(path);
    //printf("real path:%s\n",real_path);
    void * file_d = drv->open_cb(drv, real_path, mode);

    if(file_d == NULL || file_d == (void *)(-1)) {
        return LV_FS_RES_UNKNOWN;
    }
static const char * lv_fs_get_real_path(const char * path)
{
    //path++; /*Ignore the driver letter*/
    if(*path == ':') path++;

    return path;
}

在这个函数中会执行path++,这是在单片机文件系统或win文件系统中的措施,去掉前面的盘符字符,但linux这里是不需要的,所以导致将前面的“/”字符给去掉了。将这句注释后图片显示正常
在这里插入图片描述


解决方案:

将lv_fs_open函数中lv_fs_get_real_path函数中path++注释掉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值