IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 关于QGraphicsScene绘制大图片时产生空白的问题 -> 正文阅读

[C++知识库]关于QGraphicsScene绘制大图片时产生空白的问题

问题

??很早之前,在使用QGraphicsScene绘制一张横向大约7万个像素的图片时,图片绘制出来出了问题。绘制出来的结果是图片只显示了大约一半的内容,另外一半则全是空白的。
??发生这么严重的问题,我当时第一反应谷歌了下,老外非常早就解决了我的疑惑:因为QPainter内部做了限制,最大尺寸是32767。当时因为急着处理项目,只是记录了下,未再深究。
??奈何最近家里疫情,心里闷得慌,正好翻到了以前的笔记,花了点时间找到了原因。下面记录了我整个查找的流程,觉得啰嗦的可以倒序看😭。

QPainter

??由于QGraphicsScene的render函数传入的第一个参数是QPainter,所以我们可以直接从QPainter入手。
??QPainter要能成功绘制图片,必须在构造时候或者在调用begin函数时候,传入一个有效的绘制设备QPaintDevice。先看QPainter的构造函数:
在这里插入图片描述
??不难发现如果一开始传入一个设备,那么会自动调用begin函数,所以我们把视线转到begin函数。

QPainter的绘制

??在继续深入之前,我们得明白画笔的绘制原理。Qt的QPainter要绘制在指定的QPaintDevice上,是得经过一层QPaintEngine的,它提供了大量的绘制接口。
??我的理解是笔必须得手拿着才能画东西在纸上,此时笔就是QPainter,手就是QPaintEngine,纸就是QPaintDevice。当然笔不一定要手拿着,机械臂也行,画的地方也不一定是纸,在墙上偷偷涂鸦也是很OK的。
??可能我的比喻比较浮夸不贴切,所以还是贴下Qt官方的说明:

The QPaintEngine class provides an abstract definition of how QPainter draws to a given device on a given platform

QPainter的begin

函数原型

bool QPainter::begin(QPaintDevice *pd)

??begin的实现比较长,但我们已经知道了QPainter怎么绘制的,那么begin里必定有对engine的调用。

在这里插入图片描述
??上面的红箭头处,把设备的paintEngine赋值给painter的D指针,下面的箭头处,调用了QPaintEngine的begin。接着我们去看看QPaintEngine。

QPaintEngine

??我们转到QPaintEngine的begin定义,好吧,它是个纯虚函数。

	virtual bool begin(QPaintDevice *pdev) = 0;

??我感觉一定遗漏了什么!是的,就是上图第一个红色箭头的
pd->paintEngine()
??pd是QPaintDevice*,也就是说pd返回的paintEngine一定是QPaintEngine的派生类,我们只要知道pd是什么类型的QPaintDevice,就能知道它继承的PaintEngine叫什么名字。
??这边我绘制的是QImage,所以我必须跑到QImage源码去瞅瞅。

QImage

我们来到QImage的实现的地方,直接搜索paintEngine函数。

QPaintEngine *QImage::paintEngine() const
{
    if (!d)
        return 0;

    if (!d->paintEngine) {
        QPaintDevice *paintDevice = const_cast<QImage *>(this);
        QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
        if (platformIntegration)
            d->paintEngine = platformIntegration->createImagePaintEngine(paintDevice);
       if (!d->paintEngine)
            d->paintEngine = new QRasterPaintEngine(paintDevice);
    }
    return d->paintEngine;
}

可以看到,当paintEngine为空时,会新建一个QRasterPaintEngine类型的paintEngine。接着,我们去看看QRasterPaintEngine是如何实现begin的。

QRasterPaintEngine

在QRasterPaintEngine的begin中,它调用了一个systemStateChanged函数,我们进入其中。
在这里插入图片描述
发现这个deviceRectUnclipped变量定义了不裁剪的区域,这个区域的宽高中,有QT_RASTER_COORD_LIMIT变量,转到定义。
在这里插入图片描述
我们发现它最大支持32767,但它不是老外说的在QPainter,而是在QPaintEngine的子类QRasterPaintEngine。

为何是32767

??但为什么只能是32767还是没解决,所以,还是得回到原点,也就是QGraphicsScene的render函数,我们要找到它绘制的函数,或许才能窥见原因。
??在render的接近末尾处,调用了三个draw函数:
在这里插入图片描述
??我们直接进入drawBackground一探究竟。
在这里插入图片描述
这里最关键的在我看来就一个fillRect函数,开始填充整个区域。这个函数来自于QRasterPaintEngine。
在这里插入图片描述
fillRect中调用了adjustSpanMethod方法,此处省略一些过程,因为要说起来太多了。简而言之,在层层调用之后,来到了一个名为qt_intersect_spans的函数:
在这里插入图片描述
这里都是裁剪相关的函数,我们可以看到,4个const short类型锁定了区域。short的正范围是215-1,也就是32767。所以,至此知道了32767的由来。

其它

??可能有人会问,为何要用short而不是int来增大范围,这个查了半天也没查到原因,希望懂的人能留下你宝贵的评论
??大图像无法绘制我这边采用了缩放的方法来解决,StackOverflow建议是可以通过拼接图像来完成,可以根据需求选择最合适的方法。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:08:08  更:2022-04-01 23:08:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:00:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码