LVGL展示网络图片

背景

在用LVGL时,有一个需求是需要在界面上展示网络图片。本篇文章就详细介绍一下如何在UI上面展示一个给定的网络图片。网上好像没有太多相关的资料啊。

思路

一开始的思路是

  1. 利用curl获取图片,保存到本地
  2. lv_img_set_src将图片刷新到界面上

这样操作有一个问题就是文件保存以后再读取解码非常耗时,导致界面卡顿。于是想着进一步优化步骤,在不保存文件的情况下从内存中直接解码图片并展示出来。

进行了以下优化:

  1. 利用curl获取图片
  2. 在内存中直接解码
  3. 使用解码结果构造lv_img_dsc_t对象
  4. lv_img_set_src将图片刷新到界面上

免去文件存读以后速度快了很多,其中解码用stb这个库,支持很多格式的图片,应该够用了。

代码

首先要集成curlstb两个库。一般交叉编译工具链包含curl,如果不包含就要自己编译一下,stb则直接引入源文件。

实际应用中已经进行了组件化的封装,不便展示,这里贴一部分最初的原始代码参考。

注意:在正式项目中利用curl获取图片的操作应该放到子线程,避免阻塞UI线程

typedef struct
{
    char *src;
    lv_obj_t *obj;
    pthread_t *threadId;
} http_image_t;


static void load_net_image(void *arg);

static size_t net_image_callback(char *resp, size_t size, size_t nmemb, void *user_data)
{
    size_t dataLen = size * nmemb;
    http_response_t *response = (http_response_t *)user_data;

    response->data = realloc(response->data, response->size + dataLen + 1);

    if (response->data == NULL)
    {
        puts("内存分配失败!!!!!!!");
        return 0;
    }
    memcpy(&(response->data[response->size]), resp, dataLen);
    response->size += dataLen;

    return dataLen;
}

void lv_img_set_net_src(lv_obj_t *obj, const void *src)
{
    pthread_t threadId;
    http_image_t *http_image = malloc(sizeof(http_image_t));
    http_image->obj = obj;
    http_image->src = src;
    http_image->threadId = &threadId;
    pthread_create(&threadId, NULL, load_net_image, http_image);
}

static void load_net_image(void *arg)
{
    http_image_t *http_image = (http_image_t *)arg;
    printf("--------------threadId=%lu\n", http_image->threadId);
    curl_global_init(CURL_GLOBAL_ALL);
    CURL *curl_handler;
    http_response_t response = {.data = NULL, .size = 0};
    curl_handler = curl_easy_init();
    if (curl_handler != NULL)
    {
        curl_easy_setopt(curl_handler, CURLOPT_URL, http_image->src);
        // 设置请求方式
        curl_easy_setopt(curl_handler, CURLOPT_CUSTOMREQUEST, "GET");
        // 忽略SSL证书
        curl_easy_setopt(curl_handler, CURLOPT_SSL_VERIFYPEER, false);
        // 写入方法
        curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, &net_image_callback);
        // 写入数据
        curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, &response);
        // 默认https协议
        curl_easy_setopt(curl_handler, CURLOPT_DEFAULT_PROTOCOL, "https");
        // 请求超时时长(秒)
        curl_easy_setopt(curl_handler, CURLOPT_TIMEOUT, 3L);
        // 设置连接超时时长(秒)
        curl_easy_setopt(curl_handler, CURLOPT_CONNECTTIMEOUT, 10L);
        // 是否打开详细日志
        curl_easy_setopt(curl_handler, CURLOPT_VERBOSE, true);

        CURLcode res = curl_easy_perform(curl_handler);
        // 模拟耗时操作
        usleep(2 * 1000 * 1000);
        if (res == CURLE_OK)
        {
            int img_width, img_height, img_channels;
            u_int8_t *img_data = stbi_load_from_memory(response.data, response.size, &img_width, &img_height, &img_channels, STBI_rgb_alpha);
            if (img_data)
            {
            // My screen need bgra format,but the stb returned the rgba or rgb format
                for (int i = 0; i < img_width * img_height; ++i)
                {
                    unsigned char tmp = img_data[i * 4];
                    img_data[i * 4] = img_data[i * 4 + 2];
                    img_data[i * 4 + 2] = tmp;
                    if (img_channels == 3) // change the rgb to bgra
                    {
                        img_data[i * 4 + 3] = 0xFF;
                    }
                }
                lv_img_dsc_t *img_dsc = malloc(sizeof(lv_img_dsc_t));
                img_dsc->header.always_zero = 0;
                img_dsc->header.w = img_width;
                img_dsc->header.h = img_height;
                img_dsc->data_size = img_width * img_height * 4;
                img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
                img_dsc->data = (uint8_t *)malloc(img_dsc->data_size);
                memcpy(img_dsc->data, img_data, img_dsc->data_size);
                printf("The image info after decode:%d %d %d \n", img_width, img_height, img_channels);
                lv_img_set_src(http_image->obj, img_dsc);
                // free the image data returned from stb
                stbi_image_free(img_data);
            }
        }
        curl_easy_cleanup(curl_handler);
    }
    if (http_image != NULL)
    {
        free(http_image);
    }
    curl_global_cleanup();
    
}

使用时只需要调用 lv_img_set_net_src即可。以上代码只是参考,还有很多不足之处:比如内存的申请和释放可以使用lvgl已经封装好的lv_img_buf_alloclv_img_buf_free,等等还有很多。

你可能感兴趣的:(c语言,LVGL)