QML的图片加载,内存优化研究(一)

QML加载图片的两个控件

通常,qml加载图片的话,会用到Image和AnimatedImage这两种控件,本期只探讨一下Image加载图片,AnimatedImage下期进行探讨;

Image控件及其相关属性

    asynchronous : bool //是否异步加载
    autoTransform : bool //此属性保存图像是否应自动应用图像转换元数据。默认为 false。
    cache : bool //是否使用缓存
    fillMode : enumeration
    horizontalAlignment : enumeration
    mipmap : bool //此属性保存图像在缩放或变换时是否使用 mipmap 过滤,
    mirror : bool //水平反转
    paintedHeight : real
    paintedWidth : real
    progress : real //加载进度
    smooth : bool //是否平滑过滤
    source : url  //路径,可是本地路径,也可是网络路径,也可使qrc资源路径
    sourceSize : QSize //此属性保存全帧图像的缩放宽度和高度,优化内存时会有用
    status : enumeration //图像加载的状态
    verticalAlignment : enumeration

以上只是一些基础属性,我从官网搬过来简单翻译了一下,搬运的原因是确实有两个属性还蛮有用,并非是重点,每个人都是大自然的搬运工,如果要了解基础知识,看别人博客,不如去去官网看看官方文档,以免一个简单的问题,经过多个人传达,最后只能一知半解,以下是官网链接,如果不习惯看英文,就下载个浏览器翻译插件,比某些搬运工翻译的好多了。
参考链接: 官网.
先附上小弟的测试代码✌🏻:https://github.com/YQF66666666/QML_Project.测试的话,注意要把路径改成自己本地路径

Image加载图片的内存问题

如果不考虑内存,以及性能问题,以下内容就都可以不看了,因为以下都是作者在使用Image中实际掉好多次坑后总结的经验(笔者系统是MAC 12.4,Windows 10也做过测试,结论相同)。废话不多说,首先我们使用Image控件简单加载一张本地图片和一个空窗口的内存占用,

代码一:

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
}

可以看到,只打开一个window窗口内存占43mb,🐶!!!
空窗口内存占用

代码二:

然后我们再尝试加载一个2MB图片看看效果

    Image {
        id: _image
        width: 300
        height: 300
        anchors.centerIn: parent
        smooth: true
        //这个路径就是一个本地的全路径前面加了file://而已。···如果测试的话输入你自己的路径就可以
        source: "file:///···/worker/123.jpg"
        //sourceSize: Qt.size(imageWidth,imageHeight)
        antialiasing: true
    }

哇!🤩惊不惊喜,意不意外,(╯‵□′)╯︵┻━┻
加载图片后内存占用
加载了个2mb的图片,给我搞出了60mb的内存,简直是叔叔能忍,婶婶也不能忍,然后再看看它的官方文档sourceSize 的属性介绍:Unlike the width and height properties, which scale the painting of the image, this property sets the maximum number of pixels stored for the loaded image so that large images do not use more memory than necessary. !!!好的,我们开搞。

代码三:

{
        ···
        sourceSize: Qt.size(300,300)
        ···
}

what?加了一个这个内存优化,内存比一个空窗口还小?(╯‵□′)╯︵┻━┻

开启了优化后
👌🏻你以为到此结束了吗,hhh,并没有,接下来,我们给图片加个圆角,以下代码是从网上随便找了找贴过来的,我们看一下内存。

代码四:

···
    Rectangle{
        id : rect
        width: 300
        height: 300
        anchors.centerIn: parent

        Image {
            id: image
            width: 300
            height: 300
            anchors.centerIn: parent
            smooth: true
            visible: false
            anchors.fill: parent
            source: "file:///···/worker/123.jpg"
            sourceSize: Qt.size(300,300)
            antialiasing: true
        }
        Rectangle {
            id: imageRect
            color: "#FFFFFF"
            anchors.fill: parent
            radius: 10
            visible: false
            antialiasing: true
            smooth: true
        }
        OpacityMask {
            id: imageOpcityMask
            anchors.fill: image
            source: image
            maskSource: imageRect
            visible: true
            antialiasing: true
        }
    }
···

有没有搞错,大哥,我就是要加个圆角,结果你又给我多搞出这么多mb内存,确定不是在搞我心态?
圆角图片

代码五:

以下代码有参考这个兄弟,这是这个兄弟的: 原文链接.
就是简单来说,自己继承自QQuickPaintedItem,重写了paint方法,注册进qml 里面,并且这样子的抗锯齿效果要比上面👆🏻的效果好。
关键代码:

