Qt5官方demo解析集37——Vector Deformation

本系列所有文章可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873

接上文Qt5官方demo解析集36——Wiggly Example


在 Qt 中设计GUI界面,经常需要考虑不同尺寸,不同分辨率下面的情况,因此我们经常需要准备几套图片素材来应付不同的场景。不过好在,我们还可以使用矢量绘图和矢量图形。


今天这个例子基于 QPainterPath 绘制了文本字符的矢量路径,并可以在一个"滤镜"范围内进行变形处理,效果萌萌哒~下面就来看看吧:

Qt5官方demo解析集37——Vector Deformation_第1张图片


这个例子看起来代码量较多,我们关心的核心处理也就是几行,经过抽丝剥茧,去掉布局按钮定时器之类的逻辑,其实这也就是个小demo啦。

还是先从main.cpp来看:

#include "pathdeform.h"

#include <QApplication>

int main(int argc, char **argv)
{
    Q_INIT_RESOURCE(deform);

    QApplication app(argc, argv);

    bool smallScreen = QApplication::arguments().contains("-small-screen");

    PathDeformWidget deformWidget(0, smallScreen);

    QStyle *arthurStyle = new ArthurStyle();
    deformWidget.setStyle(arthurStyle);
    QList<QWidget *> widgets = deformWidget.findChildren<QWidget *>();
    foreach (QWidget *w, widgets)
        w->setStyle(arthurStyle);

    if (smallScreen)
        deformWidget.showFullScreen();
    else
        deformWidget.show();

#ifdef QT_KEYPAD_NAVIGATION
    QApplication::setNavigationMode(Qt::NavigationModeCursorAuto);
#endif
    return app.exec();
}

在上一个例子中我们提到了这个 smallScreen,这里就不多提啦,注意将这个参数传递给我们的窗口,以做不同的处理。

接着它将窗口风格设置为 “ArthurStyle”,这个类是在 shared 子工程中定义的,继承自 QCustomStyle,之所以放在 shared 中是因为有好几个 demo 是使用它的。如果大家一直在做 Qt 开发,不妨留心建立下自己的“Qt库”,把可扩展性和通用性考虑进去之后,就可以在下一个项目中复用一些东西啦。

好,不扯远了,如果屏蔽掉这几行 setStyle 语句后,可以看到这个 control 面板几乎不忍直视。。

Qt5官方demo解析集37——Vector Deformation_第2张图片


不看 shared 子工程,我们就剩 pathdeform.h 和 pathdeform.cpp 俩文件了,不过里面定义了三个类:

PathDeformWidget、PathDeformControls、PathDeformRenderer,分别是主窗口,控件窗体和渲染窗体


可以看到,控件窗体 PathDeformControls 针对 smallScreen 参数设计了两套布局

layoutForDesktop()

layoutForSmallScreen()

以应对不同的场景。


接下来,绘制“滤镜”并保存在 QImage 或 QPixmap 中,代码就不帖啦,

然后使用 QPainterPath 的 addText 将用户输入的字符添加到绘制路径中:

    bool do_quick = true;
    for (int i=0; i<text.size(); ++i) {
        if (text.at(i).unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) {
            do_quick = false;
            break;
        }
    }

    if (do_quick) {
        for (int i=0; i<text.size(); ++i) {
            QPainterPath path;
            path.addText(advance, f, text.mid(i, 1));
            m_pathBounds |= path.boundingRect();
            m_paths << path;
            advance += QPointF(fm.width(text.mid(i, 1)), 0);
        }
    } else {
        QPainterPath path;
        path.addText(advance, f, text);
        m_pathBounds |= path.boundingRect();
        m_paths << path;
    }


有意思的是,针对字符的不同编码,这里使用了两种不同的添加方式:

Unicode编码大于 0X4FF 且 小于 0X1E00 的字符,直接将整个文本添加到路径中去;

否则,使用循环语句添加该字符串中每个字符。

根据 do_quick 命名似乎是第二种方式要更快速一些,不过暂时没有找到相关的证明-.-


函数lensDeform()用来返回一个经过变形的QPainterPath,并由 painter 绘制出来:

QPainterPath PathDeformRenderer::lensDeform(const QPainterPath &source, const QPointF &offset)
{
    QPainterPath path;
    path.addPath(source);

    qreal flip = m_intensity / qreal(100);

    for (int i=0; i<path.elementCount(); ++i) {
        const QPainterPath::Element &e = path.elementAt(i);

        qreal x = e.x + offset.x();
        qreal y = e.y + offset.y();

        qreal dx = x - m_pos.x();
        qreal dy = y - m_pos.y();
        qreal len = m_radius - qSqrt(dx * dx + dy * dy);

        if (len > 0) {
            path.setElementPositionAt(i,
                                      x + flip * dx * len / m_radius,
                                      y + flip * dy * len / m_radius);
        } else {
            path.setElementPositionAt(i, x, y);
        }
    }

    return path;
}

这里通过 len 判断 QPainterPath 的元素是否在 “滤镜”内,len值越大,离“滤镜”中心点越近。

一个在圆内的点各参数可以表示为下面这张图:

Qt5官方demo解析集37——Vector Deformation_第3张图片


x + flip * dx * len / m_radius,
y + flip * dy * len / m_radius

可知当 flip 为正数时,越靠近滤镜中心的部分 x,y增量越大,表现为中心部分向外扩张得更厉害,并且保证坐标增量不至于超出滤镜范围,从而形成凸透镜的效果。

反之,flip为负数时,越靠近中心部分的x,y减去的值更大,从而使图形向中心收缩,形成凹透镜的效果。


主要就是这些啦~

你可能感兴趣的:(qt,滤镜,矢量,Qt5官方Demo解析集,QPainterPath)