快捷搜索: 王者荣耀 脱发

Qt 中大尺寸图片的处理

我们的程序(用 Qt 实现),因为一个用户图标,在发到用户的产品中崩溃了。

起因是这样的,我们允许用户注册上传自己的头像。然后中售卖的设备屏幕界面上,也会展示用户自己的头像。

有一个用户上传了一个超大的头像图片,6144 x 6144 像素,jpg 的图片有 1.8M。然后他的设备的UI界面就经常卡死、崩溃。

计算一下,假如图片每个像素 rgb 3 字节,那么 6144 * 6144 * 3 为 108M,对于 1个嵌入式平台,108 M已经很大了,申请这么大的内存很容易失败,那么程序崩溃就不足为奇了。

虽然这个问题应该在输入端(即用户上传头像时)解决,但是展示端都应该自我保护,无论如何,不能够崩溃。

下面看看 Qt/Qml 中怎么处理这种大尺寸图片。

缩小展示

对于这种大尺寸图片,可以改变输出图片的尺寸,将图片缩小展示。

QImageReader

中 Qt 中,除了直接用 QImage 加载图片,还有 QImageReader 这个更底层的 API。

QImageReader reader(file);
QImage image = reader.read();

对于大尺寸图片,使用 QImageReader 可以设置输出图片的尺寸。根据实际需要展示的尺寸,来配置 QImageReader。

QImageReader reader(file);
reader.setScaledSize({60, 60});
QImage image = reader.read();

这样不管输入图片的尺寸是多少,输出图片最大只有 60 x 60 ,内存使用就很少了。

那么是否可以用 QImage::scaled 方法呢?像下面这样:

QImage image(file);
image = image.scaled({60, 60});

不行的,因为应该中间图片尺寸任然很大, 还是有可能内存不足。

实际上,QImageReader 是直接用目标尺寸来解压缩图片(比如 jpg 格式),与 QImage 的 scaled 方法无关。

Image(QML)

在 Qml 中,并不能直接使用 QImageReader,而是使用 Image 控件。能不能处理大尺寸图片呢?

答案是肯定的,Image 有一个 sourceSize 属性,与 setScaledSize 是一样的。

Image
{
    source: "1.jpg"
    sourceSize: Qt.size(60, 60)
}

或者:

Image
{
    source: "1.jpg"
    sourceSize.width: 60
    sourceSize.height: 60
}

局部展示

除了缩小展示图片,还可以不缩小图片,而是拖动图片以浏览各个区域。比如浏览一张大的全景照片,想要看到拍摄的局部细节。

当然,内存的限制仍然存在。

中 Qt 中,针对这种需求,也提供的相关的机制。

QImageReader

除了 scaledSize,QImageReader 还支持 clipRect、scaledClipRect。这里我们不需要 scale,直接用 setClipRect、用 setScaledClipRect 也是一样的。根据屏幕的展示尺寸来配置。

QImageReader reader(file);
reader.setClipRect({0, 0, 1280, 720});
QImage image = reader.read();

上面只展示大图片的左上角 1280 x 720 的大小。当用户拖动图片时,调整 clipRect 的 left、top 重新 read 即可。

Image(QML)

在 Qml 的 Image 中,也有相应的 sourceClipRect 属性。

Image {
   source: "1.svg"
   sourceSize.width: 1024
   sourceSize.height: 1024
   sourceClipRect: Qt.rect(100, 100, 512, 512)
}

以上只是基础性介绍,实际使用中要考虑提高效率,不要因为只拖动了一点点,就要重新加载整个屏幕的图片。具体的方法要等以后遇到相关需求,研究后再来介绍。

经验分享 程序员 微信小程序 职场和发展