GraphicsMagick TTF Производительность шрифтов - PullRequest
0 голосов
/ 07 января 2019

Я использую GraphicsMagick в библиотеке C ++ для создания растеризованного вывода, который в основном состоит из текста.

Я делаю что-то вроде этого:

void gfx_writer::add_text(Magick::Image& img) const
{
    using namespace Magick;
    const unsigned x = // just a position;
    const unsigned y_title =  // just a position;
    const unsigned y_heading =  // just a position;
    const unsigned y_value =  // just a position;

    img.strokeColor("transparent");
    img.fillColor("black");

    img.font(font_title_);
    img.fontPointsize(font_size_title_);
    img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_title), "a text"});

    img.font(font_heading_);
    img.fontPointsize(font_size_heading_);
    img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_heading), "another text"});

    img.font(font_value_);
    img.fontPointsize(font_size_value_);
    img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_value), "third text"});
}

Принимая во внимание, что font_title_, font_heading_ и font_value_ являются путями к файлам TTF.

Это делается не раз, и у меня довольно плохая производительность. Когда я смотрю, что происходит с помощью Sysinternals Process Monitor, я вижу, что файлы TTF читаются снова и снова. Итак, мои вопросы:

  • Верны ли мои наблюдения, что файлы TTF читаются каждый раз, когда вызывается img.font(...)?
  • существует ли способ каким-либо образом кэшировать шрифт с помощью GraphicsMagick ИЛИ, чтобы предоставить что-то еще, кроме пути к файлу TTF?
  • что-то еще мне не хватает?

1 Ответ

0 голосов
/ 09 января 2019

Примечание. В этом ответе используется библиотека Magick++ ImageMagick, и могут возникнуть незначительные проблемы с переносимостью с GraphicsMagick, но базовое решение остается тем же.

Верны ли мои наблюдения, что файлы TTF читаются при каждом вызове img.font (...)?

Да, шрифт TTF перезагружается каждый раз. Один из вариантов - установить шрифты в системе и вызвать конструктор семейства шрифтов.

DrawableFont ( const std::string &family_,
               StyleType style_,
               const unsigned long weight_,
               StretchType stretch_ );

Большинство систем имеют какую-то систему кэширования шрифтов, которая обеспечивает более быстрый доступ, но не очень заметна на современном оборудовании.

что-то еще мне не хватает?

Попробуйте создать графический контекст, и вызывайте Magick::Image.draw только один раз. Помните, что вызовы Drawable... являются только переносом операторов MVG, а создание std::list<Drawable> позволяет создавать сложные векторы. Только когда метод draw потребляет команды рисования, это когда TTF будет загружен, поэтому его ключ готовит все команды рисования заранее.

Давайте начнем с переписывания кода, который вы предоставили (и здесь я получаю определенную степень свободы).

#include <Magick++.h>

const char * font_title_ = "fonts/OpenSans-Regular.ttf";
const char * font_heading_ = "fonts/LiberationMono-Regular.ttf";
const char * font_value_ = "fonts/sansation.ttf";
double font_size_title_ = 32;
double font_size_heading_ = 24;
double font_size_value_ = 16;

void gfx_writer_add_text(Magick::Image& img)
{
    using namespace Magick;
    double x = 10.0;
    double y_title = 10;
    double y_heading = 20.0;
    double y_value =  30.0;

    img.strokeColor("transparent");
    img.fillColor("black");

    img.font(font_title_);
    img.fontPointsize(font_size_title_);
    img.draw(DrawableText{x, y_title, "a text"});

    img.font(font_heading_);
    img.fontPointsize(font_size_heading_);
    img.draw(DrawableText{x, y_heading, "another text"});

    img.font(font_value_);
    img.fontPointsize(font_size_value_);
    img.draw(DrawableText{x, y_value, "third text"});
}

int main()
{
    Magick::Image img("wizard:");
    gfx_writer_add_text(img);
    gfx_writer_add_text(img);
    gfx_writer_add_text(img);
    img.write("output.png");
}

Я могу скомпилировать и протестировать время выполнения. Я получаю следующее время:

$ time ./original.o 
real    0m5.061s
user    0m0.094s
sys     0m0.029s

Рефакторинг кода для использования контекста рисования, и вызывать Magick::Image.draw только один раз.

#include <Magick++.h>
#include <list>

const char * font_title_ = "fonts/OpenSans-Regular.ttf";
const char * font_heading_ = "fonts/LiberationMono-Regular.ttf";
const char * font_value_ = "fonts/sansation.ttf";
double font_size_title_ = 32;
double font_size_heading_ = 24;
double font_size_value_ = 16;

void gfx_writer_add_text(Magick::Image& img)
{
    using namespace Magick;
    double x = 10.0;
    double y_title = 10;
    double y_heading = 20.0;
    double y_value =  30.0;

    std::list<Drawable> ctx;
    ctx.push_back(DrawableStrokeColor("transparent"));
    ctx.push_back(DrawableFillColor("black"));
    /* TITLE */
    ctx.push_back(DrawablePushGraphicContext());
    ctx.push_back(DrawableFont(font_title_);
    ctx.push_back(DrawablePointSize(font_size_title_));
    ctx.push_back(DrawableText{x, y_title, "a text"});
    ctx.push_back(DrawablePopGraphicContext());
    /* HEADING */
    ctx.push_back(DrawablePushGraphicContext());
    ctx.push_back(DrawableFont(font_heading_));
    ctx.push_back(DrawablePointSize(font_size_heading_));
    ctx.push_back(DrawableText{x, y_heading, "another text"});
    ctx.push_back(DrawablePopGraphicContext());
    /* Value */
    ctx.push_back(DrawablePushGraphicContext());
    ctx.push_back(DrawableFont(font_value_));
    ctx.push_back(DrawablePointSize(font_size_value_));
    ctx.push_back(DrawableText{x, y_value, "third text"});
    ctx.push_back(DrawablePopGraphicContext());
    img.draw(ctx);
}

int main()
{
    Magick::Image img("wizard:");
    gfx_writer_add_text(img);
    gfx_writer_add_text(img);
    gfx_writer_add_text(img);
    img.write("output2.png");
}

И время тестирования немного лучше.

$ time ./with_context.o
real    0m0.106s
user    0m0.090s
sys     0m0.012s

Это делается не раз, и у меня довольно низкая производительность.

Стоит сделать шаг назад и спросить: «Как может рефакторинг моего решения рисовать только в самый последний момент?» .

...