跳到主要内容
版本:Next 🚧

UI 图像缓存和异步加载

  • 开始日期:2023-04-05
  • 目标主要版本:3.x
  • 参考问题:无
  • 实现 PR:无

概括

新增 UIImage 模块来负责异步图像加载。

基本示例

#include <stdio.h>
#include <ui/image.h>

void on_image_load(ui_image_t *image, void *data)
{
pd_canvas_t *data = (pd_canvas_t *)image;

if (data) {
printf("image loaded, size: (%d, %d)\n", data->width, data->height);
} else {
printf("image loading failed!\n");
}
}

void example(void)
{
ui_image_t *image;

image = ui_image_create("/path/to/image.png");
ui_image_on_event(image, on_image_load, NULL);
}

动机

在 2.x 版本中,部件背景图加载功能的设计是:建立一个以图像路径为索引的缓存表,每个缓存项中记录了引用该图像的部件列表。在计算部件背景样式时,如果指定了图像路径,则会加载它并增加该图像的引用计数。在部件的背景图样式变更以及部件销毁时,会减少图像的引用计数。当图像引用计数为 0 时,释放图像资源。

这个设计中的图像缓存依赖于部件,而图像异步加载能力则依赖于 LCUI 的工作线程。整个功能仅供内部使用。实际上,这两个依赖项并不是必要的。其中对 LCUI 的工作线程的依赖可以通过将图像加载函数改为公共函数来解除,由上层代码决定放在哪个线程上执行。

按照 3.x 版本的架构设计思想,它可以被设计成较为独立通用的模块,以便于 LCUI 应用层代码也能使用异步图像加载功能。

详细设计

设计灵感来自:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLImageElement/Image

新增 ui_image_t 类型,基于 pd_canvas_t 扩展增加其它成员,在使用时可将 ui_image_t 指针转为 pd_canvas_t 指针来访问图像的信息。

新增事件绑定函数,用于监听图像的加载结果,回调函数的指针定义如下:

typedef void (*ui_image_event_handler_t)(ui_image_t *, void *);

如果图像加载失败,回调函数收到的第一个参数的值会为 NULL。

模块内部维护缓存表、待加载的图像列表、事件列表。

ui_image_create() 函数用于创建图像对象,当指定的图像路径已在缓存表中时则直接返回缓存中的图像,并给该缓存增加引用计数。否则,将图像对象追加到待加载的图像列表中。

ui_image_add_event_listener()ui_image_remove_event_listener() 用于图像事件的绑定与解绑,提供简写宏 ui_image_on_eventui_image_off_event

ui_load_images() 用于处理待加载的图像列表,该函数应该在 UI 线程外的其它线程中调用。

ui_process_image_events() 用于处理已加载的图像的事件,与图像绑定的事件处理函数都会在这时被调用。

缺点

图像加载失败时,事件处理函数接收的 ui_image_t 指针值为 NULL,无法得知图像信息,可考虑将错误信息写入 ui_image_t 结构体内。

备选方案

无。

采用策略

这是个新增的功能,需要将部件的背景图片加载逻辑改为基于新的 UIImage。