如何使用 alpha 混合正确渲染不同颜色的单个字母?

How do I correctly render individual letters in different colors with alpha blending?

我想绘制文本,其中每个字母都有不同的纯色。

对于像 this 这样的文本。我不能简单地一个字符一个字符地绘制文本,因为,例如,fifaf 有不同的渲染。一些字体具有三重渲染,其中中间字符将取决于它后面的字体和它前面的字体。

我还想混合草书字体中字形重叠的部分。

这是我目前得到的:

#include <QtGui/QPainter>
#include <QtGui/QImage>
#include <QGuiApplication>

int main(int argc, char *argv[])
{
    QString text("Hello World");
    QGuiApplication a(argc, argv);
    QFont font("Dancing Script", 100);
    QFontMetrics metric(font);
    auto bbox = metric.boundingRect(text);
    QImage img(bbox.width(), bbox.height(), QImage::Format_ARGB32);
    QPainter painter;
    painter.begin(&img);
    painter.setFont(font);
    painter.setBrush(QBrush(QColor(0, 0, 0, 255)));
    painter.drawRect(0, 0, img.width(), img.height());
    painter.setPen(QColor(255, 255, 255, 255));
    QTextOption option;
    option.setWrapMode(QTextOption::NoWrap);
    painter.drawText(QRect(0, 0, bbox.width(), bbox.height()), text, option);
    painter.end();
    img.save("txt.png");
}

这会正确呈现文本(黑底白字)。

我需要一些方法来沿着文本获取正确的字形,并将它们单独绘制,并将 alpha 设置为 127,以便将它们分别绘制成自己的颜色并让它们很好地融合。

任何人都可以指出我正确的方向吗?我是否需要创建 QTextLayout 并获取 QGlyphRun?我是否需要创建一个 QTextDocument 并从中获取 QTextFragment 以获取 QGlyphRun?然后我是否遍历每个字形的位置和各自的索引并创建一个仅包含单个位置和索引的新 QGlyphRun 并将其提供给 QPainter 的 drawGlyphRun?我不太确定这是如何工作的...

好的,我明白了:

我需要创建一个 QTextLayout,并在该布局中创建一行(无换行)。 我从这个布局中得到的 glyphRuns 是布局中文本的每个字母都有的,所以我只需要遍历每个字母,获取那个字母的字形,确保它的位置不是前一个字母的位置(以防万一与上一个字母合并),画出来

这是我的工作彩虹代码:

#include <QPainter>
#include <QImage>
#include <QGuiApplication>
#include <QTextLayout>


QColor get_color(float position, uint8_t alpha) {
    if (position < 0.2)
        return QColor(255 - 255 * position / 0.2,              0,                 255,                     alpha);
    if (position < 0.4)
        return QColor(              0,         255 * (position - 0.2) / 0.2,      255,                     alpha);
    if (position < 0.6)
        return QColor(              0,                  255,          255 - 255 * (position - 0.4) / 0.2,  alpha);
    if (position < 0.8)
        return QColor(255 * (position - 0.6) / 0.2,     255,                        0,                     alpha);

    return     QColor(             255,        255 - 255 * (position - 0.8) / 0.2,  0,                     alpha);

}


QImage draw_letters(const QString & text, const QFont & font) {
    QFontMetrics metric(font);
    auto bbox = metric.boundingRect(text);
    QImage img(bbox.width(), bbox.height(), QImage::Format_ARGB32);
    QPainter painter;
    painter.begin(&img);
    painter.setBrush(QBrush(QColor(0, 0, 0, 255)));
    painter.drawRect(0, 0, img.width()+1, img.height()+1);
    QTextLayout layout(text, font);
    QTextOption option;
    option.setWrapMode(QTextOption::NoWrap);
    layout.setTextOption(option);
    layout.beginLayout();
    auto line = layout.createLine();
    layout.endLayout();
    QPointF prev_position(-1, -1);
    int num_letters = text.length();
    for (int i = 0; i < num_letters; ++i) {
        auto glyph = layout.glyphRuns(i, 1)[0];
        painter.setPen(get_color(static_cast<float>(i) / (num_letters - 1), 255));
        if (prev_position != glyph.positions()[0])
            painter.drawGlyphRun(QPointF(0, 0), glyph);
        prev_position = glyph.positions()[0];
    }
    painter.end();
    return img;
}


int main(int argc, char *argv[])
{
    QString text("I can see the rainbow!");
    QGuiApplication a(argc, argv);
    QFont font("Dancing Script", 100);
    draw_letters(text, font).save("txt.png");
    return 0;
}

根据 JarMan 的建议,使用带有渲染字体的彩色垂直条作为 alpha 蒙版可以通过使用 glyph.boundingRect() 绘制矩形轻松实现,但这会导致字母出现问题,例如 W In我选择的字体,因为它与下一个字母的矩形重叠 - 所以使用该方法将以两种颜色绘制 W。

另请注意,我实际上根本没有使用 alpha 混合,事实证明下一个字母完全覆盖前一个字母而不是 alpha 混合看起来更好。一种线性混合(权重沿 x 轴变化)可能更好,但这超出了这个问题的范围。