背景
在用LVGL
时,有一个需求是需要在界面上展示网络图片。本篇文章就详细介绍一下如何在UI
上面展示一个给定的网络图片。网上好像没有太多相关的资料啊。
思路
一开始的思路是
- 利用
curl
获取图片,保存到本地 lv_img_set_src
将图片刷新到界面上
这样操作有一个问题就是文件保存以后再读取解码非常耗时,导致界面卡顿。于是想着进一步优化步骤,在不保存文件的情况下从内存中直接解码图片并展示出来。
进行了以下优化:
- 利用
curl
获取图片 - 在内存中直接解码
- 使用解码结果构造
lv_img_dsc_t
对象 lv_img_set_src
将图片刷新到界面上
免去文件存读以后速度快了很多,其中解码用stb这个库,支持很多格式的图片,应该够用了。
代码
首先要集成
curl
和stb
两个库。一般交叉编译工具链包含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_alloc
和lv_img_buf_free
,等等还有很多。