关于Qt Quick中将Canvas保存为QImage类型

环境:win + qml(QT版本5.6.0) + msvc2015

由于项目中需要实现将Canvas图像保存为QImage类型的图像,百度了一大圈都搜索不到相关答案,没办法,只能自己一个个的查看文档中是否有相关的接口可以实现。

例如将如下Canvas保存为QImage,方法有四种:

Canvas{
        property var ctx : canvas.getContext('2d')
        id:canvas
        anchors.fill: parent
        onPaint: {
        		ctx.beginPath()
                ctx.lineCap="round"
                ctx.moveTo(0,0)
                ctx.lineTo(100,100)
                ctx.stroke()
        }
    }

方法1:

这个方法挺low的,而且效率也大打折扣,我都不好意思将它写出来了,大家应该都能想到。那就是通过调用canvas.save()的方式首先将图像保存为本地文件的形式,然后在c++后台通过QImage加载该文件。挺low的吧,行,咱们跳过这个方法。

方法2:

Context2D可以通过调用getImageData获取到CanvasImageData对象,该对象中的data对象就包含了图像的数据。对于data的解释如下:

包含 RGBA 顺序数据的一维数组保存为 0 到 255 范围内的整数。

这下明白了吧,CanvasImageData.data是一个包含了四通道图像的一维数组,数组长度是宽 x高x4,因此我们可以通过遍历该数组来获取图像数据,因为每四个长度就分别代表一个像素点的r,g,b,a值,因此我们四个点跳跃式的遍历。我是先调用Provider.begin告诉后台要开始遍历了,并且传递要遍历图像的尺寸。最后调用Provider.end()告诉后台遍历结束,可以处理QImage了。具体操作如下:

CImgProvider类的头文件:

class CImgProvider : public QObject
{
    Q_OBJECT
public:
    explicit CImgProvider(QObject *parent = 0);
public:
    Q_INVOKABLE void begin(int w,int h);
    Q_INVOKABLE void saveFromData(const int& x,const int& y,const int& r,const int& g,const int& b,const int& a);
    Q_INVOKABLE void end();
private:
    QImage* m_pImage = nullptr;
};

CImgProvider类的部分函数定义:

void CImgProvider::begin(int w,int h)
{
    if(m_pImage)
    {
        delete m_pImage;
        m_pImage = nullptr;
    }
    m_pImage = new QImage(w,h,QImage::Format_ARGB32);
}

void CImgProvider::saveFromData(const int& x,const int& y,const int& r,const int& g,const int& b,const int& a)
{
    /* write to QImage */
    m_pImage->setPixelColor(x,y,QColor(r,g,b,a));
}

void CImgProvider::end()
{
    /*
     * you can do something before delete m_pImage.
     * example: m_pImage->save("d:/img.png");
    */

    delete m_pImage;
    m_pImage = nullptr;
}

qml文件中:(其中Provider为c++后台注册的CImgProvider类对象)

var ar = canvas.ctx.getImageData(0,0,canvas.width,canvas.height)
var len = ar.data.length;
var data = ar.data;

 Provider.begin(canvas.width,canvas.height)
 for(var i = 0;i< len; i=i+4)
 {
      var x = i/4 % canvas.width;
      var y = i/4 / canvas.width;
      Provider.saveFromData(x,y,data[i],data[i + 1],data[i + 2],data[i + 3])
 }
 Provider.end()

注意:其实方法2的效率也是有问题的,当我们canvas图像过大,比如3000*3000的分辨率下,整个过程需要耗时接近8秒左右,所以处理低尺寸图像还是没有问题的,如果是高尺寸的,em.. 我劝你还是放弃吧,再来看看方法3

方法3:

该方法相对来说比前面两种方法快很多了,就是通过toDataURL接口将图像数据进行编码,然后传递到c++后台进行解码,
toDataURL的参数可以是"image/jpeg"或"image/png"等,当然了,采用jpeg压缩比例更大,因此传递更快, 具体实现如下:

CImgProvider类的头文件:

class CImgProvider : public QObject
{
    Q_OBJECT
public:
    explicit CImgProvider(QObject *parent = 0);
public:
    Q_INVOKABLE void saveFromDataurl(const QString& data);
};

CImgProvider类的部分函数定义:

void CImgProvider::saveFromDataurl(const QString& data)
{
    QByteArray base64Data = data.mid(22).toUtf8();

    QImage image;
    image.loadFromData(QByteArray::fromBase64(base64Data), "jpeg");
    /*  you can do something */
}

qml
文件中:(其中Provider为c++后台注册的CImgProvider类对象)

var str = canvas.toDataURL("image/jpeg")
Provider.saveFromDataurl(str)

方法4:

哎,写累了,我就精简一点吧,通过调用grabToImage,而且该方法效率最快,代码如下:
qml:

/*
* Provider:c++后台注册的CImgProvider类对象
* canvas: Canvas控件id
*/
Provider.itemToImage(canvas)

cpp:

void CImgProvider::itemToImage(QObject* object)
{
    auto item = qobject_cast<QQuickItem*>(object);
    if(!item) { return; }

    QEventLoop eventLoop;
    QImage buf;
    auto grad = item->grabToImage();

    auto c = QObject::connect(grad.data(), &QQuickItemGrabResult::ready, [&]()
    {
        buf = grad->image();
        eventLoop.exit();
    });
    eventLoop.exec();
    QObject::disconnect(c);
    /*  you can do something */
}

好了,分享完了,最后吐槽一下网络上这个抄袭有点严重哦(其实方法4我也是copy过来的,拿到这里来总结归纳一下),有时候要查找一个问题的解决办法百度翻了一两页下来点开的全是指向同一个作者,有的还不注明转载出处。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值