void ImagePaint::paint(QPainter *painter)
{
    QPixmap pixmap(m_source);
    QRect rect(0,0,static_cast<int>(width()),static_cast<int>(height()));
    QPainterPath path;

    painter->setPen(QColor(208,208,208));
    painter->setBrush(Qt::white);
    painter->setRenderHint(QPainter::Antialiasing);
    path.addRoundedRect(rect, m_radius, m_radius);
    painter->setClipPath(path);

    painter->drawEllipse(rect);
    //SmoothTransformation平滑处理。
    pixmap = pixmap.scaled(QSize(width() , height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    painter->drawPixmap(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), pixmap);

    painter->restore();
}

来看看内存!!!,不错不错,这才是我们想要的内存效果。
优化后的
注意!!!,这样做仅限于可加载本地,或者资源当中的图片,不可加载网络图片,以及没有Image的一些带的功能,不过,如果想要,我们可以通过C++自己实现它。

通过源码来分析

//源码路径:
Qt5.12.12/5.12.12/Src/qtdeclarative/src/quick/items/qquickpainteditem.cpp

/*!
    \class QQuickPaintedItem
    \brief The QQuickPaintedItem class provides a way to use the QPainter API in the
    QML Scene Graph.

    \inmodule QtQuick

    The QQuickPaintedItem makes it possible to use the QPainter API with the
    QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
    uses a QPainter to paint onto the texture. The render target can be either
    a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
    render target is a QImage, QPainter first renders into the image then the
    content is uploaded to the texture. When a QOpenGLFramebufferObject is
    used, QPainter paints directly onto the texture. Call update() to trigger a
    repaint.

    To enable QPainter to do anti-aliased rendering, use setAntialiasing().

    To write your own painted item, you first create a subclass of
    QQuickPaintedItem, and then start by implementing its only pure virtual
    public function: paint(), which implements the actual painting. The
    painting will be inside the rectangle spanning from 0,0 to
    width(),height().

    \note It important to understand the performance implications such items
    can incur. See QQuickPaintedItem::RenderTarget and
    QQuickPaintedItem::renderTarget.
*/

/*!
    \enum QQuickPaintedItem::RenderTarget

    This enum describes QQuickPaintedItem's render targets. The render target is the
    surface QPainter paints onto before the item is rendered on screen.

    \value Image The default; QPainter paints into a QImage using the raster paint engine.
    The image's content needs to be uploaded to graphics memory afterward, this operation
    can potentially be slow if the item is large. This render target allows high quality
    anti-aliasing and fast item resizing.

    \value FramebufferObject QPainter paints into a QOpenGLFramebufferObject using the GL
    paint engine. Painting can be faster as no texture upload is required, but anti-aliasing
    quality is not as good as if using an image. This render target allows faster rendering
    in some cases, but you should avoid using it if the item is resized often.

    \value InvertedYFramebufferObject Exactly as for FramebufferObject above, except once
    the painting is done, prior to rendering the painted image is flipped about the
    x-axis so that the top-most pixels are now at the bottom.  Since this is done with the
    OpenGL texture coordinates it is a much faster way to achieve this effect than using a
    painter transform.

    \sa setRenderTarget()
*/

/*!
    \enum QQuickPaintedItem::PerformanceHint

    This enum describes flags that you can enable to improve rendering
    performance in QQuickPaintedItem. By default, none of these flags are set.

    \value FastFBOResizing Resizing an FBO can be a costly operation on a few
    OpenGL driver implementations. To work around this, one can set this flag
    to let the QQuickPaintedItem allocate one large framebuffer object and
    instead draw into a subregion of it. This saves the resize at the cost of
    using more memory. Please note that this is not a common problem.

*/

简单来说,我的理解就是QQuickPaintedItem 类提供了一种在 QML 场景图中使用 QPainter API 的方法。并且抗锯齿效果要更好,但是在一些情况下会绘制的慢一点,再多就不乱说了,毕竟源码面前了无秘密,而且源码中的注释写的又足够好。
然后通过阅读QML的Image源码不难发现,qml 的Image 提供了相对复杂的功能,包括图片的缓存,加载,优化。但是实践中我们很多时候并不需要如此复杂的功能,我们需要简单且轻量、并且足够小的内存占用🐶!!的控件来实现我们的应用。
下回有时间将分享一下GIF动态图的加载过程中遇到的各种神坑问题!!!

————本文严禁转载————

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值