文章目录
- [Qt Quick 图片加载与 GPU 纹理机制详解:QImage、QQuickTextureFactory、QSGTexture 与异步加载](#Qt Quick 图片加载与 GPU 纹理机制详解:QImage、QQuickTextureFactory、QSGTexture 与异步加载)
-
- 一、前言
- [二、先理解 Qt Quick 的图片渲染机制](#二、先理解 Qt Quick 的图片渲染机制)
-
- [2.1 Qt Widgets 和 Qt Quick 的区别](#2.1 Qt Widgets 和 Qt Quick 的区别)
- [2.2 Qt Quick 图片渲染流程](#2.2 Qt Quick 图片渲染流程)
- [三、QImage:Qt 图片数据处理类](#三、QImage:Qt 图片数据处理类)
-
- [3.1 QImage 是什么?](#3.1 QImage 是什么?)
- [3.2 QImage 的内存特点](#3.2 QImage 的内存特点)
- [3.3 QImage 和 GPU 的关系](#3.3 QImage 和 GPU 的关系)
- [四、QQuickTextureFactory:Qt Quick 纹理工厂](#四、QQuickTextureFactory:Qt Quick 纹理工厂)
-
- [4.1 QQuickTextureFactory 是什么?](#4.1 QQuickTextureFactory 是什么?)
- [4.2 为什么需要 QQuickTextureFactory?](#4.2 为什么需要 QQuickTextureFactory?)
- [4.3 QQuickTextureFactory 常用函数](#4.3 QQuickTextureFactory 常用函数)
-
- [① textureFactoryForImage()](#① textureFactoryForImage())
- [② createTexture()](#② createTexture())
- [③ textureSize()](#③ textureSize())
- [④ textureByteCount()](#④ textureByteCount())
- [⑤ image()](#⑤ image())
- [五、QSGTexture:Qt Quick 场景图纹理](#五、QSGTexture:Qt Quick 场景图纹理)
-
- [5.1 QSGTexture 是什么?](#5.1 QSGTexture 是什么?)
- [5.2 QSGTexture 的作用](#5.2 QSGTexture 的作用)
- [5.3 QSGTexture 与 QSGSimpleTextureNode](#5.3 QSGTexture 与 QSGSimpleTextureNode)
- 六、QQuickImageProvider:自定义图片提供器
-
- [6.1 QQuickImageProvider 是什么?](#6.1 QQuickImageProvider 是什么?)
- [6.2 自定义图片提供器](#6.2 自定义图片提供器)
- [6.3 QQuickImageProvider 的局限性](#6.3 QQuickImageProvider 的局限性)
- 七、QQuickAsyncImageProvider:异步图片加载
-
- [7.1 什么是异步图片提供器?](#7.1 什么是异步图片提供器?)
- [7.2 异步加载流程](#7.2 异步加载流程)
- [7.3 QQuickImageResponse 是什么?](#7.3 QQuickImageResponse 是什么?)
- [八、QQuickAsyncImageProvider 完整代码示例](#八、QQuickAsyncImageProvider 完整代码示例)
-
- [8.1 定义异步图片响应类](#8.1 定义异步图片响应类)
- [8.2 定义异步图片提供器](#8.2 定义异步图片提供器)
- [8.3 注册 Provider](#8.3 注册 Provider)
- [8.4 QML 使用](#8.4 QML 使用)
- 九、重点分析:QQuickTextureFactory::textureFactoryForImage()
- 十、图片异步加载与缩略图预加载优化
-
- [10.1 为什么需要缩略图预加载?](#10.1 为什么需要缩略图预加载?)
- [10.2 推荐的图片加载架构](#10.2 推荐的图片加载架构)
- [10.3 缓存策略](#10.3 缓存策略)
- [10.4 控制并发加载数量](#10.4 控制并发加载数量)
- [10.5 优先级管理](#10.5 优先级管理)
- 十一、常见问题及注意事项
-
- [11.1 QImage 和 QSGTexture 可以直接互相替代吗?](#11.1 QImage 和 QSGTexture 可以直接互相替代吗?)
- [11.2 QQuickTextureFactory 是不是 GPU 纹理?](#11.2 QQuickTextureFactory 是不是 GPU 纹理?)
- [11.3 可以在工作线程直接创建 QSGTexture 吗?](#11.3 可以在工作线程直接创建 QSGTexture 吗?)
- [11.4 QQuickImageProvider 和 QQuickAsyncImageProvider 有什么区别?](#11.4 QQuickImageProvider 和 QQuickAsyncImageProvider 有什么区别?)
- [11.5 textureFactoryForImage() 会立即创建 GPU 纹理吗?](#11.5 textureFactoryForImage() 会立即创建 GPU 纹理吗?)
- [11.6 图片加载完成后为什么还需要 finished()?](#11.6 图片加载完成后为什么还需要 finished()?)
- 十二、总结
- 十三、官方参考资料
Qt Quick 图片加载与 GPU 纹理机制详解:QImage、QQuickTextureFactory、QSGTexture 与异步加载
一、前言
在使用 Qt Quick(QML)开发图片浏览器、图像处理软件、工业视觉软件时,经常需要加载大量图片,并将图片显示在 QML 界面中。
例如:
- 图片浏览器需要批量加载缩略图。
- 工业视觉软件需要显示相机采集的图像。
- PCB 检测软件需要显示大量检测图片。
- 图像分析软件需要实现图片预览、缩放、平移。
- 大型图片管理软件需要实现异步加载和预加载。
如果只是使用 Qt Widgets,通常可以通过 QPixmap、QLabel 等组件完成图片显示。
但是 Qt Quick 使用的是另一套渲染体系。
Qt Quick 通过 Scene Graph(场景图)组织界面元素,最终通过图形渲染后端将内容绘制到屏幕上。
因此,图片从磁盘读取到最终显示在 QML 界面上,中间涉及多个重要类:
| 类 | 主要作用 |
|---|---|
| QImage | 图片数据读取和处理 |
| QQuickImageProvider | 向 QML 提供自定义图片 |
| QQuickAsyncImageProvider | 提供异步图片加载能力 |
| QQuickImageResponse | 管理异步图片请求结果 |
| QQuickTextureFactory | 创建 Qt Quick 可使用的纹理 |
| QSGTexture | 场景图使用的纹理资源 |
| QSGNode | 场景图节点,组织渲染内容 |
本文重点介绍这些类的职责、工作流程以及实际使用方法。
二、先理解 Qt Quick 的图片渲染机制
2.1 Qt Widgets 和 Qt Quick 的区别
Qt Widgets 和 Qt Quick 都可以显示图片,但底层渲染机制存在区别。
Qt Widgets 图片显示:
cpp
QPixmap pixmap("test.jpg");
ui->label->setPixmap(pixmap);
这里主要通过 QPixmap 和 Widgets 绘制系统完成图片显示。
Qt Quick 图片显示:
qml
Image {
source: "file:///D:/images/test.jpg"
width: 400
height: 300
fillMode: Image.PreserveAspectFit
}
Qt Quick 会将图片加载和界面渲染分开处理。
大致过程:
- 读取图片文件。
- 将图片解码为图像数据。
- 准备纹理数据。
- 在场景图渲染阶段创建纹理资源。
- 通过 GPU 完成图片绘制。
这里需要理解一个关键问题:
负责读取图片的线程,不一定具备创建 GPU 纹理所需要的图形上下文。
如果在图片加载线程中直接创建图形纹理,就可能出现线程和图形上下文不匹配的问题。
Qt Quick 使用 QQuickTextureFactory 来解决图片加载阶段与纹理创建阶段之间的衔接问题。
2.2 Qt Quick 图片渲染流程
text
图片文件
|
v
QImage 图片解码
|
v
准备图片像素数据
|
v
QQuickTextureFactory
|
v
Qt Quick 场景图渲染线程
|
v
createTexture()
|
v
QSGTexture
|
v
GPU 图形渲染
|
v
QML 界面显示
这里有三个非常重要的概念:
- QImage:保存图片数据。
- QQuickTextureFactory:提供创建纹理的能力。
- QSGTexture:供 Qt Quick 场景图使用的纹理对象。
这三个类不是同一个东西,也不能相互替代。
三、QImage:Qt 图片数据处理类
3.1 QImage 是什么?
QImage 是 Qt 提供的图片数据处理类,主要用于图片的读取、存储和像素操作。
头文件:
cpp
#include <QImage>
常见功能:
- 加载 PNG、JPG、BMP 等图片。
- 获取图片宽度和高度。
- 获取图片像素数据。
- 修改图片像素。
- 图片缩放和裁剪。
- 图片格式转换。
- 图片保存。
例如:
cpp
QImage image("D:/images/test.jpg");
if (image.isNull()) {
qDebug() << "图片加载失败";
return;
}
qDebug() << "宽度:" << image.width();
qDebug() << "高度:" << image.height();
qDebug() << "格式:" << image.format();
3.2 QImage 的内存特点
QImage 主要管理 CPU 侧的图片数据。
例如一张 1920×1080 的 RGBA 图片:
text
宽度:1920
高度:1080
每个像素:4 字节
理论像素数据大小:
text
1920 × 1080 × 4
= 8,294,400 字节
≈ 7.91 MiB
注意:这只是未考虑行对齐、额外元数据和内部存储开销时的理论像素数据大小。
QImage 采用隐式共享机制(Implicit Sharing),多个 QImage 对象可以共享同一份底层数据,发生修改时才可能触发数据分离。
这有利于降低图片复制的内存开销。
3.3 QImage 和 GPU 的关系
QImage 本身不是 GPU 纹理。
它保存的是图片像素数据,通常位于 CPU 可访问的内存中。
如果要在 Qt Quick 中显示 QImage,需要将图片数据交给 Qt Quick 的纹理创建流程。
这就引出了下一个类:QQuickTextureFactory。
四、QQuickTextureFactory:Qt Quick 纹理工厂
4.1 QQuickTextureFactory 是什么?
QQuickTextureFactory 是 Qt Quick 模块提供的一个抽象类。
主要作用:
为 QML 图片加载提供纹理创建接口,将图片数据与 Qt Quick 的场景图渲染过程连接起来。
头文件:
cpp
#include <QQuickTextureFactory>
CMake:
cmake
find_package(Qt6 REQUIRED COMPONENTS Quick)
target_link_libraries(MyApp PRIVATE Qt6::Quick)
qmake:
pro
QT += quick
这个类继承自 QObject。
它本身是一个抽象类,不能直接实例化,因为其中包含纯虚函数。
4.2 为什么需要 QQuickTextureFactory?
假设图片加载过程如下:
cpp
QImage image("test.jpg");
此时图片已经加载到内存。
如果立即创建 GPU 纹理,就需要合适的图形渲染上下文。
但是图片解码通常可以在普通工作线程中进行,而 GPU 纹理创建需要遵循 Qt Quick 渲染后端的线程和资源管理规则。
因此,Qt 将这两个过程分开:
图片加载阶段:
text
读取图片
|
v
解码为 QImage
|
v
创建纹理工厂
渲染阶段:
text
Qt Quick 渲染线程
|
v
调用 createTexture()
|
v
创建 QSGTexture
|
v
GPU 渲染
QQuickTextureFactory 就是两个阶段之间的桥梁。
需要注意,Qt 官方文档中的部分描述使用 OpenGL 纹理作为历史背景。现代 Qt 6 的 Qt Quick 还支持基于 QRhi 的不同图形后端,例如 Vulkan、Metal、Direct3D 和 OpenGL。实际项目中不应将 QSGTexture 简单等同于某一种图形 API 的纹理对象。
4.3 QQuickTextureFactory 常用函数
Qt 6 中主要有以下函数:
| 函数 | 类型 | 作用 |
|---|---|---|
| textureFactoryForImage() | 静态函数 | 根据 QImage 创建纹理工厂 |
| createTexture() | 纯虚函数 | 创建 QSGTexture |
| textureSize() | 纯虚函数 | 获取纹理尺寸 |
| textureByteCount() | 纯虚函数 | 获取纹理占用的字节数 |
| image() | 虚函数 | 返回纹理对应的 QImage |
下面分别介绍。
① textureFactoryForImage()
函数:
cpp
static QQuickTextureFactory *
textureFactoryForImage(const QImage &image);
作用:
将 QImage 包装成 QQuickTextureFactory 对象。
示例:
cpp
QImage image("test.jpg");
QQuickTextureFactory *factory =
QQuickTextureFactory::textureFactoryForImage(image);
如果图片无效,函数会返回 nullptr。
例如:
cpp
QImage image;
QQuickTextureFactory *factory =
QQuickTextureFactory::textureFactoryForImage(image);
if (!factory) {
qDebug() << "纹理工厂创建失败";
}
这里需要注意:
textureFactoryForImage() 并不是直接返回 QSGTexture,而是返回一个纹理工厂。
真正创建 QSGTexture 的操作发生在后续渲染阶段。
② createTexture()
函数:
cpp
virtual QSGTexture *createTexture(
QQuickWindow *window
) const = 0;
这是 QQuickTextureFactory 最核心的函数之一。
作用:
根据纹理工厂中的图片数据创建 QSGTexture 对象。
参数:
window:当前 Qt Quick 窗口,提供对应的图形渲染环境。
返回值:
QSGTexture*:Qt Quick 场景图使用的纹理对象。
这个函数由场景图渲染线程调用。
注意:
- 不要在普通图片加载线程中随意调用。
- 不要在没有对应渲染环境的情况下直接创建底层图形资源。
- 每次调用都应该返回一个新的、独立的纹理实例。
- 纹理资源的释放也需要遵循 Qt Quick 的渲染资源生命周期规则。
③ textureSize()
函数:
cpp
virtual QSize textureSize() const = 0;
作用:
获取纹理尺寸。
示例:
cpp
QSize size = factory->textureSize();
qDebug() << "纹理宽度:" << size.width();
qDebug() << "纹理高度:" << size.height();
例如图片尺寸是 1920×1080:
text
width = 1920
height = 1080
该函数可能在任意线程被调用,因此不能依赖已经绑定的 OpenGL 上下文。
④ textureByteCount()
函数:
cpp
virtual int textureByteCount() const = 0;
作用:
返回纹理占用的内存字节数。
例如:
cpp
int bytes = factory->textureByteCount();
qDebug() << "纹理占用字节数:" << bytes;
它可以帮助 Qt Quick 了解纹理的内存开销。
注意:实际纹理内存大小可能受到像素格式、GPU 对齐、内部存储方式等因素影响,不能简单地认为一定等于 width × height × 4。
⑤ image()
函数:
cpp
virtual QImage image() const;
作用:
返回这个纹理对应的 QImage。
不过,这个函数并不常用,而且官方文档明确指出它可能比较慢。
如果需要返回图片,应该确保返回的是自包含的 QImage,避免依赖生命周期不确定的外部像素内存。
五、QSGTexture:Qt Quick 场景图纹理
5.1 QSGTexture 是什么?
QSGTexture 是 Qt Quick Scene Graph 中的纹理抽象类。
它表示可以被场景图渲染节点使用的纹理资源。
头文件:
cpp
#include <QSGTexture>
它与 QQuickTextureFactory 的区别:
- QQuickTextureFactory:负责提供创建纹理的能力。
- QSGTexture:表示创建完成后供场景图使用的纹理资源。
可以这样理解:
text
QImage
|
v
QQuickTextureFactory
|
v
QSGTexture
|
v
Scene Graph
|
v
GPU 渲染
5.2 QSGTexture 的作用
主要包括:
- 保存和管理纹理资源。
- 提供纹理尺寸等信息。
- 供场景图节点引用。
- 支持纹理坐标和纹理采样。
- 配合图形渲染后端完成图片绘制。
需要注意,QSGTexture 是 Qt Quick 的纹理抽象,不建议在普通业务逻辑中直接操作其底层图形资源。
5.3 QSGTexture 与 QSGSimpleTextureNode
如果需要自定义场景图节点显示图片,可以使用 QSGSimpleTextureNode。
例如:
cpp
QSGSimpleTextureNode *node =
new QSGSimpleTextureNode();
node->setTexture(texture);
node->setRect(
QRectF(0, 0, 400, 300)
);
这里:
texture是 QSGTexture 对象。setTexture()设置节点使用的纹理。setRect()设置纹理显示区域。
最终 Qt Quick 会将这个节点纳入场景图渲染流程。
注意:QSGNode 及其子类通常需要在场景图渲染线程中创建和操作,不能随意跨线程访问。
六、QQuickImageProvider:自定义图片提供器
6.1 QQuickImageProvider 是什么?
QQuickImageProvider 是 Qt Quick 提供的自定义图片提供接口。
当 QML 需要加载图片时,可以通过自定义 Provider 从数据库、网络、内存缓存或者其他数据源获取图片。
头文件:
cpp
#include <QQuickImageProvider>
常见使用场景:
- 从数据库加载图片。
- 从内存缓存加载缩略图。
- 从网络服务器获取图片。
- 根据图片 ID 动态生成图片。
- 加载工业检测系统中的图像数据。
6.2 自定义图片提供器
例如,我们希望通过图片 ID 加载图片。
首先定义 Provider:
cpp
class MyImageProvider : public QQuickImageProvider
{
public:
MyImageProvider()
: QQuickImageProvider(QQuickImageProvider::Image)
{
}
QImage requestImage(
const QString &id,
QSize *size,
const QSize &requestedSize
) override
{
Q_UNUSED(size);
Q_UNUSED(requestedSize);
QImage image(
QString("D:/images/%1.jpg").arg(id)
);
return image;
}
};
然后注册:
cpp
engine.addImageProvider(
"myimage",
new MyImageProvider()
);
QML 中使用:
qml
Image {
source: "image://myimage/10001"
}
当 QML 加载图片时,会调用 Provider 的 requestImage()。
这里的 10001 就是传入的图片 ID。
6.3 QQuickImageProvider 的局限性
普通 QQuickImageProvider 可以实现自定义图片加载,但同步请求可能导致调用方等待图片加载完成。
如果图片较大,或者需要同时加载大量图片,就可能影响界面响应。
例如:
text
用户打开图片列表
|
v
同时加载 100 张图片
|
v
大量图片解码
|
v
界面响应受到影响
这种情况下,可以考虑异步图片提供器。
七、QQuickAsyncImageProvider:异步图片加载
7.1 什么是异步图片提供器?
QQuickAsyncImageProvider 继承自 QQuickImageProvider,提供异步图片加载接口。
主要作用:
将耗时的图片读取、解码等工作放到后台执行,减少对界面线程的影响。
头文件:
cpp
#include <QQuickAsyncImageProvider>
它的核心接口是:
cpp
virtual QQuickImageResponse *requestImageResponse(
const QString &id,
const QSize &requestedSize
) = 0;
当 QML 发起图片请求时,Qt Quick 会调用这个接口。
返回一个 QQuickImageResponse 对象,用来管理异步加载过程。
7.2 异步加载流程
text
QML Image
|
v
QQuickAsyncImageProvider
|
v
requestImageResponse()
|
v
QQuickImageResponse
|
v
后台加载图片
|
v
QImage
|
v
发出 finished() 信号
|
v
Qt Quick 获取纹理工厂
|
v
QQuickTextureFactory
|
v
QSGTexture
|
v
GPU
这里有两个重要的类:
- QQuickAsyncImageProvider:负责创建异步图片请求。
- QQuickImageResponse:负责保存请求结果,并通知 Qt Quick 图片加载已经完成。
7.3 QQuickImageResponse 是什么?
它是异步图片请求的响应类。
主要接口:
cpp
virtual QQuickTextureFactory *
textureFactory() const = 0;
返回图片对应的纹理工厂。
另外还有:
cpp
void finished();
表示图片加载任务已经完成。
以及:
cpp
virtual void cancel();
用于通知图片请求不再需要。
注意:即使请求被取消,响应对象仍然需要在任务真正结束后发出 finished(),以便 Qt Quick 正确清理响应对象。不能在后台任务还在运行时提前发出完成信号。
八、QQuickAsyncImageProvider 完整代码示例
下面通过一个简单的示例,演示如何实现异步图片加载。
8.1 定义异步图片响应类
cpp
#include <QQuickImageResponse>
#include <QQuickTextureFactory>
#include <QImage>
#include <QThreadPool>
#include <QRunnable>
#include <QMutex>
#include <QMutexLocker>
#include <QAtomicInteger>
class MyImageResponse : public QQuickImageResponse
{
public:
explicit MyImageResponse(const QString &path)
: m_path(path)
{
QThreadPool::globalInstance()->start(
QRunnable::create([this]() {
QImage image(m_path);
{
QMutexLocker locker(&m_mutex);
m_image = std::move(image);
}
emit finished();
})
);
}
QQuickTextureFactory *textureFactory() const override
{
QMutexLocker locker(&m_mutex);
if (m_image.isNull())
return nullptr;
return QQuickTextureFactory::
textureFactoryForImage(m_image);
}
private:
QString m_path;
mutable QMutex m_mutex;
QImage m_image;
};
代码核心:
- 创建响应对象。
- 在线程池中执行图片读取。
- 将读取结果保存到
m_image。 - 发出
finished()信号。 - Qt Quick 在需要时调用
textureFactory()。 - 通过 QQuickTextureFactory 返回纹理工厂。
说明:这是用于理解调用关系的简化示例。生产项目还需要处理响应对象与后台任务之间的生命周期、取消请求、错误信息以及任务完成后的安全清理。不能简单地让后台任务持有裸 this 指针,并假设响应对象在任务结束前一定存在。
8.2 定义异步图片提供器
cpp
class MyAsyncImageProvider
: public QQuickAsyncImageProvider
{
public:
QQuickImageResponse *requestImageResponse(
const QString &id,
const QSize &requestedSize
) override
{
Q_UNUSED(requestedSize);
QString path =
QString("D:/images/%1.jpg").arg(id);
return new MyImageResponse(path);
}
};
这里根据图片 ID 生成图片路径,然后创建异步响应对象。
8.3 注册 Provider
cpp
engine.addImageProvider(
"asyncimage",
new MyAsyncImageProvider()
);
8.4 QML 使用
qml
Image {
source: "image://asyncimage/10001"
width: 400
height: 300
fillMode: Image.PreserveAspectFit
asynchronous: true
}
这里的图片请求由自定义 Provider 处理。
注意:QQuickAsyncImageProvider 本身已经提供异步响应机制,Image.asynchronous 不能替代 Provider 内部正确的线程和任务管理。
九、重点分析:QQuickTextureFactory::textureFactoryForImage()
现在回到最初遇到的代码:
cpp
return QQuickTextureFactory::textureFactoryForImage(m_image);
这行代码经常出现在:
cpp
QQuickTextureFactory *
MyImageResponse::textureFactory() const
{
return QQuickTextureFactory::
textureFactoryForImage(m_image);
}
它的作用就是:
将已经加载完成的 QImage 包装成 Qt Quick 能够使用的纹理工厂。
从源码实现的角度看,Qt 会尝试创建适合当前场景图后端的纹理工厂;如果没有可用的特定实现,则使用默认实现。
另外,如果传入的 QImage 为空,函数会返回 nullptr。
因此,这行代码并不是直接将 QImage 上传到 GPU,而是提供后续纹理创建所需要的对象。
可以将它理解为:
text
QImage
|
| 图片像素数据
v
textureFactoryForImage()
|
| 创建纹理工厂
v
QQuickTextureFactory
|
| Qt Quick 在渲染阶段调用
v
createTexture()
|
v
QSGTexture
|
v
GPU 渲染
这也是 Qt Quick 能够将图片加载与 GPU 纹理创建分离的重要机制。
十、图片异步加载与缩略图预加载优化
如果开发的是图片浏览器或者工业视觉软件,仅仅实现异步加载还不够。
因为当图片数量达到几万甚至几十万张时,还需要考虑缓存、预加载和内存管理。
10.1 为什么需要缩略图预加载?
假设一个图片列表有 10000 张图片。
如果每次用户滚动到某个位置时才开始读取图片:
text
用户滚动
|
v
读取图片
|
v
解码图片
|
v
创建纹理
|
v
显示缩略图
就可能出现图片显示延迟、滚动卡顿等问题。
因此,可以提前加载当前页面后面的图片。
例如:
cpp
m_thumbnailPrefetchPages =
qBound(
0,
config.value("thumbnail_prefetch_pages").toInt(5),
20
);
这行代码表示:
- 默认预加载 5 页。
- 最少预加载 0 页。
- 最多预加载 20 页。
10.2 推荐的图片加载架构
可以将整个图片系统划分为几个模块:
text
QML 图片列表
|
v
图片加载请求管理器
|
+-------+-------+
| |
v v
缩略图缓存 预加载管理器
| |
+-------+-------+
|
v
任务调度器
|
v
线程池解码
|
v
QImage
|
v
QQuickTextureFactory
|
v
QSGTexture
|
v
GPU
各模块的职责:
| 模块 | 职责 |
|---|---|
| 图片列表 | 展示图片和接收用户操作 |
| 请求管理器 | 管理图片加载请求 |
| 缩略图缓存 | 避免重复读取和解码 |
| 预加载管理器 | 提前加载后续图片 |
| 任务调度器 | 控制并发数量和优先级 |
| 线程池 | 执行图片读取和解码 |
| QImage | 保存解码后的图片数据 |
| QQuickTextureFactory | 提供纹理创建接口 |
| QSGTexture | 为场景图提供纹理资源 |
10.3 缓存策略
建议至少设计两级缓存:
第一级:CPU 图片缓存
保存 QImage 数据。
优点:
- 不需要重复读取磁盘。
- 不需要重复解码图片。
- 可以快速生成纹理工厂。
缺点:
- 大量图片会占用较多内存。
第二级:GPU 纹理缓存
由 Qt Quick 场景图及其内部机制管理纹理资源。
优点:
- 避免不必要的重复纹理创建。
- 提高图片重复显示时的效率。
缺点:
- 受 GPU 显存限制。
- 纹理资源需要遵循场景图生命周期。
- 场景图重建、窗口变化等情况可能影响纹理资源。
需要注意:Qt Quick 自身已经有图片和纹理缓存机制,业务层不应在不了解其行为的情况下重复建立大量 GPU 纹理缓存。
10.4 控制并发加载数量
如果同时加载大量图片,可能导致:
- 磁盘 IO 过高。
- CPU 解码占用过高。
- 内存快速增长。
- 图片请求排队。
- GPU 纹理上传压力增大。
建议使用任务队列控制并发。
例如:
cpp
QThreadPool *pool =
QThreadPool::globalInstance();
pool->setMaxThreadCount(4);
这里将线程池最大线程数设置为 4。
实际项目中,应根据 CPU 核数、图片大小、磁盘性能以及其他任务负载进行调整。
10.5 优先级管理
对于图片浏览器,可以设置三种任务优先级:
| 优先级 | 任务 | 说明 |
|---|---|---|
| 高 | 当前可见区域图片 | 优先保证用户看到的图片 |
| 中 | 即将进入可见区域的图片 | 提前准备 |
| 低 | 后续页面预加载 | 空闲时执行 |
这样可以避免后台预加载任务占用所有资源,导致当前界面图片无法及时显示。
十一、常见问题及注意事项
11.1 QImage 和 QSGTexture 可以直接互相替代吗?
不能。
QImage 主要负责图片像素数据处理。
QSGTexture 是 Qt Quick 场景图使用的纹理资源。
两者处于不同的处理阶段。
11.2 QQuickTextureFactory 是不是 GPU 纹理?
不是。
它是纹理工厂,负责提供创建纹理的能力。
实际创建出来的对象是 QSGTexture。
11.3 可以在工作线程直接创建 QSGTexture 吗?
通常不应该。
QSGTexture 属于场景图渲染资源,其创建和使用需要遵循 Qt Quick 的渲染线程及图形资源规则。
应当让 Qt Quick 在合适的渲染阶段创建纹理。
11.4 QQuickImageProvider 和 QQuickAsyncImageProvider 有什么区别?
| 对比项 | QQuickImageProvider | QQuickAsyncImageProvider |
|---|---|---|
| 加载方式 | 通过 requestImage 等接口提供图片 | 通过异步响应提供图片 |
| 主要接口 | requestImage() | requestImageResponse() |
| 适用场景 | 简单自定义图片加载 | 大量图片或耗时图片加载 |
| 响应对象 | 直接返回图片数据 | QQuickImageResponse |
| 纹理工厂 | 可由 Qt Quick 处理图片转换 | 通过 textureFactory() 返回 |
11.5 textureFactoryForImage() 会立即创建 GPU 纹理吗?
不能这样理解。
它创建的是纹理工厂对象。
真正创建 QSGTexture 的工作由后续场景图渲染阶段完成。
11.6 图片加载完成后为什么还需要 finished()?
因为 Qt Quick 需要知道异步请求什么时候结束。
finished() 用于通知引擎响应任务已经完成。
随后引擎才可以根据需要获取纹理工厂并继续处理图片显示。
十二、总结
本文介绍了 Qt Quick 图片加载和 GPU 纹理创建过程中涉及的核心类。
可以用下面这张表进行总结:
| 类 | 核心职责 | 关键词 |
|---|---|---|
| QImage | 保存和处理图片 | CPU 图片数据 |
| QQuickImageProvider | 自定义图片来源 | 图片提供 |
| QQuickAsyncImageProvider | 异步图片请求 | 后台加载 |
| QQuickImageResponse | 管理异步加载结果 | 请求响应 |
| QQuickTextureFactory | 创建纹理的工厂 | 纹理创建 |
| QSGTexture | 场景图纹理资源 | GPU 渲染 |
| QSGNode | 组织场景图内容 | 渲染节点 |
最重要的是理解下面这条链路:
text
图片读取
|
v
QImage
|
v
QQuickTextureFactory
|
v
QSGTexture
|
v
Qt Quick Scene Graph
|
v
GPU
|
v
QML 图片显示
如果开发的是图片浏览器、工业视觉软件或者大规模图片管理系统,建议重点掌握:
- QImage 的内存管理和隐式共享机制。
- QQuickAsyncImageProvider 的异步加载机制。
- QQuickImageResponse 的任务生命周期管理。
- QQuickTextureFactory 的纹理创建机制。
- QSGTexture 的渲染资源管理。
- 缩略图缓存、预加载、并发控制和优先级调度。
理解这些内容之后,就能够更好地分析 Qt Quick 图片显示延迟、缩略图加载卡顿、CPU 占用过高以及 GPU 纹理资源管理等问题。
十三、官方参考资料
- QQuickTextureFactory 官方文档
- QQuickImageResponse 官方文档
- QQuickAsyncImageProvider 官方文档
- QQuickImageProvider 官方文档
- QSGTexture 官方文档
- Qt Quick Scene Graph 官方文档
关键词: Qt、Qt Quick、QML、QImage、QQuickTextureFactory、QQuickImageProvider、QQuickAsyncImageProvider、QQuickImageResponse、QSGTexture、GPU 渲染、异步图片加载、缩略图预加载。