天天看點

使用Qt繪制藝術字(字型硬發光效果)

最近的項目中有用到藝術字,網上也找不到合适的用例(也有其他繪制藝術字的示例,但都不支援Enter鍵和自适應對齊方式),在這樣的情況下隻能自己動手豐衣足食。

原理也很簡單,使用QPainterPath先把文字添加進去,然後使用strokePath函數先生成文字對應的路徑,然後再用畫刷把這個路徑給填充(fillPath),這樣就做出了字的發光效果, 然後在使用drawPath把字原來的樣子給畫出來。發光效果+字原來的效果 = 硬發光藝術字。

這個藝術字長啥樣?看圖:

使用Qt繪制藝術字(字型硬發光效果)

圖1

使用了紅色作為發光,字本身是黑色。繪制的文本内容為:

QString text = QString(QStringLiteral("藝術字效果\nstroke \ntext\n style"));
           

當然,把效果做出來還不夠,因為我的要求是能夠自定義适應居中方式。讓我們把上圖中繪制的文字内容的'\n'去掉,然後我用居中方式顯示看看:

尺寸1:

使用Qt繪制藝術字(字型硬發光效果)

圖2

尺寸2

使用Qt繪制藝術字(字型硬發光效果)

圖3

尺寸3

使用Qt繪制藝術字(字型硬發光效果)

圖4

可以看到我們的藝術字針對視窗的大小進行自适應居中了。

要做到自适應居中就需要在繪制文字之前把内容按照視窗的大小進行處理,根據寬度切割成N個片段一次使用QPainterPath來繪制藝術字。

下面就分享本例代碼,

使用到的函數:

///
/// \brief GetTextByWidth 根據寬度得到文字
/// \param fm
/// \param text
/// \param width
/// \return
///
QString GetTextByWidth(const QFontMetrics& fm, const QString& text, int width)
{
    if(width <= 0)
        return QString();

    int offset = 0;
    QString subStr;
    do
    {
       subStr  = text.mid(0, text.length() - offset++);
    }while(!(fm.width(subStr,width) <= width));

    return subStr;
}

///
/// \brief GetTextLinesByRectSize 根據寬、高得到文字段落
/// \param fm
/// \param text
/// \param size
/// \return
///
QStringList GetTextLinesByRectSize(const QFontMetrics& fm, const QString& text, const QSize& size)
{
    QStringList splitLines = text.split('\n');
    QStringList result;
    if(size.width() <= 120)
    {
        int ab =0;
        ab++;
    }
    for(int i = 0; i < splitLines.count(); i++)
    {
        QString splitLine = splitLines[i];

        do
        {

            QString splitLineElidedText = GetTextByWidth(fm, splitLine, size.width());

            if(!splitLineElidedText.isEmpty())
                result.append(splitLineElidedText);



            splitLine = splitLine.mid(splitLineElidedText.length(), splitLine.length() - splitLineElidedText.length());
        }
        while(!splitLine.isEmpty());
    }

    return result;
}

///
/// \brief PaintStrokeText 畫藝術字(硬發光)
/// \param painter
/// \param rect 所需要畫的區域
/// \param text 文字
/// \param font 字型
/// \param strokeWidth 藝術字發光寬度
/// \param strokeColor 藝術字發光顔色
/// \param option 對齊方式
///
void PaintStrokeText(QPainter* painter,
                     const QRect& rect,
                     const QString& text,
                     const QFont& font,
                     int strokeWidth = 2,
                     const QColor& strokeColor=QColor("0xffffff"),
                     const QTextOption& option = QTextOption()
                     )
{
    painter->save();
    painter->setFont(font);

    QPen oldPen = painter->pen();
    QPen strokePen = QPen(strokeColor,strokeWidth);

    QStringList textList = GetTextLinesByRectSize(painter->fontMetrics(), text, rect.size());
    int fontHeight = painter->fontMetrics().height();
    int lineHeight = painter->fontMetrics().lineSpacing();

    for(int i = 0; i < textList.count(); i++)
    {


        QString textLine = textList[i];
        QRect textLineBoundingRect = painter->fontMetrics().boundingRect(rect,option.flags(),textLine);
        QSize textLineSize = textLineBoundingRect.size();


        int offsetX = 0;
        int offsetY = i * lineHeight;

        //offset x
        if(option.alignment().testFlag(Qt::AlignLeft))
        {
            offsetX = 0;
        }
        if(option.alignment().testFlag(Qt::AlignRight))
        {
            offsetX = rect.width() - textLineBoundingRect.width();
        }
        if(option.alignment().testFlag(Qt::AlignHCenter))
        {
            offsetX = (rect.width() - textLineBoundingRect.width())/2;
        }

        //offset y
        if(option.alignment().testFlag(Qt::AlignTop))
        {
            //offsetY = 0;
        }
        if(option.alignment().testFlag(Qt::AlignBottom))
        {

            offsetY += rect.height() - textList.count() * lineHeight;
        }
        if(option.alignment().testFlag(Qt::AlignVCenter))
        {
            offsetY += (rect.height() - textList.count() * lineHeight)/2;
        }



        textLineBoundingRect.setLeft(rect.left() + offsetX);
        textLineBoundingRect.setRight(textLineBoundingRect.left() + textLineSize.width());
        textLineBoundingRect.setTop(rect.top() + offsetY);
        textLineBoundingRect.setBottom(textLineBoundingRect.top() + textLineSize.height());

        QPainterPath strokePath;
        strokePath.addText(textLineBoundingRect.bottomLeft(), font, textLine);
        painter->strokePath(strokePath, strokePen);
        //painter->drawPath(strokePath);//debug
        painter->fillPath(strokePath, QBrush(oldPen.color()));
    }

    //for .debug
    //painter->drawText(rect, text, option);
    painter->restore();
}
           

看看如何使用:

建立一個Qt UI項目,重載mainwindow類的paintEvent回調函數:

void MainWindow::paintEvent(QPaintEvent *e)
{

    QPainter p(this);

    QString text = QString(QStringLiteral("藝術字效果stroke text style"));
    QFont font;
    font.setPointSize(40);
    font.setBold(true);
    return PaintStrokeText(&p,
                           rect(),
                           text,
                           font,
                           8,
                           Qt::red,
                           QTextOption(Qt::AlignCenter));
}
           

運作之後得到結果:

使用Qt繪制藝術字(字型硬發光效果)

至此本例就結束了,

利用這個效果也可以做成軟發光,漸變發光等等的效果,他們的差別隻是在于fillPath的時候使用不同的畫刷而已。

繼續閱讀