Qt中设置Pixmap时执行时长不符合预期的原因咨询
我遇到了一个不符合预期的性能问题,想向大家请教。
先看最初的代码,其中Image是ImageViewer类中类型为QImage的成员变量:
void ImageViewer::setImage(const QImage &newImage) { Image = newImage; // 耗时108毫秒 imageLabel->setPixmap(QPixmap::fromImage(Image)); // 耗时58毫秒 }
因为后续不再需要将newImage赋值给类成员Image,我想着直接使用newImage的引用传入,应该能节省复制的时间,于是修改了代码:
void ImageViewer::setImage(const QImage &newImage) { imageLabel->setPixmap(QPixmap::fromImage(newImage)); // 耗时158毫秒 }
结果出乎意料,总耗时居然和之前几乎相同(158ms vs 108+58=166ms)。我到底忽略了什么?
补充说明
- 计时采用QElapsedTimer,每次测试传入的均为同一张2380x3368尺寸的.jpg图片,多次测量结果稳定,注释中为平均耗时。
- 我认为图片格式或尺寸并非核心问题,核心疑问是:为何直接传入QImage引用调用setPixmap,比先复制QImage再传入的耗时更长/几乎一致?这不符合逻辑。
嘿,这个问题挺有意思的,我来帮你拆解一下背后的原因!
核心关键点:QImage的隐式共享机制
首先你得明白:Qt的QImage是基于**隐式共享(Copy-on-Write,写时复制)**实现的。你最初代码里的Image = newImage;这一步,并没有真正复制图片的像素数据——它只是复制了一个指向数据块的指针和引用计数,这一步的108毫秒绝对不是像素复制的耗时!那这108毫秒到底是什么?
大概率是newImage本身的“懒加载”收尾或者内存预处理:比如你传入的.jpg图片,它的像素数据可能还处于磁盘缓存或者未对齐的内存区域,赋值给Image时,Qt悄悄做了内存对齐、像素格式预转换或者数据加载到更高效的内存空间的操作,这才是那108毫秒的来源。
为什么直接传引用耗时没减少?
当你直接把const QImage& newImage传入QPixmap::fromImage()时,问题来了:QPixmap::fromImage()需要将QImage的像素数据转换为适合当前显示设备的格式(比如从RGB888转换为ARGB32,或者做内存对齐),但因为newImage是const引用,函数内部不能修改原对象的数据,所以Qt不得不临时复制一份QImage的完整像素数据来进行格式转换。
而你最初的写法里,Image = newImage;之后,Image是非const的,fromImage()处理时可以直接基于已经做过预优化的Image数据进行转换,不需要额外复制——所以“预处理(108ms)+ 转换(58ms)”的总耗时,和“临时复制+转换(158ms)”的总耗时就几乎一致了。
验证思路
你可以做几个小测试来验证这个猜想:
- 在第一种写法里,
Image = newImage;之后加一行Image = Image.copy();——这会强制触发写时复制,真正复制像素数据,看看这一步的耗时。如果这一步耗时远小于108ms,就说明之前的108ms不是复制的时间,而是预处理开销。 - 打印
newImage.format()和Image.format(),对比赋值前后的像素格式是否有变化,判断是否存在隐式的格式转换。 - 尝试给
QPixmap::fromImage()传入Qt::NoFormatConversion标志,看看两种写法的耗时差异是否消失——如果消失,就说明核心问题确实是格式转换的额外开销。
内容的提问来源于Stack Exchange,提问作者blah blah




