Qt Quick绘制小瓦片:选QQuickItem还是QQuickPaintedItem?
你的场景选QQuickPaintedItem完全够用,甚至是更优解!
嘿,先给你吃个定心丸:你的需求完全不需要上QQuickItem那套OpenGL渲染方案,QQuickPaintedItem反而更适合你,根本不存在“老旧缓慢”的问题——咱们拆解下为什么:
1. 性能顾虑是多余的
你提到QQuickPaintedItem“老旧且缓慢”,但这个结论是有前提的:它针对的是高频全量重绘的场景(比如每秒60帧刷新全屏复杂图形)。而你的场景:
- 每秒仅更新2个瓦片,频率只有3次/秒
- 每次更新的区域极小(单个瓦片的范围)
- 总渲染区域仅100×80像素
这种量级的计算对现代CPU来说简直是“小菜一碟”,哪怕用CPU绘制后复制到显卡,也完全不会有性能压力。
2. 实现复杂度直接降维打击
QQuickItem的OpenGL方案需要你手动处理顶点缓冲、着色器、纹理更新等底层OpenGL逻辑,对你这种仅需像素级绘制的场景来说,纯纯是过度设计——光写初始化管线的代码就要花不少时间。
而QQuickPaintedItem的实现逻辑简单到离谱:
- 用
QImage存储你的16K4色瓦片图(用索引格式Format_Indexed8,刚好匹配4色需求,内存占用极小) - 每次更新瓦片时,直接修改
QImage对应区域的像素 - 调用
update(QRectF)触发局部重绘,只刷新变化的瓦片区域,不用全量重绘整个图像 - 最后在
paint()里用QPainter把图像画到指定区域即可
3. 放大需求轻松满足
QML的scale属性对QQuickPaintedItem完全生效,你可以直接在QML里设置放大倍数,或者在paint()方法里用QPainter::scale()提前做绘制缩放,两种方式都没有额外性能负担。
给你个极简实现示例
C++ 子类实现
#include <QQuickPaintedItem> #include <QImage> class TileMapItem : public QQuickPaintedItem { Q_OBJECT public: explicit TileMapItem(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) { // 初始化16K 4色瓦片图(假设128×128像素=16384,刚好16K) m_tileImage = QImage(128, 128, QImage::Format_Indexed8); // 设置4色调色板 QVector<QRgb> palette(4); palette[0] = qRgb(0, 0, 0); // 黑色 palette[1] = qRgb(255, 0, 0); // 红色 palette[2] = qRgb(0, 255, 0); // 绿色 palette[3] = qRgb(0, 0, 255); // 蓝色 m_tileImage.setColorTable(palette); // 初始化瓦片数据(比如全黑) m_tileImage.fill(0); } void paint(QPainter *painter) override { // 把128×128的瓦片图绘制到100×80的区域(自动适配缩放) painter->drawImage(boundingRect(), m_tileImage, QRect(0, 0, 128, 128)); } // 暴露给QML的瓦片更新方法 Q_INVOKABLE void updateTile(int tileX, int tileY, int colorIndex) { // 假设每个瓦片是8×8像素,计算像素坐标范围 const int tileSize = 8; int startX = tileX * tileSize; int startY = tileY * tileSize; // 修改对应区域的像素 for (int y = 0; y < tileSize; ++y) { uchar* scanLine = m_tileImage.scanLine(startY + y); for (int x = 0; x < tileSize; ++x) { scanLine[startX + x] = colorIndex; } } // 仅更新这个瓦片的区域,避免全量重绘 update(QRectF(startX, startY, tileSize, tileSize)); } private: QImage m_tileImage; };
QML 调用示例
import QtQuick 2.15 import YourCustomModule 1.0 Rectangle { width: 400 height: 320 TileMapItem { id: tileMap width: 100 height: 80 scale: 4 // 放大4倍,刚好填满400×320的父容器 } // 模拟每秒3次更新2个瓦片 Timer { interval: 1000 / 3 running: true repeat: true onTriggered: { // 随机更新两个瓦片 tileMap.updateTile(Math.floor(Math.random()*16), Math.floor(Math.random()*16), Math.floor(Math.random()*4)); tileMap.updateTile(Math.floor(Math.random()*16), Math.floor(Math.random()*16), Math.floor(Math.random()*4)); } } }
总结
- 你的场景完全不需要QQuickItem的OpenGL管线,那是为复杂3D渲染、高频动态内容准备的,对你来说就是过度设计
- QQuickPaintedItem的局部更新机制完美匹配你的需求,代码量少、逻辑清晰,性能完全够用
- 如果以后真的遇到性能瓶颈(比如更新频率提升到30次/秒以上,或者瓦片数量暴增),再考虑迁移到QQuickItem+OpenGL也不迟,现在先选最省心的方案
内容的提问来源于stack exchange,提问作者June
相关产品推荐
相关产品推荐

