You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 13:49:09