Как правильно отобразить отдельные буквы разных цветов с альфа-смешением? - PullRequest
0 голосов
/ 04 августа 2020

Я хочу нарисовать текст, в котором каждая буква имеет свой цвет solid.

Для текста типа this . Я не могу просто нарисовать текстовый символ за символом, так как, например, fi и fa имеют разную визуализацию для f. Некоторые шрифты имеют триплетную визуализацию, где средний символ будет зависеть от шрифта за ним и того, что перед ним.

Я также хочу смешать части, где глифы перекрываются в курсивных шрифтах.

Вот что у меня есть на данный момент:

#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");
}

Это отображает текст правильно (белый на черном).

Мне нужен какой-то способ получить правильные глифы по тексту и раскрасить их по отдельности с альфа, установленным на 127, чтобы раскрасить каждый в свой цвет и хорошо смешать.

Кто-нибудь может указать мне в правильном направлении? Нужно ли мне создавать QTextLayout и получать QGlyphRun? Нужно ли мне создавать QTextDocument и получать QTextFragment, из которого я беру QGlyphRun? Могу ли я затем перебрать позицию и соответствующий индекс каждого глифа и создать новый QGlyphRun, содержащий только одну позицию и индекс, и передать его функции drawGlyphRun QPainter? Я не совсем понимаю, как это работает ...

1 Ответ

0 голосов
/ 05 августа 2020

Хорошо, я понял:

Мне нужно создать 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;
}

Использование цветных вертикальных полос с визуализированным шрифтом в качестве альфа-маски согласно предложению Джармана может быть легко достигнуто путем рисования прямоугольников с помощью glyph.boundingRect(), но это приводит к проблеме с буквами, такими как W в моем выбранном шрифте, поскольку он перекрывает прямоугольник следующей буквы, поэтому при использовании этого метода эта буква W будет нарисована двумя цветами .

Также обратите внимание, что на самом деле я вообще не использую альфа-смешение, оказывается, это выглядит лучше, когда следующая буква полностью перезаписывает предыдущую вместо альфа-смешения. Вид линейного смешивания (с изменением веса по оси x) может быть лучше, но это выходит за рамки этого вопроса.

...