关于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)

好了,分享完了,最后吐槽一下网络上这个抄袭有点严重哦,有时候要查找一个问题的解决办法百度翻了一两页下来点开的全是指向同一个作者,有的还不注明转载出处。

上一篇:acwing 自我学习笔记-快排及遇到的问题


下一篇:点数:新Qt Quick编译器的性能优势